LCOV - code coverage report
Current view: top level - libgnucash/engine - qofquery.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 60.7 % 866 526
Test Date: 2026-11-02 18:19:43 Functions: 65.4 % 81 53
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * qof_query.c -- Implement predicate API for searching for objects *
       3              :  * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU>                *
       4              :  *                                                                  *
       5              :  * This program is free software; you can redistribute it and/or    *
       6              :  * modify it under the terms of the GNU General Public License as   *
       7              :  * published by the Free Software Foundation; either version 2 of   *
       8              :  * the License, or (at your option) any later version.              *
       9              :  *                                                                  *
      10              :  * This program is distributed in the hope that it will be useful,  *
      11              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      12              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      13              :  * GNU General Public License for more details.                     *
      14              :  *                                                                  *
      15              :  * You should have received a copy of the GNU General Public License*
      16              :  * along with this program; if not, contact:                        *
      17              :  *                                                                  *
      18              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      19              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      20              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      21              :  *                                                                  *
      22              : \********************************************************************/
      23              : #include <glib.h>
      24              : 
      25              : #include <config.h>
      26              : 
      27              : #include <sys/types.h>
      28              : #include <time.h>
      29              : #include <regex.h>
      30              : #include <string.h>
      31              : 
      32              : #include "qof.h"
      33              : #include "qof-backend.hpp"
      34              : #include "qofbook-p.hpp"
      35              : #include "qofclass-p.h"
      36              : #include "qofquery-p.h"
      37              : #include "qofquerycore-p.h"
      38              : 
      39              : static QofLogModule log_module = QOF_MOD_QUERY;
      40              : 
      41              : struct _QofQueryTerm
      42              : {
      43              :     QofQueryParamList *     param_list;
      44              :     QofQueryPredData        *pdata;
      45              :     gboolean                invert;
      46              : 
      47              :     /* These values are filled in during "compilation" of the query
      48              :      * term, based upon the obj_name, param_name, and searched-for
      49              :      * object type.  If conv_fcn is nullptr, then we don't know how to
      50              :      * convert types.
      51              :      */
      52              :     GSList *                param_fcns;
      53              :     QofQueryPredicateFunc   pred_fcn;
      54              : };
      55              : 
      56              : struct _QofQuerySort
      57              : {
      58              :     QofQueryParamList * param_list;
      59              :     gint                options;
      60              :     gboolean            increasing;
      61              : 
      62              :     /* These values are filled in during "compilation" of the query
      63              :      * term, based upon the obj_name, param_name, and searched-for
      64              :      * object type.  If conv_fcn is nullptr, then we don't know how to
      65              :      * convert types.
      66              :      */
      67              :     gboolean            use_default;
      68              :     GSList *            param_fcns;     /* Chain of parameters to walk */
      69              :     QofSortFunc         obj_cmp;        /* In case you are comparing objects */
      70              :     QofCompareFunc      comp_fcn;       /* When you are comparing core types */
      71              : };
      72              : 
      73              : /* The QUERY structure */
      74              : struct _QofQuery
      75              : {
      76              :     /* The object type that we're searching for */
      77              :     QofIdType         search_for;
      78              : 
      79              :     /* terms is a list of the OR-terms in a sum-of-products
      80              :      * logical expression. */
      81              :     GList *           terms;
      82              : 
      83              :     /* sorting and chopping is independent of the search filter */
      84              : 
      85              :     QofQuerySort      primary_sort;
      86              :     QofQuerySort      secondary_sort;
      87              :     QofQuerySort      tertiary_sort;
      88              :     QofSortFunc       defaultSort;        /* <- Computed from search_for */
      89              : 
      90              :     /* The maximum number of results to return */
      91              :     gint              max_results;
      92              : 
      93              :     /* list of books that will be participating in the query */
      94              :     GList *           books;
      95              : 
      96              :     /* a map of book to backend-compiled queries */
      97              :     GHashTable*       be_compiled;
      98              : 
      99              :     /* cache the results so we don't have to run the whole search
     100              :      * again until it's really necessary */
     101              :     gint              changed;
     102              : 
     103              :     GList *           results;
     104              : };
     105              : 
     106              : typedef struct _QofQueryCB
     107              : {
     108              :     QofQuery *        query;
     109              :     GList *           list;
     110              :     gint              count;
     111              : } QofQueryCB;
     112              : 
     113              : /* Query Print functions for use with qof_log_set_level, static prototypes */
     114              : static GList *qof_query_printSearchFor (QofQuery * query, GList * output);
     115              : static GList *qof_query_printTerms (QofQuery * query, GList * output);
     116              : static GList *qof_query_printSorts (QofQuerySort *s[], const gint numSorts,
     117              :                                     GList * output);
     118              : static GList *qof_query_printAndTerms (GList * terms, GList * output);
     119              : static const char *qof_query_printStringForHow (QofQueryCompare how);
     120              : static const char *qof_query_printStringMatch (QofStringMatch s);
     121              : static const char *qof_query_printDateMatch (QofDateMatch d);
     122              : static const char *qof_query_printNumericMatch (QofNumericMatch n);
     123              : static const char *qof_query_printGuidMatch (QofGuidMatch g);
     124              : static const char *qof_query_printCharMatch (QofCharMatch c);
     125              : static GList *qof_query_printPredData (QofQueryPredData *pd, GList *lst);
     126              : static GString *qof_query_printParamPath (QofQueryParamList * parmList);
     127              : static void qof_query_printValueForParam (QofQueryPredData *pd, GString * gs);
     128              : static void qof_query_printOutput (GList * output);
     129              : static void qof_query_print (QofQuery * query);
     130              : 
     131              : 
     132              : /* initial_term will be owned by the new Query */
     133        32291 : static void query_init (QofQuery *q, QofQueryTerm *initial_term)
     134              : {
     135        32291 :     GList * _or_ = nullptr;
     136        32291 :     GList *_and_ = nullptr;
     137              :     GHashTable *ht;
     138              : 
     139        32291 :     if (initial_term)
     140              :     {
     141         7983 :         _or_ = g_list_alloc ();
     142         7983 :         _and_ = g_list_alloc ();
     143         7983 :         _and_->data = initial_term;
     144         7983 :         _or_->data = _and_;
     145              :     }
     146              : 
     147        32291 :     if (q->terms)
     148            0 :         qof_query_clear (q);
     149              : 
     150        32291 :     g_list_free (q->results);
     151        32291 :     g_list_free (q->books);
     152              : 
     153        32291 :     g_slist_free (q->primary_sort.param_list);
     154        32291 :     g_slist_free (q->secondary_sort.param_list);
     155        32291 :     g_slist_free (q->tertiary_sort.param_list);
     156              : 
     157        32291 :     g_slist_free (q->primary_sort.param_fcns);
     158        32291 :     g_slist_free (q->secondary_sort.param_fcns);
     159        32291 :     g_slist_free (q->tertiary_sort.param_fcns);
     160              : 
     161        32291 :     ht = q->be_compiled;
     162        32291 :     memset (q, 0, sizeof (*q));
     163        32291 :     q->be_compiled = ht;
     164              : 
     165        32291 :     q->terms = _or_;
     166        32291 :     q->changed = 1;
     167        32291 :     q->max_results = -1;
     168              : 
     169        32291 :     q->primary_sort.param_list = g_slist_prepend (static_cast<GSList*>(nullptr),
     170              :                                                   static_cast<void*>(const_cast<char*>(QUERY_DEFAULT_SORT)));
     171        32291 :     q->primary_sort.increasing = TRUE;
     172        32291 :     q->secondary_sort.increasing = TRUE;
     173        32291 :     q->tertiary_sort.increasing = TRUE;
     174        32291 : }
     175              : 
     176         8516 : static void swap_terms (QofQuery *q1, QofQuery *q2)
     177              : {
     178              :     GList *g;
     179              : 
     180         8516 :     if (!q1 || !q2) return;
     181              : 
     182         8516 :     g = q1->terms;
     183         8516 :     q1->terms = q2->terms;
     184         8516 :     q2->terms = g;
     185              : 
     186         8516 :     g = q1->books;
     187         8516 :     q1->books = q2->books;
     188         8516 :     q2->books = g;
     189              : 
     190         8516 :     q1->changed = 1;
     191         8516 :     q2->changed = 1;
     192              : }
     193              : 
     194        54214 : static void free_query_term (QofQueryTerm *qt)
     195              : {
     196        54214 :     if (!qt) return;
     197              : 
     198        54214 :     qof_query_core_predicate_free (qt->pdata);
     199        54214 :     g_slist_free (qt->param_list);
     200        54214 :     g_slist_free (qt->param_fcns);
     201        54214 :     g_free (qt);
     202              : }
     203              : 
     204        46235 : static QofQueryTerm * copy_query_term (const QofQueryTerm *qt)
     205              : {
     206              :     QofQueryTerm *new_qt;
     207        46235 :     if (!qt) return nullptr;
     208              : 
     209        46235 :     new_qt = g_new0 (QofQueryTerm, 1);
     210        46235 :     *new_qt = *qt;
     211        46235 :     new_qt->param_list = g_slist_copy (qt->param_list);
     212        46235 :     new_qt->param_fcns = g_slist_copy (qt->param_fcns);
     213        46235 :     new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
     214        46235 :     return new_qt;
     215              : }
     216              : 
     217        16847 : static GList * copy_and_terms (const GList *and_terms)
     218              : {
     219        16847 :     GList *_and_ = nullptr;
     220              :     const GList *cur_and;
     221              : 
     222        62223 :     for (cur_and = and_terms; cur_and; cur_and = cur_and->next)
     223              :     {
     224        45376 :         _and_ = g_list_prepend(_and_, copy_query_term (static_cast<QofQueryTerm*>(cur_and->data)));
     225              :     }
     226              : 
     227        16847 :     return g_list_reverse(_and_);
     228              : }
     229              : 
     230              : static GList *
     231        10744 : copy_or_terms(const GList * or_terms)
     232              : {
     233        10744 :     GList * _or_ = nullptr;
     234              :     const GList * cur_or;
     235              : 
     236        17007 :     for (cur_or = or_terms; cur_or; cur_or = cur_or->next)
     237              :     {
     238         6263 :         _or_ = g_list_prepend(_or_, copy_and_terms(static_cast<GList*>(cur_or->data)));
     239              :     }
     240              : 
     241        10744 :     return g_list_reverse(_or_);
     242              : }
     243              : 
     244            0 : static void copy_sort (QofQuerySort *dst, const QofQuerySort *src)
     245              : {
     246            0 :     *dst = *src;
     247            0 :     dst->param_list = g_slist_copy (src->param_list);
     248            0 :     dst->param_fcns = g_slist_copy (src->param_fcns);
     249            0 : }
     250              : 
     251        72783 : static void free_sort (QofQuerySort *s)
     252              : {
     253        72783 :     g_slist_free (s->param_list);
     254        72783 :     s->param_list = nullptr;
     255              : 
     256        72783 :     g_slist_free (s->param_fcns);
     257        72783 :     s->param_fcns = nullptr;
     258        72783 : }
     259              : 
     260        24261 : static void free_members (QofQuery *q)
     261              : {
     262              :     GList * cur_or;
     263              : 
     264        24261 :     if (q == nullptr) return;
     265              : 
     266        44665 :     for (cur_or = q->terms; cur_or; cur_or = cur_or->next)
     267              :     {
     268              :         GList * cur_and;
     269              : 
     270        74618 :         for (cur_and = static_cast<GList*>(cur_or->data); cur_and;
     271        54214 :              cur_and = static_cast<GList*>(cur_and->next))
     272              :         {
     273        54214 :             free_query_term(static_cast<QofQueryTerm*>(cur_and->data));
     274        54214 :             cur_and->data = nullptr;
     275              :         }
     276              : 
     277        20404 :         g_list_free(static_cast<GList*>(cur_or->data));
     278        20404 :         cur_or->data = nullptr;
     279              :     }
     280              : 
     281        24261 :     free_sort (&(q->primary_sort));
     282        24261 :     free_sort (&(q->secondary_sort));
     283        24261 :     free_sort (&(q->tertiary_sort));
     284              : 
     285        24261 :     g_list_free(q->terms);
     286        24261 :     q->terms = nullptr;
     287              : 
     288        24261 :     g_list_free(q->books);
     289        24261 :     q->books = nullptr;
     290              : 
     291        24261 :     g_list_free(q->results);
     292        24261 :     q->results = nullptr;
     293              : }
     294              : 
     295         9428 : static int cmp_func (const QofQuerySort *sort, QofSortFunc default_sort,
     296              :                      const gconstpointer a, const gconstpointer b)
     297              : {
     298         9428 :     QofParam *param = nullptr;
     299              :     GSList *node;
     300              :     gpointer conva, convb;
     301              : 
     302         9428 :     g_return_val_if_fail (sort, 0);
     303              : 
     304              :     /* See if this is a default sort */
     305         9428 :     if (sort->use_default)
     306              :     {
     307         7385 :         if (default_sort) return default_sort (a, b);
     308            0 :         return 0;
     309              :     }
     310              : 
     311              :     /* If no parameters, consider them equal */
     312         2043 :     if (!sort->param_fcns) return 0;
     313              : 
     314              :     /* no compare function, consider the two objects equal */
     315         2019 :     if (!sort->comp_fcn && !sort->obj_cmp) return 0;
     316              : 
     317              :     /* Do the list of conversions */
     318         2019 :     conva = (gpointer)a;
     319         2019 :     convb = (gpointer)b;
     320         4027 :     for (node = static_cast<GSList*>(sort->param_fcns); node;
     321         2008 :          node = static_cast<GSList*>(node->next))
     322              :     {
     323         2471 :         param = static_cast<QofParam*>(node->data);
     324              : 
     325              :         /* The last term is really the "parameter getter",
     326              :          * unless we're comparing objects ;) */
     327         2471 :         if (!node->next && !sort->obj_cmp)
     328          463 :             break;
     329              : 
     330              :         /* Do the conversions */
     331         2008 :         conva = (param->param_getfcn) (conva, param);
     332         2008 :         convb = (param->param_getfcn) (convb, param);
     333              :     }
     334              : 
     335              :     /* And now return the (appropriate) compare */
     336         2019 :     if (sort->comp_fcn)
     337              :     {
     338          463 :         int rc = sort->comp_fcn (conva, convb, sort->options, param);
     339          463 :         return rc;
     340              :     }
     341              : 
     342         1556 :     return sort->obj_cmp (conva, convb);
     343              : }
     344              : 
     345              : static int
     346         7952 : sort_func (const gconstpointer a, const gconstpointer b, const gpointer q)
     347              : {
     348              :     int retval;
     349         7952 :     const QofQuery *sortQuery = static_cast<const QofQuery*>(q);
     350              : 
     351         7952 :     g_return_val_if_fail (sortQuery, 0);
     352              : 
     353         7952 :     retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b);
     354         7952 :     if (retval == 0)
     355              :     {
     356         1456 :         retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort,
     357              :                            a, b);
     358         1456 :         if (retval == 0)
     359              :         {
     360           20 :             retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort,
     361              :                                a, b);
     362           20 :             return sortQuery->tertiary_sort.increasing ? retval : -retval;
     363              :         }
     364              :         else
     365              :         {
     366         1436 :             return sortQuery->secondary_sort.increasing ? retval : -retval;
     367              :         }
     368              :     }
     369              :     else
     370              :     {
     371         6496 :         return sortQuery->primary_sort.increasing ? retval : -retval;
     372              :     }
     373              : }
     374              : 
     375              : /* ==================================================================== */
     376              : /* This is the main workhorse for performing the query.  For each
     377              :  * object, it walks over all of the query terms to see if the
     378              :  * object passes the seive.
     379              :  */
     380              : 
     381              : static int
     382        23443 : check_object (const QofQuery *q, gpointer object)
     383              : {
     384              :     const GList     * and_ptr;
     385              :     const GList     * or_ptr;
     386              :     const QofQueryTerm * qt;
     387        23443 :     int       and_terms_ok = 1;
     388              : 
     389        46462 :     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
     390              :     {
     391        25342 :         and_terms_ok = 1;
     392        73452 :         for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
     393        48110 :              and_ptr = static_cast<GList*>(and_ptr->next))
     394              :         {
     395        71129 :             qt = (QofQueryTerm *)(and_ptr->data);
     396        71129 :             if (qt->param_fcns && qt->pred_fcn)
     397              :             {
     398              :                 const GSList *node;
     399        71129 :                 QofParam *param = nullptr;
     400        71129 :                 gpointer conv_obj = object;
     401              : 
     402              :                 /* iterate through the conversions */
     403       130090 :                 for (node = qt->param_fcns; node; node = node->next)
     404              :                 {
     405       130090 :                     param = static_cast<QofParam*>(node->data);
     406              : 
     407              :                     /* The last term is the actual parameter getter */
     408       130090 :                     if (!node->next) break;
     409              : 
     410        58961 :                     conv_obj = (void*)param->param_getfcn (conv_obj, param);
     411              :                 }
     412              : 
     413        71129 :                 if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert)
     414              :                 {
     415        23019 :                     and_terms_ok = 0;
     416        23019 :                     break;
     417              :                 }
     418              :             }
     419              :             else
     420              :             {
     421              :                 /* XXX: Don't know how to do this conversion -- do we care? */
     422              :             }
     423              :         }
     424        25342 :         if (and_terms_ok)
     425              :         {
     426         2323 :             return 1;
     427              :         }
     428              :     }
     429              : 
     430              :     /* If there are no terms, assume a "match any" applies.
     431              :      * A query with no terms is still meaningful, since the user
     432              :      * may want to get all objects, but in a particular sorted
     433              :      * order.
     434              :      */
     435        21120 :     if (nullptr == q->terms) return 1;
     436        21120 :     return 0;
     437              : }
     438              : 
     439              : /* walk the list of parameters, starting with the given object, and
     440              :  * compile the list of parameter get-functions.  Save the last valid
     441              :  * parameter definition in "final" and return the list of functions.
     442              :  *
     443              :  * returns nullptr if the first parameter is bad (and final is unchanged).
     444              :  */
     445              : static GSList *
     446         5149 : compile_params (QofQueryParamList *param_list, QofIdType start_obj,
     447              :                 QofParam const **final)
     448              : {
     449         5149 :     const QofParam *objDef = nullptr;
     450         5149 :     GSList *fcns = nullptr;
     451              : 
     452         5149 :     ENTER ("param_list=%p id=%s", param_list, start_obj);
     453         5149 :     g_return_val_if_fail (param_list, nullptr);
     454         5149 :     g_return_val_if_fail (start_obj, nullptr);
     455         5149 :     g_return_val_if_fail (final, nullptr);
     456              : 
     457        13237 :     for (; param_list; param_list = param_list->next)
     458              :     {
     459         8485 :         QofIdType param_name = static_cast<QofIdType>(param_list->data);
     460         8485 :         objDef = qof_class_get_parameter (start_obj, param_name);
     461              : 
     462              :         /* If it doesn't exist, then we've reached the end */
     463         8485 :         if (!objDef) break;
     464              : 
     465              :         /* Save off this parameter */
     466         8088 :         fcns = g_slist_prepend (fcns, (gpointer) objDef);
     467              : 
     468              :         /* Save this off, just in case */
     469         8088 :         *final = objDef;
     470              : 
     471              :         /* And reset for the next parameter */
     472         8088 :         start_obj = (QofIdType) objDef->param_type;
     473              :     }
     474              : 
     475         5149 :     LEAVE ("fcns=%p", fcns);
     476         5149 :     return (g_slist_reverse (fcns));
     477              : }
     478              : 
     479              : static void
     480         1116 : compile_sort (QofQuerySort *sort, QofIdType obj)
     481              : {
     482         1116 :     const QofParam *resObj = nullptr;
     483              : 
     484         1116 :     ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
     485         1116 :     sort->use_default = FALSE;
     486              : 
     487         1116 :     g_slist_free (sort->param_fcns);
     488         1116 :     sort->param_fcns = nullptr;
     489         1116 :     sort->comp_fcn = nullptr;
     490         1116 :     sort->obj_cmp = nullptr;
     491              : 
     492              :     /* An empty param_list implies "no sort" */
     493         1116 :     if (!sort->param_list)
     494              :     {
     495          652 :         LEAVE (" ");
     496          652 :         return;
     497              :     }
     498              : 
     499              :     /* Walk the parameter list of obtain the parameter functions */
     500          464 :     sort->param_fcns = compile_params (sort->param_list, obj, &resObj);
     501              : 
     502              :     /* If we have valid parameters, grab the compare function,
     503              :      * If not, check if this is the default sort.
     504              :      */
     505          464 :     if (sort->param_fcns && resObj)
     506              :     {
     507              :         /* First, check if this parameter has a sort function override.
     508              :          * if not then check if there's a global compare function for the type
     509              :          */
     510           67 :         if (resObj->param_compfcn)
     511            1 :             sort->comp_fcn = resObj->param_compfcn;
     512              :         else
     513           66 :             sort->comp_fcn = qof_query_core_get_compare (resObj->param_type);
     514              : 
     515              :         /* Next, perhaps this is an object compare, not a core type compare? */
     516           67 :         if (sort->comp_fcn == nullptr)
     517           39 :             sort->obj_cmp = qof_class_get_default_sort (resObj->param_type);
     518              :     }
     519          397 :     else if (!g_strcmp0 (static_cast<char*>(sort->param_list->data),
     520              :                          QUERY_DEFAULT_SORT))
     521              :     {
     522          397 :         sort->use_default = TRUE;
     523              :     }
     524          464 :     LEAVE ("sort=%p id=%s", sort, obj);
     525              : }
     526              : 
     527          372 : static void compile_terms (QofQuery *q)
     528              : {
     529              :     GList *or_ptr, *and_ptr;
     530              : 
     531          372 :     ENTER (" query=%p", q);
     532              :     /* Find the specific functions for this Query.  Note that the
     533              :      * Query's search_for should now be set to the new type.
     534              :      */
     535          773 :     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
     536              :     {
     537         5086 :         for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
     538         4685 :              and_ptr = static_cast<GList*>(and_ptr->next))
     539              :         {
     540         4685 :             QofQueryTerm* qt = static_cast<QofQueryTerm*>(and_ptr->data);
     541         4685 :             const QofParam* resObj = nullptr;
     542              : 
     543         4685 :             g_slist_free (qt->param_fcns);
     544         4685 :             qt->param_fcns = nullptr;
     545              : 
     546              :             /* Walk the parameter list of obtain the parameter functions */
     547         4685 :             qt->param_fcns = compile_params (qt->param_list, q->search_for,
     548              :                                              &resObj);
     549              : 
     550              :             /* If we have valid parameters, grab the predicate function,
     551              :              * If not, see if this is the default sort.
     552              :              */
     553              : 
     554         4685 :             if (qt->param_fcns && resObj)
     555         4685 :                 qt->pred_fcn = qof_query_core_get_predicate (resObj->param_type);
     556              :             else
     557            0 :                 qt->pred_fcn = nullptr;
     558              :         }
     559              :     }
     560              : 
     561              :     /* Update the sort functions */
     562          372 :     compile_sort (&(q->primary_sort), q->search_for);
     563          372 :     compile_sort (&(q->secondary_sort), q->search_for);
     564          372 :     compile_sort (&(q->tertiary_sort), q->search_for);
     565              : 
     566          372 :     q->defaultSort = qof_class_get_default_sort (q->search_for);
     567              : #ifdef QOF_BACKEND_QUERY
     568              :     /* Now compile the backend instances */
     569              :     for (node = q->books; node; node = node->next)
     570              :     {
     571              :         QofBook* book = static_cast<QofBook*>(node->data);
     572              :         QofBackend* be = book->backend;
     573              : 
     574              :         if (be && be->compile_query)
     575              :         {
     576              :             gpointer result = (be->compile_query)(be, q);
     577              :             if (result)
     578              :                 g_hash_table_insert (q->be_compiled, book, result);
     579              :         }
     580              : 
     581              :     }
     582              : #endif
     583          372 :     LEAVE (" query=%p", q);
     584          372 : }
     585              : 
     586        23443 : static void check_item_cb (gpointer object, gpointer user_data)
     587              : {
     588        23443 :     QofQueryCB* ql = static_cast<QofQueryCB*>(user_data);
     589              : 
     590        23443 :     if (!object || !ql) return;
     591              : 
     592        23443 :     if (check_object (ql->query, object))
     593              :     {
     594         2323 :         ql->list = g_list_prepend (ql->list, object);
     595         2323 :         ql->count++;
     596              :     }
     597        23443 :     return;
     598              : }
     599              : 
     600         3672 : static int param_list_cmp (const QofQueryParamList *l1, const QofQueryParamList *l2)
     601              : {
     602              :     int ret;
     603              : 
     604              :     while (1)
     605              :     {
     606              : 
     607              :         /* Check the easy stuff */
     608         8711 :         if (!l1 && !l2) return 0;
     609         5039 :         if (!l1 && l2) return -1;
     610         5039 :         if (l1 && !l2) return 1;
     611              : 
     612        10078 :         ret = g_strcmp0 (static_cast<char*>(l1->data),
     613         5039 :                          static_cast<char*>(l2->data));
     614         5039 :         if (ret)
     615            0 :             break;
     616              : 
     617         5039 :         l1 = l1->next;
     618         5039 :         l2 = l2->next;
     619              :     }
     620            0 :     return ret;
     621              : }
     622              : 
     623        10505 : static GList * merge_books (GList *l1, GList *l2)
     624              : {
     625        10505 :     GList *res = nullptr;
     626              :     GList *node;
     627              : 
     628        10505 :     res = g_list_copy (l1);
     629              : 
     630        10505 :     for (node = l2; node; node = node->next)
     631              :     {
     632            0 :         if (g_list_index (res, node->data) == -1)
     633            0 :             res = g_list_prepend (res, node->data);
     634              :     }
     635              : 
     636        10505 :     return res;
     637              : }
     638              : 
     639              : static gboolean
     640            0 : query_free_compiled (gpointer key, gpointer value, gpointer not_used)
     641              : {
     642              : #ifdef QOF_BACKEND_QUERY
     643              :     QofBook* book = static_cast<QofBook*>(key);
     644              :     QofBackend* be = book->backend;
     645              : 
     646              :     if (be && be->free_query)
     647              :         (be->free_query)(be, value);
     648              : #endif
     649            0 :     return TRUE;
     650              : }
     651              : 
     652              : /* clear out any cached query_compilations */
     653        24633 : static void query_clear_compiles (QofQuery *q)
     654              : {
     655        24633 :     g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, nullptr);
     656        24633 : }
     657              : 
     658              : /********************************************************************/
     659              : /* PUBLISHED API FUNCTIONS */
     660              : 
     661              : QofQueryParamList *
     662         3462 : qof_query_build_param_list (char const *param, ...)
     663              : {
     664         3462 :     QofQueryParamList *param_list = nullptr;
     665              :     char const *this_param;
     666              :     va_list ap;
     667              : 
     668         3462 :     if (!param)
     669            0 :         return nullptr;
     670              : 
     671         3462 :     va_start (ap, param);
     672              : 
     673         9800 :     for (this_param = param; this_param; this_param = va_arg (ap, const char *))
     674         6338 :         param_list = g_slist_prepend (param_list, (gpointer)this_param);
     675              : 
     676         3462 :     va_end (ap);
     677              : 
     678         3462 :     return g_slist_reverse (param_list);
     679              : }
     680              : 
     681         7983 : void qof_query_add_term (QofQuery *q, QofQueryParamList *param_list,
     682              :                          QofQueryPredData *pred_data, QofQueryOp op)
     683              : {
     684              :     QofQueryTerm *qt;
     685              :     QofQuery *qr, *qs;
     686              : 
     687         7983 :     if (!q || !param_list || !pred_data) return;
     688              : 
     689         7983 :     qt = g_new0 (QofQueryTerm, 1);
     690         7983 :     qt->param_list = param_list;
     691         7983 :     qt->pdata = pred_data;
     692         7983 :     qs = qof_query_create ();
     693         7983 :     query_init (qs, qt);
     694              : 
     695         7983 :     if (q->terms != nullptr)
     696         4382 :         qr = qof_query_merge (q, qs, op);
     697              :     else
     698         3601 :         qr = qof_query_merge (q, qs, QOF_QUERY_OR);
     699              : 
     700         7983 :     swap_terms (q, qr);
     701         7983 :     qof_query_destroy (qs);
     702         7983 :     qof_query_destroy (qr);
     703              : }
     704              : 
     705            0 : void qof_query_purge_terms (QofQuery *q, QofQueryParamList *param_list)
     706              : {
     707              :     QofQueryTerm *qt;
     708              :     GList *_or_, *_and_;
     709              : 
     710            0 :     if (!q || !param_list) return;
     711              : 
     712            0 :     for (_or_ = q->terms; _or_; _or_ = _or_->next)
     713              :     {
     714            0 :         for (_and_ = static_cast<GList*>(_or_->data); _and_;
     715            0 :              _and_ = static_cast<GList*>(_and_->next))
     716              :         {
     717            0 :             qt = static_cast<QofQueryTerm*>(_and_->data);
     718            0 :             if (!param_list_cmp (qt->param_list, param_list))
     719              :             {
     720            0 :                 auto or_list = static_cast<GList*> (_or_->data);
     721            0 :                 if (or_list && !or_list->next)
     722              :                 {
     723            0 :                     q->terms = g_list_remove_link (static_cast<GList*>(q->terms), _or_);
     724            0 :                     g_list_free_1 (_or_);
     725            0 :                     _or_ = q->terms;
     726            0 :                     break;
     727              :                 }
     728              :                 else
     729              :                 {
     730            0 :                     _or_->data = g_list_remove_link (or_list, _and_);
     731            0 :                     g_list_free_1 (_and_);
     732            0 :                     _and_ = static_cast<GList*>(_or_->data);
     733            0 :                     if (!_and_) break;
     734              :                 }
     735            0 :                 q->changed = 1;
     736            0 :                 free_query_term (qt);
     737              :             }
     738              :         }
     739            0 :         if (!_or_) break;
     740              :     }
     741              : }
     742              : 
     743          378 : static GList * qof_query_run_internal (QofQuery *q,
     744              :                                        void(*run_cb)(QofQueryCB*, gpointer),
     745              :                                        gpointer cb_arg)
     746              : {
     747          378 :     GList *matching_objects = nullptr;
     748          378 :     int        object_count = 0;
     749              : 
     750          378 :     if (!q) return nullptr;
     751          372 :     g_return_val_if_fail (q->search_for, nullptr);
     752          372 :     g_return_val_if_fail (q->books, nullptr);
     753          372 :     g_return_val_if_fail (run_cb, nullptr);
     754          372 :     ENTER (" q=%p", q);
     755              : 
     756              :     /* XXX: Prioritize the query terms? */
     757              : 
     758              :     /* prepare the Query for processing */
     759          372 :     if (q->changed)
     760              :     {
     761          372 :         query_clear_compiles (q);
     762          372 :         compile_terms (q);
     763              :     }
     764              : 
     765              :     /* Maybe log this sucker */
     766          372 :     if (qof_log_check (log_module, QOF_LOG_DEBUG))
     767            0 :         qof_query_print (q);
     768              : 
     769              :     /* Now run the query over all the objects and save the results */
     770              :     {
     771              :         QofQueryCB qcb;
     772              : 
     773          372 :         memset (&qcb, 0, sizeof (qcb));
     774          372 :         qcb.query = q;
     775              : 
     776              :         /* Run the query callback */
     777          372 :         run_cb(&qcb, cb_arg);
     778              : 
     779          372 :         matching_objects = qcb.list;
     780          372 :         object_count = qcb.count;
     781              :     }
     782          372 :     PINFO ("matching objects=%p count=%d", matching_objects, object_count);
     783              : 
     784              :     /* There is no absolute need to reverse this list, since it's being
     785              :      * sorted below. However, in the common case, we will be searching
     786              :      * in a confined location where the objects are already in order,
     787              :      * thus reversing will put us in the correct order we want and make
     788              :      * the sorting go much faster.
     789              :      */
     790          372 :     matching_objects = g_list_reverse(matching_objects);
     791              : 
     792              :     /* Now sort the matching objects based on the search criteria */
     793          372 :     if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
     794          308 :             (q->primary_sort.use_default && q->defaultSort))
     795              :     {
     796          372 :         matching_objects = g_list_sort_with_data(matching_objects, sort_func, q);
     797              :     }
     798              : 
     799              :     /* Crop the list to limit the number of splits. */
     800          372 :     if ((object_count > q->max_results) && (q->max_results > -1))
     801              :     {
     802            0 :         if (q->max_results > 0)
     803              :         {
     804              :             GList *mptr;
     805              : 
     806              :             /* mptr is set to the first node of what will be the new list */
     807            0 :             mptr = g_list_nth(matching_objects, object_count - q->max_results);
     808              :             /* mptr should not be nullptr, but let's be safe */
     809            0 :             if (mptr != nullptr)
     810              :             {
     811            0 :                 if (mptr->prev != nullptr) mptr->prev->next = nullptr;
     812            0 :                 mptr->prev = nullptr;
     813              :             }
     814            0 :             g_list_free(matching_objects);
     815            0 :             matching_objects = mptr;
     816              :         }
     817              :         else
     818              :         {
     819              :             /* q->max_results == 0 */
     820            0 :             g_list_free(matching_objects);
     821            0 :             matching_objects = nullptr;
     822              :         }
     823              :     }
     824              : 
     825          372 :     q->changed = 0;
     826              : 
     827          372 :     g_list_free(q->results);
     828          372 :     q->results = matching_objects;
     829              : 
     830          372 :     LEAVE (" q=%p", q);
     831          372 :     return matching_objects;
     832              : }
     833              : 
     834          372 : static void qof_query_run_cb(QofQueryCB* qcb, gpointer cb_arg)
     835              : {
     836              :     GList *node;
     837              : 
     838              :     (void)cb_arg; /* unused */
     839          372 :     g_return_if_fail(qcb);
     840              : 
     841          744 :     for (node = qcb->query->books; node; node = node->next)
     842              :     {
     843          372 :         QofBook* book = static_cast<QofBook*>(node->data);
     844              : #ifdef QOF_BACKEND_QUERY
     845              :         QofBackend* be = book->backend;
     846              : 
     847              : 
     848              :         if (be)
     849              :         {
     850              :             gpointer compiled_query = g_hash_table_lookup (qcb->query->be_compiled,
     851              :                                       book);
     852              : 
     853              :             if (compiled_query && be->run_query)
     854              :             {
     855              :                 (be->run_query) (be, compiled_query);
     856              :             }
     857              :         }
     858              : #endif
     859              :         /* And then iterate over all the objects */
     860          372 :         qof_object_foreach (qcb->query->search_for, book,
     861              :                             (QofInstanceForeachCB) check_item_cb, qcb);
     862              :     }
     863              : }
     864              : 
     865          378 : GList * qof_query_run (QofQuery *q)
     866              : {
     867              :     /* Just a wrapper */
     868          378 :     return qof_query_run_internal(q, qof_query_run_cb, nullptr);
     869              : }
     870              : 
     871            0 : static void qof_query_run_subq_cb(QofQueryCB* qcb, gpointer cb_arg)
     872              : {
     873            0 :     QofQuery* pq = static_cast<QofQuery*>(cb_arg);
     874              : 
     875            0 :     g_return_if_fail(pq);
     876            0 :     g_list_foreach(qof_query_last_run(pq), check_item_cb, qcb);
     877              : }
     878              : 
     879              : GList *
     880            0 : qof_query_run_subquery (QofQuery *subq, const QofQuery* primaryq)
     881              : {
     882            0 :     if (!subq) return nullptr;
     883            0 :     if (!primaryq) return nullptr;
     884              : 
     885              :     /* Make sure we're searching for the same thing */
     886            0 :     g_return_val_if_fail (subq->search_for, nullptr);
     887            0 :     g_return_val_if_fail (primaryq->search_for, nullptr);
     888            0 :     g_return_val_if_fail(!g_strcmp0(subq->search_for, primaryq->search_for),
     889              :                          nullptr);
     890              : 
     891              :     /* Perform the subquery */
     892            0 :     return qof_query_run_internal(subq, qof_query_run_subq_cb,
     893            0 :                                   (gpointer)primaryq);
     894              : }
     895              : 
     896              : GList *
     897            0 : qof_query_last_run (QofQuery *query)
     898              : {
     899            0 :     if (!query)
     900            0 :         return nullptr;
     901              : 
     902            0 :     return query->results;
     903              : }
     904              : 
     905            0 : void qof_query_clear (QofQuery *query)
     906              : {
     907            0 :     QofQuery *q2 = qof_query_create ();
     908            0 :     swap_terms (query, q2);
     909            0 :     qof_query_destroy (q2);
     910              : 
     911            0 :     g_list_free (query->books);
     912            0 :     query->books = nullptr;
     913            0 :     g_list_free (query->results);
     914            0 :     query->results = nullptr;
     915            0 :     query->changed = 1;
     916            0 : }
     917              : 
     918        24308 : QofQuery * qof_query_create (void)
     919              : {
     920        24308 :     QofQuery *qp = g_new0 (QofQuery, 1);
     921        24308 :     qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
     922        24308 :     query_init (qp, nullptr);
     923        24308 :     return qp;
     924              : }
     925              : 
     926         3661 : void qof_query_search_for (QofQuery *q, QofIdTypeConst obj_type)
     927              : {
     928         3661 :     if (!q || !obj_type)
     929            0 :         return;
     930              : 
     931         3661 :     if (g_strcmp0 (q->search_for, obj_type))
     932              :     {
     933         2602 :         q->search_for = (QofIdType) obj_type;
     934         2602 :         q->changed = 1;
     935              :     }
     936              : }
     937              : 
     938         2601 : QofQuery * qof_query_create_for (QofIdTypeConst obj_type)
     939              : {
     940              :     QofQuery *q;
     941         2601 :     if (!obj_type)
     942            0 :         return nullptr;
     943         2601 :     q = qof_query_create ();
     944         2601 :     qof_query_search_for (q, obj_type);
     945         2601 :     return q;
     946              : }
     947              : 
     948            0 : int qof_query_has_terms (QofQuery *q)
     949              : {
     950            0 :     if (!q) return 0;
     951            0 :     return g_list_length (q->terms);
     952              : }
     953              : 
     954            0 : int qof_query_num_terms (QofQuery *q)
     955              : {
     956              :     GList *o;
     957            0 :     int n = 0;
     958            0 :     if (!q) return 0;
     959            0 :     for (o = q->terms; o; o = o->next)
     960            0 :         n += g_list_length(static_cast<GList*>(o->data));
     961            0 :     return n;
     962              : }
     963              : 
     964            0 : gboolean qof_query_has_term_type (QofQuery *q, QofQueryParamList *term_param)
     965              : {
     966              :     GList *_or_;
     967              :     GList *_and_;
     968              : 
     969            0 :     if (!q || !term_param)
     970            0 :         return FALSE;
     971              : 
     972            0 :     for (_or_ = q->terms; _or_; _or_ = _or_->next)
     973              :     {
     974            0 :         for (_and_ = static_cast<GList*>(_or_->data); _and_;
     975            0 :              _and_ = static_cast<GList*>(_and_->next))
     976              :         {
     977            0 :             QofQueryTerm* qt = static_cast<QofQueryTerm*>(_and_->data);
     978            0 :             if (!param_list_cmp (term_param, qt->param_list))
     979            0 :                 return TRUE;
     980              :         }
     981              :     }
     982              : 
     983            0 :     return FALSE;
     984              : }
     985              : 
     986            0 : GSList * qof_query_get_term_type (QofQuery *q, QofQueryParamList *term_param)
     987              : {
     988              :     GList *_or_;
     989              :     GList *_and_;
     990            0 :     GSList *results = nullptr;
     991              : 
     992            0 :     if (!q || !term_param)
     993            0 :         return FALSE;
     994              : 
     995            0 :     for (_or_ = q->terms; _or_; _or_ = _or_->next)
     996              :     {
     997            0 :         for (_and_ = static_cast<GList*>(_or_->data); _and_;
     998            0 :              _and_ = static_cast<GList*>(_and_->next))
     999              :         {
    1000            0 :             QofQueryTerm *qt = static_cast<QofQueryTerm*>(_and_->data);
    1001            0 :             if (!param_list_cmp (term_param, qt->param_list))
    1002            0 :                 results = g_slist_prepend (results, qt->pdata);
    1003              :         }
    1004              :     }
    1005              : 
    1006            0 :     return g_slist_reverse (results);
    1007              : }
    1008              : 
    1009        24268 : void qof_query_destroy (QofQuery *q)
    1010              : {
    1011        24268 :     if (!q) return;
    1012        24261 :     free_members (q);
    1013        24261 :     query_clear_compiles (q);
    1014        24261 :     g_hash_table_destroy (q->be_compiled);
    1015        24261 :     g_free (q);
    1016              : }
    1017              : 
    1018            0 : QofQuery * qof_query_copy (QofQuery *q)
    1019              : {
    1020              :     QofQuery *copy;
    1021              :     GHashTable *ht;
    1022              : 
    1023            0 :     if (!q) return nullptr;
    1024            0 :     copy = qof_query_create ();
    1025            0 :     ht = copy->be_compiled;
    1026            0 :     free_members (copy);
    1027              : 
    1028            0 :     *copy = *q;
    1029              : 
    1030            0 :     copy->be_compiled = ht;
    1031            0 :     copy->terms = copy_or_terms (q->terms);
    1032            0 :     copy->books = g_list_copy (q->books);
    1033            0 :     copy->results = g_list_copy (q->results);
    1034              : 
    1035            0 :     copy_sort (&(copy->primary_sort), &(q->primary_sort));
    1036            0 :     copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
    1037            0 :     copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
    1038              : 
    1039            0 :     copy->changed = 1;
    1040              : 
    1041            0 :     return copy;
    1042              : }
    1043              : 
    1044              : /* *******************************************************************
    1045              :  * qof_query_invert
    1046              :  * return a newly-allocated Query object which is the
    1047              :  * logical inverse of the original.
    1048              :  ********************************************************************/
    1049              : 
    1050          874 : QofQuery * qof_query_invert (QofQuery *q)
    1051              : {
    1052              :     QofQuery  * retval;
    1053              :     QofQuery  * right, * left, * iright, * ileft;
    1054              :     QofQueryTerm * qt;
    1055              :     GList  * aterms;
    1056              :     GList  * cur;
    1057              :     GList  * new_oterm;
    1058              :     int    num_or_terms;
    1059              : 
    1060          874 :     if (!q)
    1061            0 :         return nullptr;
    1062              : 
    1063          874 :     num_or_terms = g_list_length(q->terms);
    1064              : 
    1065          874 :     switch (num_or_terms)
    1066              :     {
    1067           31 :     case 0:
    1068           31 :         retval = qof_query_create();
    1069           31 :         retval->max_results = q->max_results;
    1070           31 :         break;
    1071              : 
    1072              :         /* This is the DeMorgan expansion for a single AND expression. */
    1073              :         /* !(abc) = !a + !b + !c */
    1074          825 :     case 1:
    1075          825 :         retval = qof_query_create();
    1076          825 :         retval->max_results = q->max_results;
    1077          825 :         retval->books = g_list_copy (q->books);
    1078          825 :         retval->search_for = q->search_for;
    1079          825 :         retval->changed = 1;
    1080              : 
    1081          825 :         aterms = static_cast<GList*>(g_list_nth_data(q->terms, 0));
    1082          825 :         new_oterm = nullptr;
    1083         1684 :         for (cur = aterms; cur; cur = cur->next)
    1084              :         {
    1085          859 :             qt = copy_query_term(static_cast<QofQueryTerm*>(cur->data));
    1086          859 :             qt->invert = !(qt->invert);
    1087          859 :             new_oterm = g_list_append(nullptr, qt);
    1088          859 :             retval->terms = g_list_prepend(retval->terms, new_oterm);
    1089              :         }
    1090          825 :         retval->terms = g_list_reverse(retval->terms);
    1091          825 :         break;
    1092              : 
    1093              :         /* If there are multiple OR-terms, we just recurse by
    1094              :          * breaking it down to !(a + b + c) =
    1095              :          * !a * !(b + c) = !a * !b * !c.  */
    1096           18 :     default:
    1097           18 :         right        = qof_query_create();
    1098           18 :         right->terms = copy_or_terms(g_list_nth(q->terms, 1));
    1099              : 
    1100           18 :         left         = qof_query_create();
    1101           36 :         left->terms  = g_list_append(nullptr,
    1102           18 :                                      copy_and_terms(static_cast<GList*>(g_list_nth_data(q->terms, 0))));
    1103              : 
    1104           18 :         iright       = qof_query_invert(right);
    1105           18 :         ileft        = qof_query_invert(left);
    1106              : 
    1107           18 :         retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
    1108           18 :         retval->books          = g_list_copy (q->books);
    1109           18 :         retval->max_results    = q->max_results;
    1110           18 :         retval->search_for     = q->search_for;
    1111           18 :         retval->changed        = 1;
    1112              : 
    1113           18 :         qof_query_destroy(iright);
    1114           18 :         qof_query_destroy(ileft);
    1115           18 :         qof_query_destroy(right);
    1116           18 :         qof_query_destroy(left);
    1117           18 :         break;
    1118              :     }
    1119              : 
    1120          874 :     return retval;
    1121              : }
    1122              : 
    1123              : /* *******************************************************************
    1124              :  * qof_query_merge
    1125              :  * combine 2 Query objects by the logical operation in "op".
    1126              :  ********************************************************************/
    1127              : 
    1128              : QofQuery *
    1129        10709 : qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
    1130              : {
    1131              : 
    1132        10709 :     QofQuery * retval = nullptr;
    1133              :     QofQuery * i1, * i2;
    1134              :     QofQuery * t1, * t2;
    1135              :     GList * i, * j;
    1136              :     QofIdType search_for;
    1137              : 
    1138        10709 :     if (!q1) return q2;
    1139        10709 :     if (!q2) return q1;
    1140              : 
    1141        10709 :     if (q1->search_for && q2->search_for)
    1142           91 :         g_return_val_if_fail (g_strcmp0 (q1->search_for, q2->search_for) == 0,
    1143              :                               nullptr);
    1144              : 
    1145        10709 :     search_for = (q1->search_for ? q1->search_for : q2->search_for);
    1146              : 
    1147              :     /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
    1148              :      * The goal of this tweak is to allow the user to start with
    1149              :      * an empty q1 and then append to it recursively
    1150              :      * (and q1 (and q2 (and q3 (and q4 ....))))
    1151              :      * without bombing out because the append started with an
    1152              :      * empty list.
    1153              :      * We do essentially the same check in qof_query_add_term()
    1154              :      * so that the first term added to an empty query doesn't screw up.
    1155              :      */
    1156        10709 :     if ((QOF_QUERY_AND == op) &&
    1157         5213 :         (q1->terms == nullptr || q2->terms == nullptr))
    1158              :     {
    1159           71 :         op = QOF_QUERY_OR;
    1160              :     }
    1161              : 
    1162        10709 :     switch (op)
    1163              :     {
    1164         5363 :     case QOF_QUERY_OR:
    1165         5363 :         retval = qof_query_create();
    1166         5363 :         retval->terms =
    1167         5363 :             g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
    1168         5363 :         retval->books           = merge_books (q1->books, q2->books);
    1169         5363 :         retval->max_results    = q1->max_results;
    1170         5363 :         retval->changed        = 1;
    1171         5363 :         break;
    1172              : 
    1173         5142 :     case QOF_QUERY_AND:
    1174         5142 :         retval = qof_query_create();
    1175         5142 :         retval->books          = merge_books (q1->books, q2->books);
    1176         5142 :         retval->max_results    = q1->max_results;
    1177         5142 :         retval->changed        = 1;
    1178              : 
    1179              :         /* g_list_append() can take forever, so let's build the list in
    1180              :          * reverse and then reverse it at the end, to deal better with
    1181              :          * "large" queries.
    1182              :          */
    1183        10381 :         for (i = q1->terms; i; i = i->next)
    1184              :         {
    1185        10522 :             for (j = q2->terms; j; j = j->next)
    1186              :             {
    1187         5283 :                 retval->terms =
    1188         5283 :                     g_list_prepend(retval->terms,
    1189              :                                    g_list_concat
    1190         5283 :                                    (copy_and_terms(static_cast<GList*>(i->data)),
    1191         5283 :                                     copy_and_terms(static_cast<GList*>(j->data))));
    1192              :             }
    1193              :         }
    1194         5142 :         retval->terms = g_list_reverse(retval->terms);
    1195         5142 :         break;
    1196              : 
    1197           55 :     case QOF_QUERY_NAND:
    1198              :         /* !(a*b) = (!a + !b) */
    1199           55 :         i1     = qof_query_invert(q1);
    1200           55 :         i2     = qof_query_invert(q2);
    1201           55 :         retval = qof_query_merge(i1, i2, QOF_QUERY_OR);
    1202           55 :         qof_query_destroy(i1);
    1203           55 :         qof_query_destroy(i2);
    1204           55 :         break;
    1205              : 
    1206           74 :     case QOF_QUERY_NOR:
    1207              :         /* !(a+b) = (!a*!b) */
    1208           74 :         i1     = qof_query_invert(q1);
    1209           74 :         i2     = qof_query_invert(q2);
    1210           74 :         retval = qof_query_merge(i1, i2, QOF_QUERY_AND);
    1211           74 :         qof_query_destroy(i1);
    1212           74 :         qof_query_destroy(i2);
    1213           74 :         break;
    1214              : 
    1215           75 :     case QOF_QUERY_XOR:
    1216              :         /* a xor b = (a * !b) + (!a * b) */
    1217           75 :         i1     = qof_query_invert(q1);
    1218           75 :         i2     = qof_query_invert(q2);
    1219           75 :         t1     = qof_query_merge(q1, i2, QOF_QUERY_AND);
    1220           75 :         t2     = qof_query_merge(i1, q2, QOF_QUERY_AND);
    1221           75 :         retval = qof_query_merge(t1, t2, QOF_QUERY_OR);
    1222              : 
    1223           75 :         qof_query_destroy(i1);
    1224           75 :         qof_query_destroy(i2);
    1225           75 :         qof_query_destroy(t1);
    1226           75 :         qof_query_destroy(t2);
    1227           75 :         break;
    1228              :     }
    1229              : 
    1230        10709 :     if (retval)
    1231        10709 :         retval->search_for = search_for;
    1232        10709 :     return retval;
    1233              : }
    1234              : 
    1235              : void
    1236          533 : qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
    1237              : {
    1238              :     QofQuery *tmp_q;
    1239              : 
    1240          533 :     if (!q1 || !q2)
    1241            0 :         return;
    1242              : 
    1243          533 :     tmp_q = qof_query_merge (q1, q2, op);
    1244          533 :     swap_terms (q1, tmp_q);
    1245          533 :     qof_query_destroy (tmp_q);
    1246              : }
    1247              : 
    1248              : void
    1249         2223 : qof_query_set_sort_order (QofQuery *q,
    1250              :                           QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
    1251              : {
    1252         2223 :     if (!q) return;
    1253         2223 :     if (q->primary_sort.param_list)
    1254         2223 :         g_slist_free (q->primary_sort.param_list);
    1255         2223 :     q->primary_sort.param_list = params1;
    1256         2223 :     q->primary_sort.options = 0;
    1257              : 
    1258         2223 :     if (q->secondary_sort.param_list)
    1259            0 :         g_slist_free (q->secondary_sort.param_list);
    1260         2223 :     q->secondary_sort.param_list = params2;
    1261         2223 :     q->secondary_sort.options = 0;
    1262              : 
    1263         2223 :     if (q->tertiary_sort.param_list)
    1264            0 :         g_slist_free (q->tertiary_sort.param_list);
    1265         2223 :     q->tertiary_sort.param_list = params3;
    1266         2223 :     q->tertiary_sort.options = 0;
    1267              : 
    1268         2223 :     q->changed = 1;
    1269              : }
    1270              : 
    1271         1058 : void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
    1272              :                                  gint tert_op)
    1273              : {
    1274         1058 :     if (!q) return;
    1275         1058 :     q->primary_sort.options = prim_op;
    1276         1058 :     q->secondary_sort.options = sec_op;
    1277         1058 :     q->tertiary_sort.options = tert_op;
    1278              : }
    1279              : 
    1280         2216 : void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
    1281              :                                     gboolean sec_inc, gboolean tert_inc)
    1282              : {
    1283         2216 :     if (!q) return;
    1284         2216 :     q->primary_sort.increasing = prim_inc;
    1285         2216 :     q->secondary_sort.increasing = sec_inc;
    1286         2216 :     q->tertiary_sort.increasing = tert_inc;
    1287              : }
    1288              : 
    1289         2159 : void qof_query_set_max_results (QofQuery *q, int n)
    1290              : {
    1291         2159 :     if (!q) return;
    1292         2159 :     q->max_results = n;
    1293              : }
    1294              : 
    1295         1141 : void qof_query_add_guid_list_match (QofQuery *q, QofQueryParamList *param_list,
    1296              :                                     GList *guid_list, QofGuidMatch options,
    1297              :                                     QofQueryOp op)
    1298              : {
    1299              :     QofQueryPredData *pdata;
    1300              : 
    1301         1141 :     if (!q || !param_list) return;
    1302              : 
    1303         1141 :     if (!guid_list)
    1304            0 :         g_return_if_fail (options == QOF_GUID_MATCH_NULL);
    1305              : 
    1306         1141 :     pdata = qof_query_guid_predicate (options, guid_list);
    1307         1141 :     qof_query_add_term (q, param_list, pdata, op);
    1308              : }
    1309              : 
    1310         1141 : void qof_query_add_guid_match (QofQuery *q, QofQueryParamList *param_list,
    1311              :                                const GncGUID *guid, QofQueryOp op)
    1312              : {
    1313         1141 :     GList *g = nullptr;
    1314              : 
    1315         1141 :     if (!q || !param_list) return;
    1316              : 
    1317         1141 :     if (guid)
    1318         1141 :         g = g_list_prepend (g, (gpointer)guid);
    1319              : 
    1320         1141 :     qof_query_add_guid_list_match (q, param_list, g,
    1321              :                                    g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
    1322              : 
    1323         1141 :     g_list_free (g);
    1324              : }
    1325              : 
    1326          379 : void qof_query_set_book (QofQuery *q, QofBook *book)
    1327              : {
    1328          379 :     QofQueryParamList *slist = nullptr;
    1329          379 :     if (!q || !book) return;
    1330              : 
    1331              :     /* Make sure this book is only in the list once */
    1332          373 :     if (g_list_index (q->books, book) == -1)
    1333          373 :         q->books = g_list_prepend (q->books, book);
    1334              : 
    1335          373 :     slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_GUID)));
    1336          373 :     slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_BOOK)));
    1337          373 :     qof_query_add_guid_match (q, slist,
    1338              :                               qof_instance_get_guid(book), QOF_QUERY_AND);
    1339              : }
    1340              : 
    1341            0 : GList * qof_query_get_books (QofQuery *q)
    1342              : {
    1343            0 :     if (!q) return nullptr;
    1344            0 :     return q->books;
    1345              : }
    1346              : 
    1347           97 : void qof_query_add_boolean_match (QofQuery *q, QofQueryParamList *param_list, gboolean value,
    1348              :                                   QofQueryOp op)
    1349              : {
    1350              :     QofQueryPredData *pdata;
    1351           97 :     if (!q || !param_list) return;
    1352              : 
    1353           97 :     pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
    1354           97 :     qof_query_add_term (q, param_list, pdata, op);
    1355              : }
    1356              : 
    1357              : /**********************************************************************/
    1358              : /* PRIVATE PUBLISHED API FUNCTIONS                                    */
    1359              : 
    1360          118 : void qof_query_init (void)
    1361              : {
    1362          118 :     ENTER (" ");
    1363          118 :     qof_query_core_init ();
    1364          118 :     qof_class_init ();
    1365          118 :     LEAVE ("Completed initialization of QofQuery");
    1366          118 : }
    1367              : 
    1368           43 : void qof_query_shutdown (void)
    1369              : {
    1370           43 :     qof_class_shutdown ();
    1371           43 :     qof_query_core_shutdown ();
    1372           43 : }
    1373              : 
    1374         1110 : int qof_query_get_max_results (const QofQuery *q)
    1375              : {
    1376         1110 :     if (!q) return 0;
    1377         1110 :     return q->max_results;
    1378              : }
    1379              : 
    1380         1112 : QofIdType qof_query_get_search_for (const QofQuery *q)
    1381              : {
    1382         1112 :     if (!q) return nullptr;
    1383         1112 :     return q->search_for;
    1384              : }
    1385              : 
    1386         1110 : GList * qof_query_get_terms (const QofQuery *q)
    1387              : {
    1388         1110 :     if (!q) return nullptr;
    1389         1110 :     return q->terms;
    1390              : }
    1391              : 
    1392         1913 : QofQueryParamList * qof_query_term_get_param_path (const QofQueryTerm *qt)
    1393              : {
    1394         1913 :     if (!qt)
    1395            0 :         return nullptr;
    1396         1913 :     return qt->param_list;
    1397              : }
    1398              : 
    1399         1913 : QofQueryPredData *qof_query_term_get_pred_data (const QofQueryTerm *qt)
    1400              : {
    1401         1913 :     if (!qt)
    1402            0 :         return nullptr;
    1403         1913 :     return qt->pdata;
    1404              : }
    1405              : 
    1406         1913 : gboolean qof_query_term_is_inverted (const QofQueryTerm *qt)
    1407              : {
    1408         1913 :     if (!qt)
    1409            0 :         return FALSE;
    1410         1913 :     return qt->invert;
    1411              : }
    1412              : 
    1413         1110 : void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
    1414              :                           QofQuerySort **secondary, QofQuerySort **tertiary)
    1415              : {
    1416         1110 :     if (!q)
    1417            0 :         return;
    1418         1110 :     if (primary)
    1419         1110 :         *primary = &(q->primary_sort);
    1420         1110 :     if (secondary)
    1421         1110 :         *secondary = &(q->secondary_sort);
    1422         1110 :     if (tertiary)
    1423         1110 :         *tertiary = &(q->tertiary_sort);
    1424              : }
    1425              : 
    1426         3330 : QofQueryParamList * qof_query_sort_get_param_path (const QofQuerySort *qs)
    1427              : {
    1428         3330 :     if (!qs)
    1429            0 :         return nullptr;
    1430         3330 :     return qs->param_list;
    1431              : }
    1432              : 
    1433         1961 : gint qof_query_sort_get_sort_options (const QofQuerySort *qs)
    1434              : {
    1435         1961 :     if (!qs)
    1436            0 :         return 0;
    1437         1961 :     return qs->options;
    1438              : }
    1439              : 
    1440         1961 : gboolean qof_query_sort_get_increasing (const QofQuerySort *qs)
    1441              : {
    1442         1961 :     if (!qs)
    1443            0 :         return FALSE;
    1444         1961 :     return qs->increasing;
    1445              : }
    1446              : 
    1447              : static gboolean
    1448         1809 : qof_query_term_equal (const QofQueryTerm *qt1, const QofQueryTerm *qt2)
    1449              : {
    1450         1809 :     if (qt1 == qt2) return TRUE;
    1451         1809 :     if (!qt1 || !qt2) return FALSE;
    1452              : 
    1453         1809 :     if (qt1->invert != qt2->invert) return FALSE;
    1454         1809 :     if (param_list_cmp (qt1->param_list, qt2->param_list)) return FALSE;
    1455         1809 :     return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
    1456              : }
    1457              : 
    1458              : static gboolean
    1459         3156 : qof_query_sort_equal (const QofQuerySort* qs1, const QofQuerySort* qs2)
    1460              : {
    1461         3156 :     if (qs1 == qs2) return TRUE;
    1462         3156 :     if (!qs1 || !qs2) return FALSE;
    1463              : 
    1464              :     /* "Empty" sorts are equivalent, regardless of the flags */
    1465         3156 :     if (!qs1->param_list && !qs2->param_list) return TRUE;
    1466              : 
    1467         1863 :     if (qs1->options != qs2->options) return FALSE;
    1468         1863 :     if (qs1->increasing != qs2->increasing) return FALSE;
    1469         1863 :     return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
    1470              : }
    1471              : 
    1472         1053 : gboolean qof_query_equal (const QofQuery *q1, const QofQuery *q2)
    1473              : {
    1474              :     GList *or1, *or2;
    1475              : 
    1476         1053 :     if (q1 == q2) return TRUE;
    1477         1052 :     if (!q1 || !q2) return FALSE;
    1478              : 
    1479         1052 :     if (q1->max_results != q2->max_results) return FALSE;
    1480              : 
    1481         2332 :     for (or1 = q1->terms, or2 = q2->terms; or1 || or2;
    1482         1280 :          or1 = or1->next, or2 = or2->next)
    1483              :     {
    1484              :         GList *and1, *and2;
    1485              : 
    1486         1280 :         if (!or1 || !or2)
    1487            0 :             return FALSE;
    1488         1280 :         and1 = static_cast<GList*>(or1->data);
    1489         1280 :         and2 = static_cast<GList*>(or2->data);
    1490              : 
    1491         3089 :         for (; and1 || and2; and1 = and1->next, and2 = and2->next)
    1492              :         {
    1493         1809 :             if (!and1 || !and2)
    1494            0 :                 return FALSE;
    1495         1809 :             if (!qof_query_term_equal (static_cast<QofQueryTerm*>(and1->data),
    1496         1809 :                                        static_cast<QofQueryTerm*>(and2->data)))
    1497            0 :                 return FALSE;
    1498              :         }
    1499              :     }
    1500              : 
    1501         1052 :     if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
    1502            0 :         return FALSE;
    1503         1052 :     if (!qof_query_sort_equal (&(q1->secondary_sort), &(q2->secondary_sort)))
    1504            0 :         return FALSE;
    1505         1052 :     if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
    1506            0 :         return FALSE;
    1507              : 
    1508         1052 :     return TRUE;
    1509              : }
    1510              : 
    1511              : /* **************************************************************************/
    1512              : /* Query Print functions for use with qof_log_set_level.
    1513              : */
    1514              : 
    1515              : void
    1516            0 : qof_query_print (QofQuery * query)
    1517              : {
    1518              :     GList *output;
    1519              :     GString *str;
    1520              :     QofQuerySort *s[3];
    1521            0 :     gint maxResults = 0, numSorts = 3;
    1522              : 
    1523            0 :     ENTER (" ");
    1524              : 
    1525            0 :     if (!query)
    1526              :     {
    1527            0 :         LEAVE("query is (null)");
    1528            0 :         return;
    1529              :     }
    1530              : 
    1531            0 :     output = nullptr;
    1532            0 :     str = nullptr;
    1533            0 :     maxResults = qof_query_get_max_results (query);
    1534              : 
    1535            0 :     output = qof_query_printSearchFor (query, output);
    1536            0 :     output = qof_query_printTerms (query, output);
    1537              : 
    1538            0 :     qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
    1539              : 
    1540            0 :     if (s[0])
    1541              :     {
    1542            0 :         output = qof_query_printSorts (s, numSorts, output);
    1543              :     }
    1544              : 
    1545            0 :     str = g_string_new (" ");
    1546            0 :     g_string_printf (str, "Maximum number of results: %d", maxResults);
    1547            0 :     output = g_list_append (output, str);
    1548              : 
    1549            0 :     qof_query_printOutput (output);
    1550            0 :     LEAVE (" ");
    1551              : }
    1552              : 
    1553              : static void
    1554            0 : qof_query_printOutput (GList * output)
    1555              : {
    1556              :     GList *lst;
    1557              : 
    1558            0 :     for (lst = output; lst; lst = lst->next)
    1559              :     {
    1560            0 :         GString *line = (GString *) lst->data;
    1561              : 
    1562            0 :         DEBUG (" %s", line->str);
    1563            0 :         g_string_free (line, TRUE);
    1564            0 :         line = nullptr;
    1565              :     }
    1566            0 : }
    1567              : 
    1568              : /*
    1569              :         Get the search_for type--This is the type of Object
    1570              :         we are searching for (SPLIT, TRANS, etc)
    1571              : */
    1572              : static GList *
    1573            0 : qof_query_printSearchFor (QofQuery * query, GList * output)
    1574              : {
    1575              :     QofIdType searchFor;
    1576              :     GString *gs;
    1577              : 
    1578            0 :     searchFor = qof_query_get_search_for (query);
    1579            0 :     gs = g_string_new ("Query Object Type: ");
    1580            0 :     g_string_append (gs, (nullptr == searchFor) ? "(null)" : searchFor);
    1581            0 :     output = g_list_append (output, gs);
    1582              : 
    1583            0 :     return output;
    1584              : }       /* qof_query_printSearchFor */
    1585              : 
    1586              : /*
    1587              :         Run through the terms of the query.  This is a outer-inner
    1588              :         loop.  The elements of the outer loop are ORed, and the
    1589              :         elements of the inner loop are ANDed.
    1590              : */
    1591              : static GList *
    1592            0 : qof_query_printTerms (QofQuery * query, GList * output)
    1593              : {
    1594              : 
    1595              :     GList *terms, *lst;
    1596              : 
    1597            0 :     terms = qof_query_get_terms (query);
    1598              : 
    1599            0 :     for (lst = terms; lst; lst = lst->next)
    1600              :     {
    1601            0 :         output = g_list_append (output, g_string_new ("OR and AND Terms:"));
    1602              : 
    1603            0 :         if (lst->data)
    1604              :         {
    1605            0 :             output = qof_query_printAndTerms (static_cast<GList*>(lst->data),
    1606              :                                               output);
    1607              :         }
    1608              :         else
    1609              :         {
    1610              :             output =
    1611            0 :                 g_list_append (output, g_string_new ("  No data for AND terms"));
    1612              :         }
    1613              :     }
    1614              : 
    1615            0 :     return output;
    1616              : }       /* qof_query_printTerms */
    1617              : 
    1618              : /*
    1619              :         Process the sort parameters
    1620              :         If this function is called, the assumption is that the first sort
    1621              :         not null.
    1622              : */
    1623              : static GList *
    1624            0 : qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
    1625              : {
    1626            0 :     QofQueryParamList *gsl, *n = nullptr;
    1627              :     gint curSort;
    1628            0 :     GString *gs = g_string_new ("Sort Parameters:   ");
    1629              : 
    1630            0 :     for (curSort = 0; curSort < numSorts; curSort++)
    1631              :     {
    1632              :         gboolean increasing;
    1633            0 :         if (!s[curSort])
    1634              :         {
    1635            0 :             break;
    1636              :         }
    1637            0 :         increasing = qof_query_sort_get_increasing (s[curSort]);
    1638              : 
    1639            0 :         gsl = qof_query_sort_get_param_path (s[curSort]);
    1640            0 :         if (gsl) g_string_append_printf (gs, " Param: ");
    1641            0 :         for (n = gsl; n; n = n->next)
    1642              :         {
    1643            0 :             QofIdType param_name = static_cast<QofIdType>(n->data);
    1644            0 :             if (gsl != n) g_string_append_printf (gs, " ");
    1645            0 :             g_string_append_printf (gs, "%s", param_name);
    1646              :         }
    1647            0 :         if (gsl)
    1648              :         {
    1649            0 :             g_string_append_printf (gs, " %s ", increasing ? "DESC" : "ASC");
    1650            0 :             g_string_append_printf (gs, " Options: 0x%x ", s[curSort]->options);
    1651              :         }
    1652              :     }
    1653              : 
    1654            0 :     output = g_list_append (output, gs);
    1655            0 :     return output;
    1656              : 
    1657              : }      /* qof_query_printSorts */
    1658              : 
    1659              : /*
    1660              :         Process the AND terms of the query.  This is a GList
    1661              :         of WHERE terms that will be ANDed
    1662              : */
    1663              : static GList *
    1664            0 : qof_query_printAndTerms (GList * terms, GList * output)
    1665              : {
    1666            0 :     const char *prefix = "AND Terms:";
    1667              :     QofQueryTerm *qt;
    1668              :     QofQueryPredData *pd;
    1669              :     QofQueryParamList *path;
    1670              :     GList *lst;
    1671              :     gboolean invert;
    1672              : 
    1673            0 :     output = g_list_append (output, g_string_new (prefix));
    1674            0 :     for (lst = terms; lst; lst = lst->next)
    1675              :     {
    1676            0 :         qt = (QofQueryTerm *) lst->data;
    1677            0 :         pd = qof_query_term_get_pred_data (qt);
    1678            0 :         path = qof_query_term_get_param_path (qt);
    1679            0 :         invert = qof_query_term_is_inverted (qt);
    1680              : 
    1681            0 :         if (invert) output = g_list_append (output,
    1682            0 :                                                 g_string_new(" INVERT SENSE "));
    1683            0 :         output = g_list_append (output, qof_query_printParamPath (path));
    1684            0 :         output = qof_query_printPredData (pd, output);
    1685              : //    output = g_list_append (output, g_string_new(" "));
    1686              :     }
    1687              : 
    1688            0 :     return output;
    1689              : }        /* qof_query_printAndTerms */
    1690              : 
    1691              : /*
    1692              :         Process the parameter types of the predicate data
    1693              : */
    1694              : static GString *
    1695            0 : qof_query_printParamPath (QofQueryParamList * parmList)
    1696              : {
    1697            0 :     QofQueryParamList *list = nullptr;
    1698            0 :     GString *gs = g_string_new ("Param List: ");
    1699            0 :     g_string_append (gs, " ");
    1700            0 :     for (list = parmList; list; list = list->next)
    1701              :     {
    1702            0 :         g_string_append (gs, (gchar *) list->data);
    1703            0 :         if (list->next)
    1704            0 :             g_string_append (gs, "->");
    1705              :     }
    1706              : 
    1707            0 :     return gs;
    1708              : }        /* qof_query_printParamPath */
    1709              : 
    1710              : /*
    1711              :         Process the PredData of the AND terms
    1712              : */
    1713              : static GList *
    1714            0 : qof_query_printPredData (QofQueryPredData *pd, GList *lst)
    1715              : {
    1716              :     GString *gs;
    1717              : 
    1718            0 :     gs = g_string_new ("Pred Data: ");
    1719            0 :     g_string_append (gs, (gchar *) pd->type_name);
    1720              : 
    1721              :     /* Char Predicate and GncGUID predicate don't use the 'how' field. */
    1722            0 :     if (g_strcmp0 (pd->type_name, QOF_TYPE_CHAR) &&
    1723            0 :             g_strcmp0 (pd->type_name, QOF_TYPE_GUID))
    1724              :     {
    1725            0 :         g_string_append_printf (gs, " how: %s",
    1726              :                                 qof_query_printStringForHow (pd->how));
    1727              :     }
    1728            0 :     lst = g_list_append(lst, gs);
    1729            0 :     gs = g_string_new ("");
    1730            0 :     qof_query_printValueForParam (pd, gs);
    1731            0 :     lst = g_list_append(lst, gs);
    1732            0 :     return lst;
    1733              : } /* qof_query_printPredData */
    1734              : 
    1735              : /*
    1736              :         Get a string representation for the
    1737              :         QofCompareFunc enum type.
    1738              : */
    1739              : static const char *
    1740            0 : qof_query_printStringForHow (QofQueryCompare how)
    1741              : {
    1742              : 
    1743            0 :     switch (how)
    1744              :     {
    1745            0 :     case QOF_COMPARE_LT:
    1746            0 :         return "QOF_COMPARE_LT";
    1747            0 :     case QOF_COMPARE_LTE:
    1748            0 :         return "QOF_COMPARE_LTE";
    1749            0 :     case QOF_COMPARE_EQUAL:
    1750            0 :         return "QOF_COMPARE_EQUAL";
    1751            0 :     case QOF_COMPARE_GT:
    1752            0 :         return "QOF_COMPARE_GT";
    1753            0 :     case QOF_COMPARE_GTE:
    1754            0 :         return "QOF_COMPARE_GTE";
    1755            0 :     case QOF_COMPARE_NEQ:
    1756            0 :         return "QOF_COMPARE_NEQ";
    1757            0 :     case QOF_COMPARE_CONTAINS:
    1758            0 :         return "QOF_COMPARE_CONTAINS";
    1759            0 :     case QOF_COMPARE_NCONTAINS:
    1760            0 :         return "QOF_COMPARE_NCONTAINS";
    1761              :     }
    1762              : 
    1763            0 :     return "INVALID HOW";
    1764              : }         /* qncQueryPrintStringForHow */
    1765              : 
    1766              : 
    1767              : static void
    1768            0 : qof_query_printValueForParam (QofQueryPredData *pd, GString * gs)
    1769              : {
    1770              : 
    1771            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_GUID))
    1772              :     {
    1773              :         GList *node;
    1774            0 :         query_guid_t pdata = (query_guid_t) pd;
    1775            0 :         g_string_append_printf (gs, "Match type %s",
    1776              :                                 qof_query_printGuidMatch (pdata->options));
    1777            0 :         for (node = pdata->guids; node; node = node->next)
    1778              :         {
    1779              :             gchar guidstr[GUID_ENCODING_LENGTH+1];
    1780            0 :             guid_to_string_buff ((GncGUID *) node->data,guidstr);
    1781            0 :             g_string_append_printf (gs, ", guids: %s",guidstr);
    1782              :         }
    1783            0 :         return;
    1784              :     }
    1785            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_STRING))
    1786              :     {
    1787            0 :         query_string_t pdata = (query_string_t) pd;
    1788            0 :         g_string_append_printf (gs, " Match type %s",
    1789              :                                 qof_query_printStringMatch (pdata->options));
    1790            0 :         g_string_append_printf (gs, " %s string: %s",
    1791            0 :                                 pdata->is_regex ? "Regex" : "Not regex",
    1792              :                                 pdata->matchstring);
    1793            0 :         return;
    1794              :     }
    1795            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_NUMERIC))
    1796              :     {
    1797            0 :         query_numeric_t pdata = (query_numeric_t) pd;
    1798            0 :         g_string_append_printf (gs, " Match type %s",
    1799              :                                 qof_query_printNumericMatch (pdata->options));
    1800            0 :         g_string_append_printf (gs, " gnc_numeric: %s",
    1801              :                                 gnc_num_dbg_to_string (pdata->amount));
    1802            0 :         return;
    1803              :     }
    1804            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_INT64))
    1805              :     {
    1806            0 :         query_int64_t pdata = (query_int64_t) pd;
    1807            0 :         g_string_append_printf (gs, " int64: %" G_GINT64_FORMAT, pdata->val);
    1808            0 :         return;
    1809              :     }
    1810            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_INT32))
    1811              :     {
    1812            0 :         query_int32_t pdata = (query_int32_t) pd;
    1813            0 :         g_string_append_printf (gs, " int32: %d", pdata->val);
    1814            0 :         return;
    1815              :     }
    1816            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_DOUBLE))
    1817              :     {
    1818            0 :         query_double_t pdata = (query_double_t) pd;
    1819            0 :         g_string_append_printf (gs, " double: %.18g", pdata->val);
    1820            0 :         return;
    1821              :     }
    1822            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_DATE))
    1823              :     {
    1824            0 :         query_date_t pdata = (query_date_t) pd;
    1825              :         char datebuff[MAX_DATE_LENGTH + 1];
    1826            0 :         memset (datebuff, 0, sizeof(datebuff));
    1827            0 :         qof_print_date_buff (datebuff, sizeof(datebuff), pdata->date);
    1828            0 :         g_string_append_printf (gs, " Match type %s",
    1829              :                                 qof_query_printDateMatch (pdata->options));
    1830            0 :         g_string_append_printf (gs, " query_date: %s", datebuff);
    1831            0 :         return;
    1832              :     }
    1833            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_CHAR))
    1834              :     {
    1835            0 :         query_char_t pdata = (query_char_t) pd;
    1836            0 :         g_string_append_printf (gs, " Match type %s",
    1837              :                                 qof_query_printCharMatch (pdata->options));
    1838            0 :         g_string_append_printf (gs, " char list: %s", pdata->char_list);
    1839            0 :         return;
    1840              :     }
    1841            0 :     if (!g_strcmp0 (pd->type_name, QOF_TYPE_BOOLEAN))
    1842              :     {
    1843            0 :         query_boolean_t pdata = (query_boolean_t) pd;
    1844            0 :         g_string_append_printf (gs, " boolean: %s", pdata->val ? "TRUE" : "FALSE");
    1845            0 :         return;
    1846              :     }
    1847            0 :     return;
    1848              : }        /* qof_query_printValueForParam */
    1849              : 
    1850              : /*
    1851              :  * Print out a string representation of the
    1852              :  * QofStringMatch enum
    1853              :  */
    1854              : static const char *
    1855            0 : qof_query_printStringMatch (QofStringMatch s)
    1856              : {
    1857            0 :     switch (s)
    1858              :     {
    1859            0 :     case QOF_STRING_MATCH_NORMAL:
    1860            0 :         return "QOF_STRING_MATCH_NORMAL";
    1861            0 :     case QOF_STRING_MATCH_CASEINSENSITIVE:
    1862            0 :         return "QOF_STRING_MATCH_CASEINSENSITIVE";
    1863              :     }
    1864            0 :     return "UNKNOWN MATCH TYPE";
    1865              : }           /* qof_query_printStringMatch */
    1866              : 
    1867              : /*
    1868              :  * Print out a string representation of the
    1869              :  * QofDateMatch enum
    1870              :  */
    1871              : static const char *
    1872            0 : qof_query_printDateMatch (QofDateMatch d)
    1873              : {
    1874            0 :     switch (d)
    1875              :     {
    1876            0 :     case QOF_DATE_MATCH_NORMAL:
    1877            0 :         return "QOF_DATE_MATCH_NORMAL";
    1878            0 :     case QOF_DATE_MATCH_DAY:
    1879            0 :         return "QOF_DATE_MATCH_DAY";
    1880              :     }
    1881            0 :     return "UNKNOWN MATCH TYPE";
    1882              : }            /* qof_query_printDateMatch */
    1883              : 
    1884              : /*
    1885              :  * Print out a string representation of the
    1886              :  * QofNumericMatch enum
    1887              :  */
    1888              : static const char *
    1889            0 : qof_query_printNumericMatch (QofNumericMatch n)
    1890              : {
    1891            0 :     switch (n)
    1892              :     {
    1893            0 :     case QOF_NUMERIC_MATCH_DEBIT:
    1894            0 :         return "QOF_NUMERIC_MATCH_DEBIT";
    1895            0 :     case QOF_NUMERIC_MATCH_CREDIT:
    1896            0 :         return "QOF_NUMERIC_MATCH_CREDIT";
    1897            0 :     case QOF_NUMERIC_MATCH_ANY:
    1898            0 :         return "QOF_NUMERIC_MATCH_ANY";
    1899              :     }
    1900            0 :     return "UNKNOWN MATCH TYPE";
    1901              : }        /* qof_query_printNumericMatch */
    1902              : 
    1903              : /*
    1904              :  * Print out a string representation of the
    1905              :  * QofGuidMatch enum
    1906              :  */
    1907              : static const char *
    1908            0 : qof_query_printGuidMatch (QofGuidMatch g)
    1909              : {
    1910            0 :     switch (g)
    1911              :     {
    1912            0 :     case QOF_GUID_MATCH_ANY:
    1913            0 :         return "QOF_GUID_MATCH_ANY";
    1914            0 :     case QOF_GUID_MATCH_ALL:
    1915            0 :         return "QOF_GUID_MATCH_ALL";
    1916            0 :     case QOF_GUID_MATCH_NONE:
    1917            0 :         return "QOF_GUID_MATCH_NONE";
    1918            0 :     case QOF_GUID_MATCH_NULL:
    1919            0 :         return "QOF_GUID_MATCH_NULL";
    1920            0 :     case QOF_GUID_MATCH_LIST_ANY:
    1921            0 :         return "QOF_GUID_MATCH_LIST_ANY";
    1922              :     }
    1923              : 
    1924            0 :     return "UNKNOWN MATCH TYPE";
    1925              : }         /* qof_query_printGuidMatch */
    1926              : 
    1927              : /*
    1928              :  * Print out a string representation of the
    1929              :  * QofCharMatch enum
    1930              :  */
    1931              : static const char *
    1932            0 : qof_query_printCharMatch (QofCharMatch c)
    1933              : {
    1934            0 :     switch (c)
    1935              :     {
    1936            0 :     case QOF_CHAR_MATCH_ANY:
    1937            0 :         return "QOF_CHAR_MATCH_ANY";
    1938            0 :     case QOF_CHAR_MATCH_NONE:
    1939            0 :         return "QOF_CHAR_MATCH_NONE";
    1940              :     }
    1941            0 :     return "UNKNOWN MATCH TYPE";
    1942              : }         /* qof_query_printGuidMatch */
    1943              : 
    1944              : /* ======================== END OF FILE =================== */
        

Generated by: LCOV version 2.0-1