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: 2025-03-30 14:51:15 Functions: 65.4 % 81 53
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     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.h"
      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                 :       61415 : static void query_init (QofQuery *q, QofQueryTerm *initial_term)
     134                 :             : {
     135                 :       61415 :     GList * _or_ = nullptr;
     136                 :       61415 :     GList *_and_ = nullptr;
     137                 :             :     GHashTable *ht;
     138                 :             : 
     139                 :       61415 :     if (initial_term)
     140                 :             :     {
     141                 :       15866 :         _or_ = g_list_alloc ();
     142                 :       15866 :         _and_ = g_list_alloc ();
     143                 :       15866 :         _and_->data = initial_term;
     144                 :       15866 :         _or_->data = _and_;
     145                 :             :     }
     146                 :             : 
     147                 :       61415 :     if (q->terms)
     148                 :           0 :         qof_query_clear (q);
     149                 :             : 
     150                 :       61415 :     g_list_free (q->results);
     151                 :       61415 :     g_list_free (q->books);
     152                 :             : 
     153                 :       61415 :     g_slist_free (q->primary_sort.param_list);
     154                 :       61415 :     g_slist_free (q->secondary_sort.param_list);
     155                 :       61415 :     g_slist_free (q->tertiary_sort.param_list);
     156                 :             : 
     157                 :       61415 :     g_slist_free (q->primary_sort.param_fcns);
     158                 :       61415 :     g_slist_free (q->secondary_sort.param_fcns);
     159                 :       61415 :     g_slist_free (q->tertiary_sort.param_fcns);
     160                 :             : 
     161                 :       61415 :     ht = q->be_compiled;
     162                 :       61415 :     memset (q, 0, sizeof (*q));
     163                 :       61415 :     q->be_compiled = ht;
     164                 :             : 
     165                 :       61415 :     q->terms = _or_;
     166                 :       61415 :     q->changed = 1;
     167                 :       61415 :     q->max_results = -1;
     168                 :             : 
     169                 :       61415 :     q->primary_sort.param_list = g_slist_prepend (static_cast<GSList*>(nullptr),
     170                 :             :                                                   static_cast<void*>(const_cast<char*>(QUERY_DEFAULT_SORT)));
     171                 :       61415 :     q->primary_sort.increasing = TRUE;
     172                 :       61415 :     q->secondary_sort.increasing = TRUE;
     173                 :       61415 :     q->tertiary_sort.increasing = TRUE;
     174                 :       61415 : }
     175                 :             : 
     176                 :       18198 : static void swap_terms (QofQuery *q1, QofQuery *q2)
     177                 :             : {
     178                 :             :     GList *g;
     179                 :             : 
     180                 :       18198 :     if (!q1 || !q2) return;
     181                 :             : 
     182                 :       18198 :     g = q1->terms;
     183                 :       18198 :     q1->terms = q2->terms;
     184                 :       18198 :     q2->terms = g;
     185                 :             : 
     186                 :       18198 :     g = q1->books;
     187                 :       18198 :     q1->books = q2->books;
     188                 :       18198 :     q2->books = g;
     189                 :             : 
     190                 :       18198 :     q1->changed = 1;
     191                 :       18198 :     q2->changed = 1;
     192                 :             : }
     193                 :             : 
     194                 :       84521 : static void free_query_term (QofQueryTerm *qt)
     195                 :             : {
     196                 :       84521 :     if (!qt) return;
     197                 :             : 
     198                 :       84521 :     qof_query_core_predicate_free (qt->pdata);
     199                 :       84521 :     g_slist_free (qt->param_list);
     200                 :       84521 :     g_slist_free (qt->param_fcns);
     201                 :       84521 :     g_free (qt);
     202                 :             : }
     203                 :             : 
     204                 :       68671 : static QofQueryTerm * copy_query_term (const QofQueryTerm *qt)
     205                 :             : {
     206                 :             :     QofQueryTerm *new_qt;
     207                 :       68671 :     if (!qt) return nullptr;
     208                 :             : 
     209                 :       68671 :     new_qt = g_new0 (QofQueryTerm, 1);
     210                 :       68671 :     *new_qt = *qt;
     211                 :       68671 :     new_qt->param_list = g_slist_copy (qt->param_list);
     212                 :       68671 :     new_qt->param_fcns = g_slist_copy (qt->param_fcns);
     213                 :       68671 :     new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
     214                 :       68671 :     return new_qt;
     215                 :             : }
     216                 :             : 
     217                 :       32917 : static GList * copy_and_terms (const GList *and_terms)
     218                 :             : {
     219                 :       32917 :     GList *_and_ = nullptr;
     220                 :             :     const GList *cur_and;
     221                 :             : 
     222                 :      100729 :     for (cur_and = and_terms; cur_and; cur_and = cur_and->next)
     223                 :             :     {
     224                 :       67812 :         _and_ = g_list_prepend(_and_, copy_query_term (static_cast<QofQueryTerm*>(cur_and->data)));
     225                 :             :     }
     226                 :             : 
     227                 :       32917 :     return g_list_reverse(_and_);
     228                 :             : }
     229                 :             : 
     230                 :             : static GList *
     231                 :       18016 : copy_or_terms(const GList * or_terms)
     232                 :             : {
     233                 :       18016 :     GList * _or_ = nullptr;
     234                 :             :     const GList * cur_or;
     235                 :             : 
     236                 :       28005 :     for (cur_or = or_terms; cur_or; cur_or = cur_or->next)
     237                 :             :     {
     238                 :        9989 :         _or_ = g_list_prepend(_or_, copy_and_terms(static_cast<GList*>(cur_or->data)));
     239                 :             :     }
     240                 :             : 
     241                 :       18016 :     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                 :      136152 : static void free_sort (QofQuerySort *s)
     252                 :             : {
     253                 :      136152 :     g_slist_free (s->param_list);
     254                 :      136152 :     s->param_list = nullptr;
     255                 :             : 
     256                 :      136152 :     g_slist_free (s->param_fcns);
     257                 :      136152 :     s->param_fcns = nullptr;
     258                 :      136152 : }
     259                 :             : 
     260                 :       45384 : static void free_members (QofQuery *q)
     261                 :             : {
     262                 :             :     GList * cur_or;
     263                 :             : 
     264                 :       45384 :     if (q == nullptr) return;
     265                 :             : 
     266                 :       83563 :     for (cur_or = q->terms; cur_or; cur_or = cur_or->next)
     267                 :             :     {
     268                 :             :         GList * cur_and;
     269                 :             : 
     270                 :      122700 :         for (cur_and = static_cast<GList*>(cur_or->data); cur_and;
     271                 :       84521 :              cur_and = static_cast<GList*>(cur_and->next))
     272                 :             :         {
     273                 :       84521 :             free_query_term(static_cast<QofQueryTerm*>(cur_and->data));
     274                 :       84521 :             cur_and->data = nullptr;
     275                 :             :         }
     276                 :             : 
     277                 :       38179 :         g_list_free(static_cast<GList*>(cur_or->data));
     278                 :       38179 :         cur_or->data = nullptr;
     279                 :             :     }
     280                 :             : 
     281                 :       45384 :     free_sort (&(q->primary_sort));
     282                 :       45384 :     free_sort (&(q->secondary_sort));
     283                 :       45384 :     free_sort (&(q->tertiary_sort));
     284                 :             : 
     285                 :       45384 :     g_list_free(q->terms);
     286                 :       45384 :     q->terms = nullptr;
     287                 :             : 
     288                 :       45384 :     g_list_free(q->books);
     289                 :       45384 :     q->books = nullptr;
     290                 :             : 
     291                 :       45384 :     g_list_free(q->results);
     292                 :       45384 :     q->results = nullptr;
     293                 :             : }
     294                 :             : 
     295                 :       41445 : static int cmp_func (const QofQuerySort *sort, QofSortFunc default_sort,
     296                 :             :                      const gconstpointer a, const gconstpointer b)
     297                 :             : {
     298                 :       41445 :     QofParam *param = nullptr;
     299                 :             :     GSList *node;
     300                 :             :     gpointer conva, convb;
     301                 :             : 
     302                 :       41445 :     g_return_val_if_fail (sort, 0);
     303                 :             : 
     304                 :             :     /* See if this is a default sort */
     305                 :       41445 :     if (sort->use_default)
     306                 :             :     {
     307                 :       36157 :         if (default_sort) return default_sort (a, b);
     308                 :           0 :         return 0;
     309                 :             :     }
     310                 :             : 
     311                 :             :     /* If no parameters, consider them equal */
     312                 :        5288 :     if (!sort->param_fcns) return 0;
     313                 :             : 
     314                 :             :     /* no compare function, consider the two objects equal */
     315                 :        5265 :     if (!sort->comp_fcn && !sort->obj_cmp) return 0;
     316                 :             : 
     317                 :             :     /* Do the list of conversions */
     318                 :        5265 :     conva = (gpointer)a;
     319                 :        5265 :     convb = (gpointer)b;
     320                 :       10044 :     for (node = static_cast<GSList*>(sort->param_fcns); node;
     321                 :        4779 :          node = static_cast<GSList*>(node->next))
     322                 :             :     {
     323                 :        8529 :         param = static_cast<QofParam*>(node->data);
     324                 :             : 
     325                 :             :         /* The last term is really the "parameter getter",
     326                 :             :          * unless we're comparing objects ;) */
     327                 :        8529 :         if (!node->next && !sort->obj_cmp)
     328                 :        3750 :             break;
     329                 :             : 
     330                 :             :         /* Do the conversions */
     331                 :        4779 :         conva = (param->param_getfcn) (conva, param);
     332                 :        4779 :         convb = (param->param_getfcn) (convb, param);
     333                 :             :     }
     334                 :             : 
     335                 :             :     /* And now return the (appropriate) compare */
     336                 :        5265 :     if (sort->comp_fcn)
     337                 :             :     {
     338                 :        3750 :         int rc = sort->comp_fcn (conva, convb, sort->options, param);
     339                 :        3750 :         return rc;
     340                 :             :     }
     341                 :             : 
     342                 :        1515 :     return sort->obj_cmp (conva, convb);
     343                 :             : }
     344                 :             : 
     345                 :             : static int
     346                 :       39470 : sort_func (const gconstpointer a, const gconstpointer b, const gpointer q)
     347                 :             : {
     348                 :             :     int retval;
     349                 :       39470 :     const QofQuery *sortQuery = static_cast<const QofQuery*>(q);
     350                 :             : 
     351                 :       39470 :     g_return_val_if_fail (sortQuery, 0);
     352                 :             : 
     353                 :       39470 :     retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b);
     354                 :       39470 :     if (retval == 0)
     355                 :             :     {
     356                 :        1819 :         retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort,
     357                 :             :                            a, b);
     358                 :        1819 :         if (retval == 0)
     359                 :             :         {
     360                 :         156 :             retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort,
     361                 :             :                                a, b);
     362                 :         156 :             return sortQuery->tertiary_sort.increasing ? retval : -retval;
     363                 :             :         }
     364                 :             :         else
     365                 :             :         {
     366                 :        1663 :             return sortQuery->secondary_sort.increasing ? retval : -retval;
     367                 :             :         }
     368                 :             :     }
     369                 :             :     else
     370                 :             :     {
     371                 :       37651 :         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                 :      259822 : check_object (const QofQuery *q, gpointer object)
     383                 :             : {
     384                 :             :     const GList     * and_ptr;
     385                 :             :     const GList     * or_ptr;
     386                 :             :     const QofQueryTerm * qt;
     387                 :      259822 :     int       and_terms_ok = 1;
     388                 :             : 
     389                 :      525638 :     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
     390                 :             :     {
     391                 :      275221 :         and_terms_ok = 1;
     392                 :      926401 :         for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
     393                 :      651180 :              and_ptr = static_cast<GList*>(and_ptr->next))
     394                 :             :         {
     395                 :      916996 :             qt = (QofQueryTerm *)(and_ptr->data);
     396                 :      916996 :             if (qt->param_fcns && qt->pred_fcn)
     397                 :             :             {
     398                 :             :                 const GSList *node;
     399                 :      916996 :                 QofParam *param = nullptr;
     400                 :      916996 :                 gpointer conv_obj = object;
     401                 :             : 
     402                 :             :                 /* iterate through the conversions */
     403                 :     1623785 :                 for (node = qt->param_fcns; node; node = node->next)
     404                 :             :                 {
     405                 :     1623785 :                     param = static_cast<QofParam*>(node->data);
     406                 :             : 
     407                 :             :                     /* The last term is the actual parameter getter */
     408                 :     1623785 :                     if (!node->next) break;
     409                 :             : 
     410                 :      706789 :                     conv_obj = (void*)param->param_getfcn (conv_obj, param);
     411                 :             :                 }
     412                 :             : 
     413                 :      916996 :                 if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert)
     414                 :             :                 {
     415                 :      265816 :                     and_terms_ok = 0;
     416                 :      265816 :                     break;
     417                 :             :                 }
     418                 :             :             }
     419                 :             :             else
     420                 :             :             {
     421                 :             :                 /* XXX: Don't know how to do this conversion -- do we care? */
     422                 :             :             }
     423                 :             :         }
     424                 :      275221 :         if (and_terms_ok)
     425                 :             :         {
     426                 :        9405 :             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                 :      250417 :     if (nullptr == q->terms) return 1;
     436                 :      250417 :     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                 :       15238 : compile_params (QofQueryParamList *param_list, QofIdType start_obj,
     447                 :             :                 QofParam const **final)
     448                 :             : {
     449                 :       15238 :     const QofParam *objDef = nullptr;
     450                 :       15238 :     GSList *fcns = nullptr;
     451                 :             : 
     452                 :       15238 :     ENTER ("param_list=%p id=%s", param_list, start_obj);
     453                 :       15238 :     g_return_val_if_fail (param_list, nullptr);
     454                 :       15238 :     g_return_val_if_fail (start_obj, nullptr);
     455                 :       15238 :     g_return_val_if_fail (final, nullptr);
     456                 :             : 
     457                 :       38686 :     for (; param_list; param_list = param_list->next)
     458                 :             :     {
     459                 :       25569 :         QofIdType param_name = static_cast<QofIdType>(param_list->data);
     460                 :       25569 :         objDef = qof_class_get_parameter (start_obj, param_name);
     461                 :             : 
     462                 :             :         /* If it doesn't exist, then we've reached the end */
     463                 :       25569 :         if (!objDef) break;
     464                 :             : 
     465                 :             :         /* Save off this parameter */
     466                 :       23448 :         fcns = g_slist_prepend (fcns, (gpointer) objDef);
     467                 :             : 
     468                 :             :         /* Save this off, just in case */
     469                 :       23448 :         *final = objDef;
     470                 :             : 
     471                 :             :         /* And reset for the next parameter */
     472                 :       23448 :         start_obj = (QofIdType) objDef->param_type;
     473                 :             :     }
     474                 :             : 
     475                 :       15238 :     LEAVE ("fcns=%p", fcns);
     476                 :       15238 :     return (g_slist_reverse (fcns));
     477                 :             : }
     478                 :             : 
     479                 :             : static void
     480                 :        6285 : compile_sort (QofQuerySort *sort, QofIdType obj)
     481                 :             : {
     482                 :        6285 :     const QofParam *resObj = nullptr;
     483                 :             : 
     484                 :        6285 :     ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
     485                 :        6285 :     sort->use_default = FALSE;
     486                 :             : 
     487                 :        6285 :     g_slist_free (sort->param_fcns);
     488                 :        6285 :     sort->param_fcns = nullptr;
     489                 :        6285 :     sort->comp_fcn = nullptr;
     490                 :        6285 :     sort->obj_cmp = nullptr;
     491                 :             : 
     492                 :             :     /* An empty param_list implies "no sort" */
     493                 :        6285 :     if (!sort->param_list)
     494                 :             :     {
     495                 :        4053 :         LEAVE (" ");
     496                 :        4053 :         return;
     497                 :             :     }
     498                 :             : 
     499                 :             :     /* Walk the parameter list of obtain the parameter functions */
     500                 :        2232 :     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                 :        2232 :     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                 :         111 :         if (resObj->param_compfcn)
     511                 :           5 :             sort->comp_fcn = resObj->param_compfcn;
     512                 :             :         else
     513                 :         106 :             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                 :         111 :         if (sort->comp_fcn == nullptr)
     517                 :          44 :             sort->obj_cmp = qof_class_get_default_sort (resObj->param_type);
     518                 :             :     }
     519                 :        2121 :     else if (!g_strcmp0 (static_cast<char*>(sort->param_list->data),
     520                 :             :                          QUERY_DEFAULT_SORT))
     521                 :             :     {
     522                 :        2121 :         sort->use_default = TRUE;
     523                 :             :     }
     524                 :        2232 :     LEAVE ("sort=%p id=%s", sort, obj);
     525                 :             : }
     526                 :             : 
     527                 :        2095 : static void compile_terms (QofQuery *q)
     528                 :             : {
     529                 :             :     GList *or_ptr, *and_ptr;
     530                 :             : 
     531                 :        2095 :     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                 :        4309 :     for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
     536                 :             :     {
     537                 :       15220 :         for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
     538                 :       13006 :              and_ptr = static_cast<GList*>(and_ptr->next))
     539                 :             :         {
     540                 :       13006 :             QofQueryTerm* qt = static_cast<QofQueryTerm*>(and_ptr->data);
     541                 :       13006 :             const QofParam* resObj = nullptr;
     542                 :             : 
     543                 :       13006 :             g_slist_free (qt->param_fcns);
     544                 :       13006 :             qt->param_fcns = nullptr;
     545                 :             : 
     546                 :             :             /* Walk the parameter list of obtain the parameter functions */
     547                 :       13006 :             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                 :       13006 :             if (qt->param_fcns && resObj)
     555                 :       13006 :                 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                 :        2095 :     compile_sort (&(q->primary_sort), q->search_for);
     563                 :        2095 :     compile_sort (&(q->secondary_sort), q->search_for);
     564                 :        2095 :     compile_sort (&(q->tertiary_sort), q->search_for);
     565                 :             : 
     566                 :        2095 :     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                 :        2095 :     LEAVE (" query=%p", q);
     584                 :        2095 : }
     585                 :             : 
     586                 :      259822 : static void check_item_cb (gpointer object, gpointer user_data)
     587                 :             : {
     588                 :      259822 :     QofQueryCB* ql = static_cast<QofQueryCB*>(user_data);
     589                 :             : 
     590                 :      259822 :     if (!object || !ql) return;
     591                 :             : 
     592                 :      259822 :     if (check_object (ql->query, object))
     593                 :             :     {
     594                 :        9405 :         ql->list = g_list_prepend (ql->list, object);
     595                 :        9405 :         ql->count++;
     596                 :             :     }
     597                 :      259822 :     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                 :       20205 : static GList * merge_books (GList *l1, GList *l2)
     624                 :             : {
     625                 :       20205 :     GList *res = nullptr;
     626                 :             :     GList *node;
     627                 :             : 
     628                 :       20205 :     res = g_list_copy (l1);
     629                 :             : 
     630                 :       20205 :     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                 :       20205 :     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                 :       47479 : static void query_clear_compiles (QofQuery *q)
     654                 :             : {
     655                 :       47479 :     g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, nullptr);
     656                 :       47479 : }
     657                 :             : 
     658                 :             : /********************************************************************/
     659                 :             : /* PUBLISHED API FUNCTIONS */
     660                 :             : 
     661                 :             : QofQueryParamList *
     662                 :        9497 : qof_query_build_param_list (char const *param, ...)
     663                 :             : {
     664                 :        9497 :     QofQueryParamList *param_list = nullptr;
     665                 :             :     char const *this_param;
     666                 :             :     va_list ap;
     667                 :             : 
     668                 :        9497 :     if (!param)
     669                 :           0 :         return nullptr;
     670                 :             : 
     671                 :        9497 :     va_start (ap, param);
     672                 :             : 
     673                 :       26607 :     for (this_param = param; this_param; this_param = va_arg (ap, const char *))
     674                 :       17110 :         param_list = g_slist_prepend (param_list, (gpointer)this_param);
     675                 :             : 
     676                 :        9497 :     va_end (ap);
     677                 :             : 
     678                 :        9497 :     return g_slist_reverse (param_list);
     679                 :             : }
     680                 :             : 
     681                 :       15866 : 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                 :       15866 :     if (!q || !param_list || !pred_data) return;
     688                 :             : 
     689                 :       15866 :     qt = g_new0 (QofQueryTerm, 1);
     690                 :       15866 :     qt->param_list = param_list;
     691                 :       15866 :     qt->pdata = pred_data;
     692                 :       15866 :     qs = qof_query_create ();
     693                 :       15866 :     query_init (qs, qt);
     694                 :             : 
     695                 :       15866 :     if (q->terms != nullptr)
     696                 :        8728 :         qr = qof_query_merge (q, qs, op);
     697                 :             :     else
     698                 :        7138 :         qr = qof_query_merge (q, qs, QOF_QUERY_OR);
     699                 :             : 
     700                 :       15866 :     swap_terms (q, qr);
     701                 :       15866 :     qof_query_destroy (qs);
     702                 :       15866 :     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                 :        2101 : static GList * qof_query_run_internal (QofQuery *q,
     744                 :             :                                        void(*run_cb)(QofQueryCB*, gpointer),
     745                 :             :                                        gpointer cb_arg)
     746                 :             : {
     747                 :        2101 :     GList *matching_objects = nullptr;
     748                 :        2101 :     int        object_count = 0;
     749                 :             : 
     750                 :        2101 :     if (!q) return nullptr;
     751                 :        2095 :     g_return_val_if_fail (q->search_for, nullptr);
     752                 :        2095 :     g_return_val_if_fail (q->books, nullptr);
     753                 :        2095 :     g_return_val_if_fail (run_cb, nullptr);
     754                 :        2095 :     ENTER (" q=%p", q);
     755                 :             : 
     756                 :             :     /* XXX: Prioritize the query terms? */
     757                 :             : 
     758                 :             :     /* prepare the Query for processing */
     759                 :        2095 :     if (q->changed)
     760                 :             :     {
     761                 :        2095 :         query_clear_compiles (q);
     762                 :        2095 :         compile_terms (q);
     763                 :             :     }
     764                 :             : 
     765                 :             :     /* Maybe log this sucker */
     766                 :        2095 :     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                 :        2095 :         memset (&qcb, 0, sizeof (qcb));
     774                 :        2095 :         qcb.query = q;
     775                 :             : 
     776                 :             :         /* Run the query callback */
     777                 :        2095 :         run_cb(&qcb, cb_arg);
     778                 :             : 
     779                 :        2095 :         matching_objects = qcb.list;
     780                 :        2095 :         object_count = qcb.count;
     781                 :             :     }
     782                 :        2095 :     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                 :        2095 :     matching_objects = g_list_reverse(matching_objects);
     791                 :             : 
     792                 :             :     /* Now sort the matching objects based on the search criteria */
     793                 :        2095 :     if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
     794                 :        1996 :             (q->primary_sort.use_default && q->defaultSort))
     795                 :             :     {
     796                 :        2094 :         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                 :        2095 :     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                 :        2095 :     q->changed = 0;
     826                 :             : 
     827                 :        2095 :     g_list_free(q->results);
     828                 :        2095 :     q->results = matching_objects;
     829                 :             : 
     830                 :        2095 :     LEAVE (" q=%p", q);
     831                 :        2095 :     return matching_objects;
     832                 :             : }
     833                 :             : 
     834                 :        2095 : static void qof_query_run_cb(QofQueryCB* qcb, gpointer cb_arg)
     835                 :             : {
     836                 :             :     GList *node;
     837                 :             : 
     838                 :             :     (void)cb_arg; /* unused */
     839                 :        2095 :     g_return_if_fail(qcb);
     840                 :             : 
     841                 :        4190 :     for (node = qcb->query->books; node; node = node->next)
     842                 :             :     {
     843                 :        2095 :         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                 :        2095 :         qof_object_foreach (qcb->query->search_for, book,
     861                 :             :                             (QofInstanceForeachCB) check_item_cb, qcb);
     862                 :             :     }
     863                 :             : }
     864                 :             : 
     865                 :        2101 : GList * qof_query_run (QofQuery *q)
     866                 :             : {
     867                 :             :     /* Just a wrapper */
     868                 :        2101 :     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                 :       45549 : QofQuery * qof_query_create (void)
     919                 :             : {
     920                 :       45549 :     QofQuery *qp = g_new0 (QofQuery, 1);
     921                 :       45549 :     qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
     922                 :       45549 :     query_init (qp, nullptr);
     923                 :       45549 :     return qp;
     924                 :             : }
     925                 :             : 
     926                 :        5603 : void qof_query_search_for (QofQuery *q, QofIdTypeConst obj_type)
     927                 :             : {
     928                 :        5603 :     if (!q || !obj_type)
     929                 :           0 :         return;
     930                 :             : 
     931                 :        5603 :     if (g_strcmp0 (q->search_for, obj_type))
     932                 :             :     {
     933                 :        4535 :         q->search_for = (QofIdType) obj_type;
     934                 :        4535 :         q->changed = 1;
     935                 :             :     }
     936                 :             : }
     937                 :             : 
     938                 :        4534 : QofQuery * qof_query_create_for (QofIdTypeConst obj_type)
     939                 :             : {
     940                 :             :     QofQuery *q;
     941                 :        4534 :     if (!obj_type)
     942                 :           0 :         return nullptr;
     943                 :        4534 :     q = qof_query_create ();
     944                 :        4534 :     qof_query_search_for (q, obj_type);
     945                 :        4534 :     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                 :       45391 : void qof_query_destroy (QofQuery *q)
    1010                 :             : {
    1011                 :       45391 :     if (!q) return;
    1012                 :       45384 :     free_members (q);
    1013                 :       45384 :     query_clear_compiles (q);
    1014                 :       45384 :     g_hash_table_destroy (q->be_compiled);
    1015                 :       45384 :     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                 :       20409 : qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
    1130                 :             : {
    1131                 :             : 
    1132                 :       20409 :     QofQuery * retval = nullptr;
    1133                 :             :     QofQuery * i1, * i2;
    1134                 :             :     QofQuery * t1, * t2;
    1135                 :             :     GList * i, * j;
    1136                 :             :     QofIdType search_for;
    1137                 :             : 
    1138                 :       20409 :     if (!q1) return q2;
    1139                 :       20409 :     if (!q2) return q1;
    1140                 :             : 
    1141                 :       20409 :     if (q1->search_for && q2->search_for)
    1142                 :         183 :         g_return_val_if_fail (g_strcmp0 (q1->search_for, q2->search_for) == 0,
    1143                 :             :                               nullptr);
    1144                 :             : 
    1145                 :       20409 :     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                 :       20409 :     if ((QOF_QUERY_AND == op) &&
    1157                 :       11277 :         (q1->terms == nullptr || q2->terms == nullptr))
    1158                 :             :     {
    1159                 :          71 :         op = QOF_QUERY_OR;
    1160                 :             :     }
    1161                 :             : 
    1162                 :       20409 :     switch (op)
    1163                 :             :     {
    1164                 :        8999 :     case QOF_QUERY_OR:
    1165                 :        8999 :         retval = qof_query_create();
    1166                 :        8999 :         retval->terms =
    1167                 :        8999 :             g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
    1168                 :        8999 :         retval->books           = merge_books (q1->books, q2->books);
    1169                 :        8999 :         retval->max_results    = q1->max_results;
    1170                 :        8999 :         retval->changed        = 1;
    1171                 :        8999 :         break;
    1172                 :             : 
    1173                 :       11206 :     case QOF_QUERY_AND:
    1174                 :       11206 :         retval = qof_query_create();
    1175                 :       11206 :         retval->books          = merge_books (q1->books, q2->books);
    1176                 :       11206 :         retval->max_results    = q1->max_results;
    1177                 :       11206 :         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                 :       22533 :         for (i = q1->terms; i; i = i->next)
    1184                 :             :         {
    1185                 :       22782 :             for (j = q2->terms; j; j = j->next)
    1186                 :             :             {
    1187                 :       11455 :                 retval->terms =
    1188                 :       11455 :                     g_list_prepend(retval->terms,
    1189                 :             :                                    g_list_concat
    1190                 :       11455 :                                    (copy_and_terms(static_cast<GList*>(i->data)),
    1191                 :       11455 :                                     copy_and_terms(static_cast<GList*>(j->data))));
    1192                 :             :             }
    1193                 :             :         }
    1194                 :       11206 :         retval->terms = g_list_reverse(retval->terms);
    1195                 :       11206 :         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                 :       20409 :     if (retval)
    1231                 :       20409 :         retval->search_for = search_for;
    1232                 :       20409 :     return retval;
    1233                 :             : }
    1234                 :             : 
    1235                 :             : void
    1236                 :        2332 : qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
    1237                 :             : {
    1238                 :             :     QofQuery *tmp_q;
    1239                 :             : 
    1240                 :        2332 :     if (!q1 || !q2)
    1241                 :           0 :         return;
    1242                 :             : 
    1243                 :        2332 :     tmp_q = qof_query_merge (q1, q2, op);
    1244                 :        2332 :     swap_terms (q1, tmp_q);
    1245                 :        2332 :     qof_query_destroy (tmp_q);
    1246                 :             : }
    1247                 :             : 
    1248                 :             : void
    1249                 :        2266 : qof_query_set_sort_order (QofQuery *q,
    1250                 :             :                           QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
    1251                 :             : {
    1252                 :        2266 :     if (!q) return;
    1253                 :        2266 :     if (q->primary_sort.param_list)
    1254                 :        2266 :         g_slist_free (q->primary_sort.param_list);
    1255                 :        2266 :     q->primary_sort.param_list = params1;
    1256                 :        2266 :     q->primary_sort.options = 0;
    1257                 :             : 
    1258                 :        2266 :     if (q->secondary_sort.param_list)
    1259                 :           0 :         g_slist_free (q->secondary_sort.param_list);
    1260                 :        2266 :     q->secondary_sort.param_list = params2;
    1261                 :        2266 :     q->secondary_sort.options = 0;
    1262                 :             : 
    1263                 :        2266 :     if (q->tertiary_sort.param_list)
    1264                 :           0 :         g_slist_free (q->tertiary_sort.param_list);
    1265                 :        2266 :     q->tertiary_sort.param_list = params3;
    1266                 :        2266 :     q->tertiary_sort.options = 0;
    1267                 :             : 
    1268                 :        2266 :     q->changed = 1;
    1269                 :             : }
    1270                 :             : 
    1271                 :        1067 : void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
    1272                 :             :                                  gint tert_op)
    1273                 :             : {
    1274                 :        1067 :     if (!q) return;
    1275                 :        1067 :     q->primary_sort.options = prim_op;
    1276                 :        1067 :     q->secondary_sort.options = sec_op;
    1277                 :        1067 :     q->tertiary_sort.options = tert_op;
    1278                 :             : }
    1279                 :             : 
    1280                 :        2239 : void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
    1281                 :             :                                     gboolean sec_inc, gboolean tert_inc)
    1282                 :             : {
    1283                 :        2239 :     if (!q) return;
    1284                 :        2239 :     q->primary_sort.increasing = prim_inc;
    1285                 :        2239 :     q->secondary_sort.increasing = sec_inc;
    1286                 :        2239 :     q->tertiary_sort.increasing = tert_inc;
    1287                 :             : }
    1288                 :             : 
    1289                 :        2168 : void qof_query_set_max_results (QofQuery *q, int n)
    1290                 :             : {
    1291                 :        2168 :     if (!q) return;
    1292                 :        2168 :     q->max_results = n;
    1293                 :             : }
    1294                 :             : 
    1295                 :        2879 : 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                 :        2879 :     if (!q || !param_list) return;
    1302                 :             : 
    1303                 :        2879 :     if (!guid_list)
    1304                 :           0 :         g_return_if_fail (options == QOF_GUID_MATCH_NULL);
    1305                 :             : 
    1306                 :        2879 :     pdata = qof_query_guid_predicate (options, guid_list);
    1307                 :        2879 :     qof_query_add_term (q, param_list, pdata, op);
    1308                 :             : }
    1309                 :             : 
    1310                 :        2879 : void qof_query_add_guid_match (QofQuery *q, QofQueryParamList *param_list,
    1311                 :             :                                const GncGUID *guid, QofQueryOp op)
    1312                 :             : {
    1313                 :        2879 :     GList *g = nullptr;
    1314                 :             : 
    1315                 :        2879 :     if (!q || !param_list) return;
    1316                 :             : 
    1317                 :        2879 :     if (guid)
    1318                 :        2879 :         g = g_list_prepend (g, (gpointer)guid);
    1319                 :             : 
    1320                 :        2879 :     qof_query_add_guid_list_match (q, param_list, g,
    1321                 :             :                                    g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
    1322                 :             : 
    1323                 :        2879 :     g_list_free (g);
    1324                 :             : }
    1325                 :             : 
    1326                 :        2105 : void qof_query_set_book (QofQuery *q, QofBook *book)
    1327                 :             : {
    1328                 :        2105 :     QofQueryParamList *slist = nullptr;
    1329                 :        2105 :     if (!q || !book) return;
    1330                 :             : 
    1331                 :             :     /* Make sure this book is only in the list once */
    1332                 :        2099 :     if (g_list_index (q->books, book) == -1)
    1333                 :        2099 :         q->books = g_list_prepend (q->books, book);
    1334                 :             : 
    1335                 :        2099 :     slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_GUID)));
    1336                 :        2099 :     slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_BOOK)));
    1337                 :        2099 :     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                 :         208 : void qof_query_add_boolean_match (QofQuery *q, QofQueryParamList *param_list, gboolean value,
    1348                 :             :                                   QofQueryOp op)
    1349                 :             : {
    1350                 :             :     QofQueryPredData *pdata;
    1351                 :         208 :     if (!q || !param_list) return;
    1352                 :             : 
    1353                 :         208 :     pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
    1354                 :         208 :     qof_query_add_term (q, param_list, pdata, op);
    1355                 :             : }
    1356                 :             : 
    1357                 :             : /**********************************************************************/
    1358                 :             : /* PRIVATE PUBLISHED API FUNCTIONS                                    */
    1359                 :             : 
    1360                 :         116 : void qof_query_init (void)
    1361                 :             : {
    1362                 :         116 :     ENTER (" ");
    1363                 :         116 :     qof_query_core_init ();
    1364                 :         116 :     qof_class_init ();
    1365                 :         116 :     LEAVE ("Completed initialization of QofQuery");
    1366                 :         116 : }
    1367                 :             : 
    1368                 :          42 : void qof_query_shutdown (void)
    1369                 :             : {
    1370                 :          42 :     qof_class_shutdown ();
    1371                 :          42 :     qof_query_core_shutdown ();
    1372                 :          42 : }
    1373                 :             : 
    1374                 :        1119 : int qof_query_get_max_results (const QofQuery *q)
    1375                 :             : {
    1376                 :        1119 :     if (!q) return 0;
    1377                 :        1119 :     return q->max_results;
    1378                 :             : }
    1379                 :             : 
    1380                 :        1121 : QofIdType qof_query_get_search_for (const QofQuery *q)
    1381                 :             : {
    1382                 :        1121 :     if (!q) return nullptr;
    1383                 :        1121 :     return q->search_for;
    1384                 :             : }
    1385                 :             : 
    1386                 :        1119 : GList * qof_query_get_terms (const QofQuery *q)
    1387                 :             : {
    1388                 :        1119 :     if (!q) return nullptr;
    1389                 :        1119 :     return q->terms;
    1390                 :             : }
    1391                 :             : 
    1392                 :        1931 : QofQueryParamList * qof_query_term_get_param_path (const QofQueryTerm *qt)
    1393                 :             : {
    1394                 :        1931 :     if (!qt)
    1395                 :           0 :         return nullptr;
    1396                 :        1931 :     return qt->param_list;
    1397                 :             : }
    1398                 :             : 
    1399                 :        1931 : QofQueryPredData *qof_query_term_get_pred_data (const QofQueryTerm *qt)
    1400                 :             : {
    1401                 :        1931 :     if (!qt)
    1402                 :           0 :         return nullptr;
    1403                 :        1931 :     return qt->pdata;
    1404                 :             : }
    1405                 :             : 
    1406                 :        1931 : gboolean qof_query_term_is_inverted (const QofQueryTerm *qt)
    1407                 :             : {
    1408                 :        1931 :     if (!qt)
    1409                 :           0 :         return FALSE;
    1410                 :        1931 :     return qt->invert;
    1411                 :             : }
    1412                 :             : 
    1413                 :        1119 : void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
    1414                 :             :                           QofQuerySort **secondary, QofQuerySort **tertiary)
    1415                 :             : {
    1416                 :        1119 :     if (!q)
    1417                 :           0 :         return;
    1418                 :        1119 :     if (primary)
    1419                 :        1119 :         *primary = &(q->primary_sort);
    1420                 :        1119 :     if (secondary)
    1421                 :        1119 :         *secondary = &(q->secondary_sort);
    1422                 :        1119 :     if (tertiary)
    1423                 :        1119 :         *tertiary = &(q->tertiary_sort);
    1424                 :             : }
    1425                 :             : 
    1426                 :        3357 : QofQueryParamList * qof_query_sort_get_param_path (const QofQuerySort *qs)
    1427                 :             : {
    1428                 :        3357 :     if (!qs)
    1429                 :           0 :         return nullptr;
    1430                 :        3357 :     return qs->param_list;
    1431                 :             : }
    1432                 :             : 
    1433                 :        1979 : gint qof_query_sort_get_sort_options (const QofQuerySort *qs)
    1434                 :             : {
    1435                 :        1979 :     if (!qs)
    1436                 :           0 :         return 0;
    1437                 :        1979 :     return qs->options;
    1438                 :             : }
    1439                 :             : 
    1440                 :        1979 : gboolean qof_query_sort_get_increasing (const QofQuerySort *qs)
    1441                 :             : {
    1442                 :        1979 :     if (!qs)
    1443                 :           0 :         return FALSE;
    1444                 :        1979 :     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                 :             :     /** \todo QOF_TYPE_COLLECT */
    1848                 :           0 :     return;
    1849                 :             : }        /* qof_query_printValueForParam */
    1850                 :             : 
    1851                 :             : /*
    1852                 :             :  * Print out a string representation of the
    1853                 :             :  * QofStringMatch enum
    1854                 :             :  */
    1855                 :             : static const char *
    1856                 :           0 : qof_query_printStringMatch (QofStringMatch s)
    1857                 :             : {
    1858                 :           0 :     switch (s)
    1859                 :             :     {
    1860                 :           0 :     case QOF_STRING_MATCH_NORMAL:
    1861                 :           0 :         return "QOF_STRING_MATCH_NORMAL";
    1862                 :           0 :     case QOF_STRING_MATCH_CASEINSENSITIVE:
    1863                 :           0 :         return "QOF_STRING_MATCH_CASEINSENSITIVE";
    1864                 :             :     }
    1865                 :           0 :     return "UNKNOWN MATCH TYPE";
    1866                 :             : }           /* qof_query_printStringMatch */
    1867                 :             : 
    1868                 :             : /*
    1869                 :             :  * Print out a string representation of the
    1870                 :             :  * QofDateMatch enum
    1871                 :             :  */
    1872                 :             : static const char *
    1873                 :           0 : qof_query_printDateMatch (QofDateMatch d)
    1874                 :             : {
    1875                 :           0 :     switch (d)
    1876                 :             :     {
    1877                 :           0 :     case QOF_DATE_MATCH_NORMAL:
    1878                 :           0 :         return "QOF_DATE_MATCH_NORMAL";
    1879                 :           0 :     case QOF_DATE_MATCH_DAY:
    1880                 :           0 :         return "QOF_DATE_MATCH_DAY";
    1881                 :             :     }
    1882                 :           0 :     return "UNKNOWN MATCH TYPE";
    1883                 :             : }            /* qof_query_printDateMatch */
    1884                 :             : 
    1885                 :             : /*
    1886                 :             :  * Print out a string representation of the
    1887                 :             :  * QofNumericMatch enum
    1888                 :             :  */
    1889                 :             : static const char *
    1890                 :           0 : qof_query_printNumericMatch (QofNumericMatch n)
    1891                 :             : {
    1892                 :           0 :     switch (n)
    1893                 :             :     {
    1894                 :           0 :     case QOF_NUMERIC_MATCH_DEBIT:
    1895                 :           0 :         return "QOF_NUMERIC_MATCH_DEBIT";
    1896                 :           0 :     case QOF_NUMERIC_MATCH_CREDIT:
    1897                 :           0 :         return "QOF_NUMERIC_MATCH_CREDIT";
    1898                 :           0 :     case QOF_NUMERIC_MATCH_ANY:
    1899                 :           0 :         return "QOF_NUMERIC_MATCH_ANY";
    1900                 :             :     }
    1901                 :           0 :     return "UNKNOWN MATCH TYPE";
    1902                 :             : }        /* qof_query_printNumericMatch */
    1903                 :             : 
    1904                 :             : /*
    1905                 :             :  * Print out a string representation of the
    1906                 :             :  * QofGuidMatch enum
    1907                 :             :  */
    1908                 :             : static const char *
    1909                 :           0 : qof_query_printGuidMatch (QofGuidMatch g)
    1910                 :             : {
    1911                 :           0 :     switch (g)
    1912                 :             :     {
    1913                 :           0 :     case QOF_GUID_MATCH_ANY:
    1914                 :           0 :         return "QOF_GUID_MATCH_ANY";
    1915                 :           0 :     case QOF_GUID_MATCH_ALL:
    1916                 :           0 :         return "QOF_GUID_MATCH_ALL";
    1917                 :           0 :     case QOF_GUID_MATCH_NONE:
    1918                 :           0 :         return "QOF_GUID_MATCH_NONE";
    1919                 :           0 :     case QOF_GUID_MATCH_NULL:
    1920                 :           0 :         return "QOF_GUID_MATCH_NULL";
    1921                 :           0 :     case QOF_GUID_MATCH_LIST_ANY:
    1922                 :           0 :         return "QOF_GUID_MATCH_LIST_ANY";
    1923                 :             :     }
    1924                 :             : 
    1925                 :           0 :     return "UNKNOWN MATCH TYPE";
    1926                 :             : }         /* qof_query_printGuidMatch */
    1927                 :             : 
    1928                 :             : /*
    1929                 :             :  * Print out a string representation of the
    1930                 :             :  * QofCharMatch enum
    1931                 :             :  */
    1932                 :             : static const char *
    1933                 :           0 : qof_query_printCharMatch (QofCharMatch c)
    1934                 :             : {
    1935                 :           0 :     switch (c)
    1936                 :             :     {
    1937                 :           0 :     case QOF_CHAR_MATCH_ANY:
    1938                 :           0 :         return "QOF_CHAR_MATCH_ANY";
    1939                 :           0 :     case QOF_CHAR_MATCH_NONE:
    1940                 :           0 :         return "QOF_CHAR_MATCH_NONE";
    1941                 :             :     }
    1942                 :           0 :     return "UNKNOWN MATCH TYPE";
    1943                 :             : }         /* qof_query_printGuidMatch */
    1944                 :             : 
    1945                 :             : /* ======================== END OF FILE =================== */
        

Generated by: LCOV version 2.0-1