GnuCash  5.6-150-g038405b370+
qofquery.cpp
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 
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 
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 static void query_init (QofQuery *q, QofQueryTerm *initial_term)
134 {
135  GList * _or_ = nullptr;
136  GList *_and_ = nullptr;
137  GHashTable *ht;
138 
139  if (initial_term)
140  {
141  _or_ = g_list_alloc ();
142  _and_ = g_list_alloc ();
143  _and_->data = initial_term;
144  _or_->data = _and_;
145  }
146 
147  if (q->terms)
148  qof_query_clear (q);
149 
150  g_list_free (q->results);
151  g_list_free (q->books);
152 
153  g_slist_free (q->primary_sort.param_list);
154  g_slist_free (q->secondary_sort.param_list);
155  g_slist_free (q->tertiary_sort.param_list);
156 
157  g_slist_free (q->primary_sort.param_fcns);
158  g_slist_free (q->secondary_sort.param_fcns);
159  g_slist_free (q->tertiary_sort.param_fcns);
160 
161  ht = q->be_compiled;
162  memset (q, 0, sizeof (*q));
163  q->be_compiled = ht;
164 
165  q->terms = _or_;
166  q->changed = 1;
167  q->max_results = -1;
168 
169  q->primary_sort.param_list = g_slist_prepend (static_cast<GSList*>(nullptr),
170  static_cast<void*>(const_cast<char*>(QUERY_DEFAULT_SORT)));
171  q->primary_sort.increasing = TRUE;
172  q->secondary_sort.increasing = TRUE;
173  q->tertiary_sort.increasing = TRUE;
174 }
175 
176 static void swap_terms (QofQuery *q1, QofQuery *q2)
177 {
178  GList *g;
179 
180  if (!q1 || !q2) return;
181 
182  g = q1->terms;
183  q1->terms = q2->terms;
184  q2->terms = g;
185 
186  g = q1->books;
187  q1->books = q2->books;
188  q2->books = g;
189 
190  q1->changed = 1;
191  q2->changed = 1;
192 }
193 
194 static void free_query_term (QofQueryTerm *qt)
195 {
196  if (!qt) return;
197 
198  qof_query_core_predicate_free (qt->pdata);
199  g_slist_free (qt->param_list);
200  g_slist_free (qt->param_fcns);
201  g_free (qt);
202 }
203 
204 static QofQueryTerm * copy_query_term (const QofQueryTerm *qt)
205 {
206  QofQueryTerm *new_qt;
207  if (!qt) return nullptr;
208 
209  new_qt = g_new0 (QofQueryTerm, 1);
210  *new_qt = *qt;
211  new_qt->param_list = g_slist_copy (qt->param_list);
212  new_qt->param_fcns = g_slist_copy (qt->param_fcns);
213  new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
214  return new_qt;
215 }
216 
217 static GList * copy_and_terms (const GList *and_terms)
218 {
219  GList *_and_ = nullptr;
220  const GList *cur_and;
221 
222  for (cur_and = and_terms; cur_and; cur_and = cur_and->next)
223  {
224  _and_ = g_list_prepend(_and_, copy_query_term (static_cast<QofQueryTerm*>(cur_and->data)));
225  }
226 
227  return g_list_reverse(_and_);
228 }
229 
230 static GList *
231 copy_or_terms(const GList * or_terms)
232 {
233  GList * _or_ = nullptr;
234  const GList * cur_or;
235 
236  for (cur_or = or_terms; cur_or; cur_or = cur_or->next)
237  {
238  _or_ = g_list_prepend(_or_, copy_and_terms(static_cast<GList*>(cur_or->data)));
239  }
240 
241  return g_list_reverse(_or_);
242 }
243 
244 static void copy_sort (QofQuerySort *dst, const QofQuerySort *src)
245 {
246  *dst = *src;
247  dst->param_list = g_slist_copy (src->param_list);
248  dst->param_fcns = g_slist_copy (src->param_fcns);
249 }
250 
251 static void free_sort (QofQuerySort *s)
252 {
253  g_slist_free (s->param_list);
254  s->param_list = nullptr;
255 
256  g_slist_free (s->param_fcns);
257  s->param_fcns = nullptr;
258 }
259 
260 static void free_members (QofQuery *q)
261 {
262  GList * cur_or;
263 
264  if (q == nullptr) return;
265 
266  for (cur_or = q->terms; cur_or; cur_or = cur_or->next)
267  {
268  GList * cur_and;
269 
270  for (cur_and = static_cast<GList*>(cur_or->data); cur_and;
271  cur_and = static_cast<GList*>(cur_and->next))
272  {
273  free_query_term(static_cast<QofQueryTerm*>(cur_and->data));
274  cur_and->data = nullptr;
275  }
276 
277  g_list_free(static_cast<GList*>(cur_or->data));
278  cur_or->data = nullptr;
279  }
280 
281  free_sort (&(q->primary_sort));
282  free_sort (&(q->secondary_sort));
283  free_sort (&(q->tertiary_sort));
284 
285  g_list_free(q->terms);
286  q->terms = nullptr;
287 
288  g_list_free(q->books);
289  q->books = nullptr;
290 
291  g_list_free(q->results);
292  q->results = nullptr;
293 }
294 
295 static int cmp_func (const QofQuerySort *sort, QofSortFunc default_sort,
296  const gconstpointer a, const gconstpointer b)
297 {
298  QofParam *param = nullptr;
299  GSList *node;
300  gpointer conva, convb;
301 
302  g_return_val_if_fail (sort, 0);
303 
304  /* See if this is a default sort */
305  if (sort->use_default)
306  {
307  if (default_sort) return default_sort (a, b);
308  return 0;
309  }
310 
311  /* If no parameters, consider them equal */
312  if (!sort->param_fcns) return 0;
313 
314  /* no compare function, consider the two objects equal */
315  if (!sort->comp_fcn && !sort->obj_cmp) return 0;
316 
317  /* Do the list of conversions */
318  conva = (gpointer)a;
319  convb = (gpointer)b;
320  for (node = static_cast<GSList*>(sort->param_fcns); node;
321  node = static_cast<GSList*>(node->next))
322  {
323  param = static_cast<QofParam*>(node->data);
324 
325  /* The last term is really the "parameter getter",
326  * unless we're comparing objects ;) */
327  if (!node->next && !sort->obj_cmp)
328  break;
329 
330  /* Do the conversions */
331  conva = (param->param_getfcn) (conva, param);
332  convb = (param->param_getfcn) (convb, param);
333  }
334 
335  /* And now return the (appropriate) compare */
336  if (sort->comp_fcn)
337  {
338  int rc = sort->comp_fcn (conva, convb, sort->options, param);
339  return rc;
340  }
341 
342  return sort->obj_cmp (conva, convb);
343 }
344 
345 static int
346 sort_func (const gconstpointer a, const gconstpointer b, const gpointer q)
347 {
348  int retval;
349  const QofQuery *sortQuery = static_cast<const QofQuery*>(q);
350 
351  g_return_val_if_fail (sortQuery, 0);
352 
353  retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b);
354  if (retval == 0)
355  {
356  retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort,
357  a, b);
358  if (retval == 0)
359  {
360  retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort,
361  a, b);
362  return sortQuery->tertiary_sort.increasing ? retval : -retval;
363  }
364  else
365  {
366  return sortQuery->secondary_sort.increasing ? retval : -retval;
367  }
368  }
369  else
370  {
371  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 check_object (const QofQuery *q, gpointer object)
383 {
384  const GList * and_ptr;
385  const GList * or_ptr;
386  const QofQueryTerm * qt;
387  int and_terms_ok = 1;
388 
389  for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
390  {
391  and_terms_ok = 1;
392  for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
393  and_ptr = static_cast<GList*>(and_ptr->next))
394  {
395  qt = (QofQueryTerm *)(and_ptr->data);
396  if (qt->param_fcns && qt->pred_fcn)
397  {
398  const GSList *node;
399  QofParam *param = nullptr;
400  gpointer conv_obj = object;
401 
402  /* iterate through the conversions */
403  for (node = qt->param_fcns; node; node = node->next)
404  {
405  param = static_cast<QofParam*>(node->data);
406 
407  /* The last term is the actual parameter getter */
408  if (!node->next) break;
409 
410  conv_obj = param->param_getfcn (conv_obj, param);
411  }
412 
413  if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert)
414  {
415  and_terms_ok = 0;
416  break;
417  }
418  }
419  else
420  {
421  /* XXX: Don't know how to do this conversion -- do we care? */
422  }
423  }
424  if (and_terms_ok)
425  {
426  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  if (nullptr == q->terms) return 1;
436  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 compile_params (QofQueryParamList *param_list, QofIdType start_obj,
447  QofParam const **final)
448 {
449  const QofParam *objDef = nullptr;
450  GSList *fcns = nullptr;
451 
452  ENTER ("param_list=%p id=%s", param_list, start_obj);
453  g_return_val_if_fail (param_list, nullptr);
454  g_return_val_if_fail (start_obj, nullptr);
455  g_return_val_if_fail (final, nullptr);
456 
457  for (; param_list; param_list = param_list->next)
458  {
459  QofIdType param_name = static_cast<QofIdType>(param_list->data);
460  objDef = qof_class_get_parameter (start_obj, param_name);
461 
462  /* If it doesn't exist, then we've reached the end */
463  if (!objDef) break;
464 
465  /* Save off this parameter */
466  fcns = g_slist_prepend (fcns, (gpointer) objDef);
467 
468  /* Save this off, just in case */
469  *final = objDef;
470 
471  /* And reset for the next parameter */
472  start_obj = (QofIdType) objDef->param_type;
473  }
474 
475  LEAVE ("fcns=%p", fcns);
476  return (g_slist_reverse (fcns));
477 }
478 
479 static void
480 compile_sort (QofQuerySort *sort, QofIdType obj)
481 {
482  const QofParam *resObj = nullptr;
483 
484  ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
485  sort->use_default = FALSE;
486 
487  g_slist_free (sort->param_fcns);
488  sort->param_fcns = nullptr;
489  sort->comp_fcn = nullptr;
490  sort->obj_cmp = nullptr;
491 
492  /* An empty param_list implies "no sort" */
493  if (!sort->param_list)
494  {
495  LEAVE (" ");
496  return;
497  }
498 
499  /* Walk the parameter list of obtain the parameter functions */
500  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  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  if (resObj->param_compfcn)
511  sort->comp_fcn = resObj->param_compfcn;
512  else
513  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  if (sort->comp_fcn == nullptr)
517  sort->obj_cmp = qof_class_get_default_sort (resObj->param_type);
518  }
519  else if (!g_strcmp0 (static_cast<char*>(sort->param_list->data),
521  {
522  sort->use_default = TRUE;
523  }
524  LEAVE ("sort=%p id=%s", sort, obj);
525 }
526 
527 static void compile_terms (QofQuery *q)
528 {
529  GList *or_ptr, *and_ptr;
530 
531  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  for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
536  {
537  for (and_ptr = static_cast<GList*>(or_ptr->data); and_ptr;
538  and_ptr = static_cast<GList*>(and_ptr->next))
539  {
540  QofQueryTerm* qt = static_cast<QofQueryTerm*>(and_ptr->data);
541  const QofParam* resObj = nullptr;
542 
543  g_slist_free (qt->param_fcns);
544  qt->param_fcns = nullptr;
545 
546  /* Walk the parameter list of obtain the parameter functions */
547  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  if (qt->param_fcns && resObj)
555  qt->pred_fcn = qof_query_core_get_predicate (resObj->param_type);
556  else
557  qt->pred_fcn = nullptr;
558  }
559  }
560 
561  /* Update the sort functions */
562  compile_sort (&(q->primary_sort), q->search_for);
563  compile_sort (&(q->secondary_sort), q->search_for);
564  compile_sort (&(q->tertiary_sort), q->search_for);
565 
566  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  LEAVE (" query=%p", q);
584 }
585 
586 static void check_item_cb (gpointer object, gpointer user_data)
587 {
588  QofQueryCB* ql = static_cast<QofQueryCB*>(user_data);
589 
590  if (!object || !ql) return;
591 
592  if (check_object (ql->query, object))
593  {
594  ql->list = g_list_prepend (ql->list, object);
595  ql->count++;
596  }
597  return;
598 }
599 
600 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  if (!l1 && !l2) return 0;
609  if (!l1 && l2) return -1;
610  if (l1 && !l2) return 1;
611 
612  ret = g_strcmp0 (static_cast<char*>(l1->data),
613  static_cast<char*>(l2->data));
614  if (ret)
615  break;
616 
617  l1 = l1->next;
618  l2 = l2->next;
619  }
620  return ret;
621 }
622 
623 static GList * merge_books (GList *l1, GList *l2)
624 {
625  GList *res = nullptr;
626  GList *node;
627 
628  res = g_list_copy (l1);
629 
630  for (node = l2; node; node = node->next)
631  {
632  if (g_list_index (res, node->data) == -1)
633  res = g_list_prepend (res, node->data);
634  }
635 
636  return res;
637 }
638 
639 static gboolean
640 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  return TRUE;
650 }
651 
652 /* clear out any cached query_compilations */
653 static void query_clear_compiles (QofQuery *q)
654 {
655  g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, nullptr);
656 }
657 
658 /********************************************************************/
659 /* PUBLISHED API FUNCTIONS */
660 
662 qof_query_build_param_list (char const *param, ...)
663 {
664  QofQueryParamList *param_list = nullptr;
665  char const *this_param;
666  va_list ap;
667 
668  if (!param)
669  return nullptr;
670 
671  va_start (ap, param);
672 
673  for (this_param = param; this_param; this_param = va_arg (ap, const char *))
674  param_list = g_slist_prepend (param_list, (gpointer)this_param);
675 
676  va_end (ap);
677 
678  return g_slist_reverse (param_list);
679 }
680 
682  QofQueryPredData *pred_data, QofQueryOp op)
683 {
684  QofQueryTerm *qt;
685  QofQuery *qr, *qs;
686 
687  if (!q || !param_list || !pred_data) return;
688 
689  qt = g_new0 (QofQueryTerm, 1);
690  qt->param_list = param_list;
691  qt->pdata = pred_data;
692  qs = qof_query_create ();
693  query_init (qs, qt);
694 
695  if (q->terms != nullptr)
696  qr = qof_query_merge (q, qs, op);
697  else
698  qr = qof_query_merge (q, qs, QOF_QUERY_OR);
699 
700  swap_terms (q, qr);
701  qof_query_destroy (qs);
702  qof_query_destroy (qr);
703 }
704 
706 {
707  QofQueryTerm *qt;
708  GList *_or_, *_and_;
709 
710  if (!q || !param_list) return;
711 
712  for (_or_ = q->terms; _or_; _or_ = _or_->next)
713  {
714  for (_and_ = static_cast<GList*>(_or_->data); _and_;
715  _and_ = static_cast<GList*>(_and_->next))
716  {
717  qt = static_cast<QofQueryTerm*>(_and_->data);
718  if (!param_list_cmp (qt->param_list, param_list))
719  {
720  auto or_list = static_cast<GList*> (_or_->data);
721  if (or_list && !or_list->next)
722  {
723  q->terms = g_list_remove_link (static_cast<GList*>(q->terms), _or_);
724  g_list_free_1 (_or_);
725  _or_ = q->terms;
726  break;
727  }
728  else
729  {
730  _or_->data = g_list_remove_link (or_list, _and_);
731  g_list_free_1 (_and_);
732  _and_ = static_cast<GList*>(_or_->data);
733  if (!_and_) break;
734  }
735  q->changed = 1;
736  free_query_term (qt);
737  }
738  }
739  if (!_or_) break;
740  }
741 }
742 
743 static GList * qof_query_run_internal (QofQuery *q,
744  void(*run_cb)(QofQueryCB*, gpointer),
745  gpointer cb_arg)
746 {
747  GList *matching_objects = nullptr;
748  int object_count = 0;
749 
750  if (!q) return nullptr;
751  g_return_val_if_fail (q->search_for, nullptr);
752  g_return_val_if_fail (q->books, nullptr);
753  g_return_val_if_fail (run_cb, nullptr);
754  ENTER (" q=%p", q);
755 
756  /* XXX: Prioritize the query terms? */
757 
758  /* prepare the Query for processing */
759  if (q->changed)
760  {
761  query_clear_compiles (q);
762  compile_terms (q);
763  }
764 
765  /* Maybe log this sucker */
766  if (qof_log_check (log_module, QOF_LOG_DEBUG))
767  qof_query_print (q);
768 
769  /* Now run the query over all the objects and save the results */
770  {
771  QofQueryCB qcb;
772 
773  memset (&qcb, 0, sizeof (qcb));
774  qcb.query = q;
775 
776  /* Run the query callback */
777  run_cb(&qcb, cb_arg);
778 
779  matching_objects = qcb.list;
780  object_count = qcb.count;
781  }
782  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  matching_objects = g_list_reverse(matching_objects);
791 
792  /* Now sort the matching objects based on the search criteria */
793  if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
794  (q->primary_sort.use_default && q->defaultSort))
795  {
796  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  if ((object_count > q->max_results) && (q->max_results > -1))
801  {
802  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  mptr = g_list_nth(matching_objects, object_count - q->max_results);
808  /* mptr should not be nullptr, but let's be safe */
809  if (mptr != nullptr)
810  {
811  if (mptr->prev != nullptr) mptr->prev->next = nullptr;
812  mptr->prev = nullptr;
813  }
814  g_list_free(matching_objects);
815  matching_objects = mptr;
816  }
817  else
818  {
819  /* q->max_results == 0 */
820  g_list_free(matching_objects);
821  matching_objects = nullptr;
822  }
823  }
824 
825  q->changed = 0;
826 
827  g_list_free(q->results);
828  q->results = matching_objects;
829 
830  LEAVE (" q=%p", q);
831  return matching_objects;
832 }
833 
834 static void qof_query_run_cb(QofQueryCB* qcb, gpointer cb_arg)
835 {
836  GList *node;
837 
838  (void)cb_arg; /* unused */
839  g_return_if_fail(qcb);
840 
841  for (node = qcb->query->books; node; node = node->next)
842  {
843  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  qof_object_foreach (qcb->query->search_for, book,
861  (QofInstanceForeachCB) check_item_cb, qcb);
862  }
863 }
864 
866 {
867  /* Just a wrapper */
868  return qof_query_run_internal(q, qof_query_run_cb, nullptr);
869 }
870 
871 static void qof_query_run_subq_cb(QofQueryCB* qcb, gpointer cb_arg)
872 {
873  QofQuery* pq = static_cast<QofQuery*>(cb_arg);
874 
875  g_return_if_fail(pq);
876  g_list_foreach(qof_query_last_run(pq), check_item_cb, qcb);
877 }
878 
879 GList *
880 qof_query_run_subquery (QofQuery *subq, const QofQuery* primaryq)
881 {
882  if (!subq) return nullptr;
883  if (!primaryq) return nullptr;
884 
885  /* Make sure we're searching for the same thing */
886  g_return_val_if_fail (subq->search_for, nullptr);
887  g_return_val_if_fail (primaryq->search_for, nullptr);
888  g_return_val_if_fail(!g_strcmp0(subq->search_for, primaryq->search_for),
889  nullptr);
890 
891  /* Perform the subquery */
892  return qof_query_run_internal(subq, qof_query_run_subq_cb,
893  (gpointer)primaryq);
894 }
895 
896 GList *
898 {
899  if (!query)
900  return nullptr;
901 
902  return query->results;
903 }
904 
906 {
907  QofQuery *q2 = qof_query_create ();
908  swap_terms (query, q2);
909  qof_query_destroy (q2);
910 
911  g_list_free (query->books);
912  query->books = nullptr;
913  g_list_free (query->results);
914  query->results = nullptr;
915  query->changed = 1;
916 }
917 
919 {
920  QofQuery *qp = g_new0 (QofQuery, 1);
921  qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
922  query_init (qp, nullptr);
923  return qp;
924 }
925 
927 {
928  if (!q || !obj_type)
929  return;
930 
931  if (g_strcmp0 (q->search_for, obj_type))
932  {
933  q->search_for = (QofIdType) obj_type;
934  q->changed = 1;
935  }
936 }
937 
938 QofQuery * qof_query_create_for (QofIdTypeConst obj_type)
939 {
940  QofQuery *q;
941  if (!obj_type)
942  return nullptr;
943  q = qof_query_create ();
944  qof_query_search_for (q, obj_type);
945  return q;
946 }
947 
949 {
950  if (!q) return 0;
951  return g_list_length (q->terms);
952 }
953 
955 {
956  GList *o;
957  int n = 0;
958  if (!q) return 0;
959  for (o = q->terms; o; o = o->next)
960  n += g_list_length(static_cast<GList*>(o->data));
961  return n;
962 }
963 
965 {
966  GList *_or_;
967  GList *_and_;
968 
969  if (!q || !term_param)
970  return FALSE;
971 
972  for (_or_ = q->terms; _or_; _or_ = _or_->next)
973  {
974  for (_and_ = static_cast<GList*>(_or_->data); _and_;
975  _and_ = static_cast<GList*>(_and_->next))
976  {
977  QofQueryTerm* qt = static_cast<QofQueryTerm*>(_and_->data);
978  if (!param_list_cmp (term_param, qt->param_list))
979  return TRUE;
980  }
981  }
982 
983  return FALSE;
984 }
985 
986 GSList * qof_query_get_term_type (QofQuery *q, QofQueryParamList *term_param)
987 {
988  GList *_or_;
989  GList *_and_;
990  GSList *results = nullptr;
991 
992  if (!q || !term_param)
993  return FALSE;
994 
995  for (_or_ = q->terms; _or_; _or_ = _or_->next)
996  {
997  for (_and_ = static_cast<GList*>(_or_->data); _and_;
998  _and_ = static_cast<GList*>(_and_->next))
999  {
1000  QofQueryTerm *qt = static_cast<QofQueryTerm*>(_and_->data);
1001  if (!param_list_cmp (term_param, qt->param_list))
1002  results = g_slist_prepend (results, qt->pdata);
1003  }
1004  }
1005 
1006  return g_slist_reverse (results);
1007 }
1008 
1010 {
1011  if (!q) return;
1012  free_members (q);
1013  query_clear_compiles (q);
1014  g_hash_table_destroy (q->be_compiled);
1015  g_free (q);
1016 }
1017 
1019 {
1020  QofQuery *copy;
1021  GHashTable *ht;
1022 
1023  if (!q) return nullptr;
1024  copy = qof_query_create ();
1025  ht = copy->be_compiled;
1026  free_members (copy);
1027 
1028  *copy = *q;
1029 
1030  copy->be_compiled = ht;
1031  copy->terms = copy_or_terms (q->terms);
1032  copy->books = g_list_copy (q->books);
1033  copy->results = g_list_copy (q->results);
1034 
1035  copy_sort (&(copy->primary_sort), &(q->primary_sort));
1036  copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
1037  copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
1038 
1039  copy->changed = 1;
1040 
1041  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 
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  if (!q)
1061  return nullptr;
1062 
1063  num_or_terms = g_list_length(q->terms);
1064 
1065  switch (num_or_terms)
1066  {
1067  case 0:
1068  retval = qof_query_create();
1069  retval->max_results = q->max_results;
1070  break;
1071 
1072  /* This is the DeMorgan expansion for a single AND expression. */
1073  /* !(abc) = !a + !b + !c */
1074  case 1:
1075  retval = qof_query_create();
1076  retval->max_results = q->max_results;
1077  retval->books = g_list_copy (q->books);
1078  retval->search_for = q->search_for;
1079  retval->changed = 1;
1080 
1081  aterms = static_cast<GList*>(g_list_nth_data(q->terms, 0));
1082  new_oterm = nullptr;
1083  for (cur = aterms; cur; cur = cur->next)
1084  {
1085  qt = copy_query_term(static_cast<QofQueryTerm*>(cur->data));
1086  qt->invert = !(qt->invert);
1087  new_oterm = g_list_append(nullptr, qt);
1088  retval->terms = g_list_prepend(retval->terms, new_oterm);
1089  }
1090  retval->terms = g_list_reverse(retval->terms);
1091  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  default:
1097  right = qof_query_create();
1098  right->terms = copy_or_terms(g_list_nth(q->terms, 1));
1099 
1100  left = qof_query_create();
1101  left->terms = g_list_append(nullptr,
1102  copy_and_terms(static_cast<GList*>(g_list_nth_data(q->terms, 0))));
1103 
1104  iright = qof_query_invert(right);
1105  ileft = qof_query_invert(left);
1106 
1107  retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
1108  retval->books = g_list_copy (q->books);
1109  retval->max_results = q->max_results;
1110  retval->search_for = q->search_for;
1111  retval->changed = 1;
1112 
1113  qof_query_destroy(iright);
1114  qof_query_destroy(ileft);
1115  qof_query_destroy(right);
1116  qof_query_destroy(left);
1117  break;
1118  }
1119 
1120  return retval;
1121 }
1122 
1123 /* *******************************************************************
1124  * qof_query_merge
1125  * combine 2 Query objects by the logical operation in "op".
1126  ********************************************************************/
1127 
1128 QofQuery *
1130 {
1131 
1132  QofQuery * retval = nullptr;
1133  QofQuery * i1, * i2;
1134  QofQuery * t1, * t2;
1135  GList * i, * j;
1136  QofIdType search_for;
1137 
1138  if (!q1) return q2;
1139  if (!q2) return q1;
1140 
1141  if (q1->search_for && q2->search_for)
1142  g_return_val_if_fail (g_strcmp0 (q1->search_for, q2->search_for) == 0,
1143  nullptr);
1144 
1145  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  if ((QOF_QUERY_AND == op) &&
1157  (q1->terms == nullptr || q2->terms == nullptr))
1158  {
1159  op = QOF_QUERY_OR;
1160  }
1161 
1162  switch (op)
1163  {
1164  case QOF_QUERY_OR:
1165  retval = qof_query_create();
1166  retval->terms =
1167  g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
1168  retval->books = merge_books (q1->books, q2->books);
1169  retval->max_results = q1->max_results;
1170  retval->changed = 1;
1171  break;
1172 
1173  case QOF_QUERY_AND:
1174  retval = qof_query_create();
1175  retval->books = merge_books (q1->books, q2->books);
1176  retval->max_results = q1->max_results;
1177  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  for (i = q1->terms; i; i = i->next)
1184  {
1185  for (j = q2->terms; j; j = j->next)
1186  {
1187  retval->terms =
1188  g_list_prepend(retval->terms,
1189  g_list_concat
1190  (copy_and_terms(static_cast<GList*>(i->data)),
1191  copy_and_terms(static_cast<GList*>(j->data))));
1192  }
1193  }
1194  retval->terms = g_list_reverse(retval->terms);
1195  break;
1196 
1197  case QOF_QUERY_NAND:
1198  /* !(a*b) = (!a + !b) */
1199  i1 = qof_query_invert(q1);
1200  i2 = qof_query_invert(q2);
1201  retval = qof_query_merge(i1, i2, QOF_QUERY_OR);
1202  qof_query_destroy(i1);
1203  qof_query_destroy(i2);
1204  break;
1205 
1206  case QOF_QUERY_NOR:
1207  /* !(a+b) = (!a*!b) */
1208  i1 = qof_query_invert(q1);
1209  i2 = qof_query_invert(q2);
1210  retval = qof_query_merge(i1, i2, QOF_QUERY_AND);
1211  qof_query_destroy(i1);
1212  qof_query_destroy(i2);
1213  break;
1214 
1215  case QOF_QUERY_XOR:
1216  /* a xor b = (a * !b) + (!a * b) */
1217  i1 = qof_query_invert(q1);
1218  i2 = qof_query_invert(q2);
1219  t1 = qof_query_merge(q1, i2, QOF_QUERY_AND);
1220  t2 = qof_query_merge(i1, q2, QOF_QUERY_AND);
1221  retval = qof_query_merge(t1, t2, QOF_QUERY_OR);
1222 
1223  qof_query_destroy(i1);
1224  qof_query_destroy(i2);
1225  qof_query_destroy(t1);
1226  qof_query_destroy(t2);
1227  break;
1228  }
1229 
1230  if (retval)
1231  retval->search_for = search_for;
1232  return retval;
1233 }
1234 
1235 void
1237 {
1238  QofQuery *tmp_q;
1239 
1240  if (!q1 || !q2)
1241  return;
1242 
1243  tmp_q = qof_query_merge (q1, q2, op);
1244  swap_terms (q1, tmp_q);
1245  qof_query_destroy (tmp_q);
1246 }
1247 
1248 void
1250  QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
1251 {
1252  if (!q) return;
1253  if (q->primary_sort.param_list)
1254  g_slist_free (q->primary_sort.param_list);
1255  q->primary_sort.param_list = params1;
1256  q->primary_sort.options = 0;
1257 
1258  if (q->secondary_sort.param_list)
1259  g_slist_free (q->secondary_sort.param_list);
1260  q->secondary_sort.param_list = params2;
1261  q->secondary_sort.options = 0;
1262 
1263  if (q->tertiary_sort.param_list)
1264  g_slist_free (q->tertiary_sort.param_list);
1265  q->tertiary_sort.param_list = params3;
1266  q->tertiary_sort.options = 0;
1267 
1268  q->changed = 1;
1269 }
1270 
1271 void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
1272  gint tert_op)
1273 {
1274  if (!q) return;
1275  q->primary_sort.options = prim_op;
1276  q->secondary_sort.options = sec_op;
1277  q->tertiary_sort.options = tert_op;
1278 }
1279 
1280 void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
1281  gboolean sec_inc, gboolean tert_inc)
1282 {
1283  if (!q) return;
1284  q->primary_sort.increasing = prim_inc;
1285  q->secondary_sort.increasing = sec_inc;
1286  q->tertiary_sort.increasing = tert_inc;
1287 }
1288 
1290 {
1291  if (!q) return;
1292  q->max_results = n;
1293 }
1294 
1296  GList *guid_list, QofGuidMatch options,
1297  QofQueryOp op)
1298 {
1299  QofQueryPredData *pdata;
1300 
1301  if (!q || !param_list) return;
1302 
1303  if (!guid_list)
1304  g_return_if_fail (options == QOF_GUID_MATCH_NULL);
1305 
1306  pdata = qof_query_guid_predicate (options, guid_list);
1307  qof_query_add_term (q, param_list, pdata, op);
1308 }
1309 
1311  const GncGUID *guid, QofQueryOp op)
1312 {
1313  GList *g = nullptr;
1314 
1315  if (!q || !param_list) return;
1316 
1317  if (guid)
1318  g = g_list_prepend (g, (gpointer)guid);
1319 
1320  qof_query_add_guid_list_match (q, param_list, g,
1321  g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
1322 
1323  g_list_free (g);
1324 }
1325 
1326 void qof_query_set_book (QofQuery *q, QofBook *book)
1327 {
1328  QofQueryParamList *slist = nullptr;
1329  if (!q || !book) return;
1330 
1331  /* Make sure this book is only in the list once */
1332  if (g_list_index (q->books, book) == -1)
1333  q->books = g_list_prepend (q->books, book);
1334 
1335  slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_GUID)));
1336  slist = g_slist_prepend (slist, static_cast<void*>(const_cast<char*>(QOF_PARAM_BOOK)));
1337  qof_query_add_guid_match (q, slist,
1338  qof_instance_get_guid(book), QOF_QUERY_AND);
1339 }
1340 
1342 {
1343  if (!q) return nullptr;
1344  return q->books;
1345 }
1346 
1347 void qof_query_add_boolean_match (QofQuery *q, QofQueryParamList *param_list, gboolean value,
1348  QofQueryOp op)
1349 {
1350  QofQueryPredData *pdata;
1351  if (!q || !param_list) return;
1352 
1353  pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
1354  qof_query_add_term (q, param_list, pdata, op);
1355 }
1356 
1357 /**********************************************************************/
1358 /* PRIVATE PUBLISHED API FUNCTIONS */
1359 
1360 void qof_query_init (void)
1361 {
1362  ENTER (" ");
1363  qof_query_core_init ();
1364  qof_class_init ();
1365  LEAVE ("Completed initialization of QofQuery");
1366 }
1367 
1368 void qof_query_shutdown (void)
1369 {
1370  qof_class_shutdown ();
1371  qof_query_core_shutdown ();
1372 }
1373 
1374 int qof_query_get_max_results (const QofQuery *q)
1375 {
1376  if (!q) return 0;
1377  return q->max_results;
1378 }
1379 
1381 {
1382  if (!q) return nullptr;
1383  return q->search_for;
1384 }
1385 
1386 GList * qof_query_get_terms (const QofQuery *q)
1387 {
1388  if (!q) return nullptr;
1389  return q->terms;
1390 }
1391 
1392 QofQueryParamList * qof_query_term_get_param_path (const QofQueryTerm *qt)
1393 {
1394  if (!qt)
1395  return nullptr;
1396  return qt->param_list;
1397 }
1398 
1399 QofQueryPredData *qof_query_term_get_pred_data (const QofQueryTerm *qt)
1400 {
1401  if (!qt)
1402  return nullptr;
1403  return qt->pdata;
1404 }
1405 
1406 gboolean qof_query_term_is_inverted (const QofQueryTerm *qt)
1407 {
1408  if (!qt)
1409  return FALSE;
1410  return qt->invert;
1411 }
1412 
1413 void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
1414  QofQuerySort **secondary, QofQuerySort **tertiary)
1415 {
1416  if (!q)
1417  return;
1418  if (primary)
1419  *primary = &(q->primary_sort);
1420  if (secondary)
1421  *secondary = &(q->secondary_sort);
1422  if (tertiary)
1423  *tertiary = &(q->tertiary_sort);
1424 }
1425 
1426 QofQueryParamList * qof_query_sort_get_param_path (const QofQuerySort *qs)
1427 {
1428  if (!qs)
1429  return nullptr;
1430  return qs->param_list;
1431 }
1432 
1433 gint qof_query_sort_get_sort_options (const QofQuerySort *qs)
1434 {
1435  if (!qs)
1436  return 0;
1437  return qs->options;
1438 }
1439 
1440 gboolean qof_query_sort_get_increasing (const QofQuerySort *qs)
1441 {
1442  if (!qs)
1443  return FALSE;
1444  return qs->increasing;
1445 }
1446 
1447 static gboolean
1448 qof_query_term_equal (const QofQueryTerm *qt1, const QofQueryTerm *qt2)
1449 {
1450  if (qt1 == qt2) return TRUE;
1451  if (!qt1 || !qt2) return FALSE;
1452 
1453  if (qt1->invert != qt2->invert) return FALSE;
1454  if (param_list_cmp (qt1->param_list, qt2->param_list)) return FALSE;
1455  return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
1456 }
1457 
1458 static gboolean
1459 qof_query_sort_equal (const QofQuerySort* qs1, const QofQuerySort* qs2)
1460 {
1461  if (qs1 == qs2) return TRUE;
1462  if (!qs1 || !qs2) return FALSE;
1463 
1464  /* "Empty" sorts are equivalent, regardless of the flags */
1465  if (!qs1->param_list && !qs2->param_list) return TRUE;
1466 
1467  if (qs1->options != qs2->options) return FALSE;
1468  if (qs1->increasing != qs2->increasing) return FALSE;
1469  return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
1470 }
1471 
1472 gboolean qof_query_equal (const QofQuery *q1, const QofQuery *q2)
1473 {
1474  GList *or1, *or2;
1475 
1476  if (q1 == q2) return TRUE;
1477  if (!q1 || !q2) return FALSE;
1478 
1479  if (q1->max_results != q2->max_results) return FALSE;
1480 
1481  for (or1 = q1->terms, or2 = q2->terms; or1 || or2;
1482  or1 = or1->next, or2 = or2->next)
1483  {
1484  GList *and1, *and2;
1485 
1486  if (!or1 || !or2)
1487  return FALSE;
1488  and1 = static_cast<GList*>(or1->data);
1489  and2 = static_cast<GList*>(or2->data);
1490 
1491  for (; and1 || and2; and1 = and1->next, and2 = and2->next)
1492  {
1493  if (!and1 || !and2)
1494  return FALSE;
1495  if (!qof_query_term_equal (static_cast<QofQueryTerm*>(and1->data),
1496  static_cast<QofQueryTerm*>(and2->data)))
1497  return FALSE;
1498  }
1499  }
1500 
1501  if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
1502  return FALSE;
1503  if (!qof_query_sort_equal (&(q1->secondary_sort), &(q2->secondary_sort)))
1504  return FALSE;
1505  if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
1506  return FALSE;
1507 
1508  return TRUE;
1509 }
1510 
1511 /* **************************************************************************/
1512 /* Query Print functions for use with qof_log_set_level.
1513 */
1514 
1515 void
1516 qof_query_print (QofQuery * query)
1517 {
1518  GList *output;
1519  GString *str;
1520  QofQuerySort *s[3];
1521  gint maxResults = 0, numSorts = 3;
1522 
1523  ENTER (" ");
1524 
1525  if (!query)
1526  {
1527  LEAVE("query is (null)");
1528  return;
1529  }
1530 
1531  output = nullptr;
1532  str = nullptr;
1533  maxResults = qof_query_get_max_results (query);
1534 
1535  output = qof_query_printSearchFor (query, output);
1536  output = qof_query_printTerms (query, output);
1537 
1538  qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
1539 
1540  if (s[0])
1541  {
1542  output = qof_query_printSorts (s, numSorts, output);
1543  }
1544 
1545  str = g_string_new (" ");
1546  g_string_printf (str, "Maximum number of results: %d", maxResults);
1547  output = g_list_append (output, str);
1548 
1549  qof_query_printOutput (output);
1550  LEAVE (" ");
1551 }
1552 
1553 static void
1554 qof_query_printOutput (GList * output)
1555 {
1556  GList *lst;
1557 
1558  for (lst = output; lst; lst = lst->next)
1559  {
1560  GString *line = (GString *) lst->data;
1561 
1562  DEBUG (" %s", line->str);
1563  g_string_free (line, TRUE);
1564  line = nullptr;
1565  }
1566 }
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 qof_query_printSearchFor (QofQuery * query, GList * output)
1574 {
1575  QofIdType searchFor;
1576  GString *gs;
1577 
1578  searchFor = qof_query_get_search_for (query);
1579  gs = g_string_new ("Query Object Type: ");
1580  g_string_append (gs, (nullptr == searchFor) ? "(null)" : searchFor);
1581  output = g_list_append (output, gs);
1582 
1583  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 qof_query_printTerms (QofQuery * query, GList * output)
1593 {
1594 
1595  GList *terms, *lst;
1596 
1597  terms = qof_query_get_terms (query);
1598 
1599  for (lst = terms; lst; lst = lst->next)
1600  {
1601  output = g_list_append (output, g_string_new ("OR and AND Terms:"));
1602 
1603  if (lst->data)
1604  {
1605  output = qof_query_printAndTerms (static_cast<GList*>(lst->data),
1606  output);
1607  }
1608  else
1609  {
1610  output =
1611  g_list_append (output, g_string_new (" No data for AND terms"));
1612  }
1613  }
1614 
1615  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 qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
1625 {
1626  QofQueryParamList *gsl, *n = nullptr;
1627  gint curSort;
1628  GString *gs = g_string_new ("Sort Parameters: ");
1629 
1630  for (curSort = 0; curSort < numSorts; curSort++)
1631  {
1632  gboolean increasing;
1633  if (!s[curSort])
1634  {
1635  break;
1636  }
1637  increasing = qof_query_sort_get_increasing (s[curSort]);
1638 
1639  gsl = qof_query_sort_get_param_path (s[curSort]);
1640  if (gsl) g_string_append_printf (gs, " Param: ");
1641  for (n = gsl; n; n = n->next)
1642  {
1643  QofIdType param_name = static_cast<QofIdType>(n->data);
1644  if (gsl != n) g_string_append_printf (gs, " ");
1645  g_string_append_printf (gs, "%s", param_name);
1646  }
1647  if (gsl)
1648  {
1649  g_string_append_printf (gs, " %s ", increasing ? "DESC" : "ASC");
1650  g_string_append_printf (gs, " Options: 0x%x ", s[curSort]->options);
1651  }
1652  }
1653 
1654  output = g_list_append (output, gs);
1655  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 qof_query_printAndTerms (GList * terms, GList * output)
1665 {
1666  const char *prefix = "AND Terms:";
1667  QofQueryTerm *qt;
1668  QofQueryPredData *pd;
1669  QofQueryParamList *path;
1670  GList *lst;
1671  gboolean invert;
1672 
1673  output = g_list_append (output, g_string_new (prefix));
1674  for (lst = terms; lst; lst = lst->next)
1675  {
1676  qt = (QofQueryTerm *) lst->data;
1677  pd = qof_query_term_get_pred_data (qt);
1678  path = qof_query_term_get_param_path (qt);
1679  invert = qof_query_term_is_inverted (qt);
1680 
1681  if (invert) output = g_list_append (output,
1682  g_string_new(" INVERT SENSE "));
1683  output = g_list_append (output, qof_query_printParamPath (path));
1684  output = qof_query_printPredData (pd, output);
1685 // output = g_list_append (output, g_string_new(" "));
1686  }
1687 
1688  return output;
1689 } /* qof_query_printAndTerms */
1690 
1691 /*
1692  Process the parameter types of the predicate data
1693 */
1694 static GString *
1695 qof_query_printParamPath (QofQueryParamList * parmList)
1696 {
1697  QofQueryParamList *list = nullptr;
1698  GString *gs = g_string_new ("Param List: ");
1699  g_string_append (gs, " ");
1700  for (list = parmList; list; list = list->next)
1701  {
1702  g_string_append (gs, (gchar *) list->data);
1703  if (list->next)
1704  g_string_append (gs, "->");
1705  }
1706 
1707  return gs;
1708 } /* qof_query_printParamPath */
1709 
1710 /*
1711  Process the PredData of the AND terms
1712 */
1713 static GList *
1714 qof_query_printPredData (QofQueryPredData *pd, GList *lst)
1715 {
1716  GString *gs;
1717 
1718  gs = g_string_new ("Pred Data: ");
1719  g_string_append (gs, (gchar *) pd->type_name);
1720 
1721  /* Char Predicate and GncGUID predicate don't use the 'how' field. */
1722  if (g_strcmp0 (pd->type_name, QOF_TYPE_CHAR) &&
1723  g_strcmp0 (pd->type_name, QOF_TYPE_GUID))
1724  {
1725  g_string_append_printf (gs, " how: %s",
1726  qof_query_printStringForHow (pd->how));
1727  }
1728  lst = g_list_append(lst, gs);
1729  gs = g_string_new ("");
1730  qof_query_printValueForParam (pd, gs);
1731  lst = g_list_append(lst, gs);
1732  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 qof_query_printStringForHow (QofQueryCompare how)
1741 {
1742 
1743  switch (how)
1744  {
1745  case QOF_COMPARE_LT:
1746  return "QOF_COMPARE_LT";
1747  case QOF_COMPARE_LTE:
1748  return "QOF_COMPARE_LTE";
1749  case QOF_COMPARE_EQUAL:
1750  return "QOF_COMPARE_EQUAL";
1751  case QOF_COMPARE_GT:
1752  return "QOF_COMPARE_GT";
1753  case QOF_COMPARE_GTE:
1754  return "QOF_COMPARE_GTE";
1755  case QOF_COMPARE_NEQ:
1756  return "QOF_COMPARE_NEQ";
1757  case QOF_COMPARE_CONTAINS:
1758  return "QOF_COMPARE_CONTAINS";
1759  case QOF_COMPARE_NCONTAINS:
1760  return "QOF_COMPARE_NCONTAINS";
1761  }
1762 
1763  return "INVALID HOW";
1764 } /* qncQueryPrintStringForHow */
1765 
1766 
1767 static void
1768 qof_query_printValueForParam (QofQueryPredData *pd, GString * gs)
1769 {
1770 
1771  if (!g_strcmp0 (pd->type_name, QOF_TYPE_GUID))
1772  {
1773  GList *node;
1774  query_guid_t pdata = (query_guid_t) pd;
1775  g_string_append_printf (gs, "Match type %s",
1776  qof_query_printGuidMatch (pdata->options));
1777  for (node = pdata->guids; node; node = node->next)
1778  {
1779  gchar guidstr[GUID_ENCODING_LENGTH+1];
1780  guid_to_string_buff ((GncGUID *) node->data,guidstr);
1781  g_string_append_printf (gs, ", guids: %s",guidstr);
1782  }
1783  return;
1784  }
1785  if (!g_strcmp0 (pd->type_name, QOF_TYPE_STRING))
1786  {
1787  query_string_t pdata = (query_string_t) pd;
1788  g_string_append_printf (gs, " Match type %s",
1789  qof_query_printStringMatch (pdata->options));
1790  g_string_append_printf (gs, " %s string: %s",
1791  pdata->is_regex ? "Regex" : "Not regex",
1792  pdata->matchstring);
1793  return;
1794  }
1795  if (!g_strcmp0 (pd->type_name, QOF_TYPE_NUMERIC))
1796  {
1797  query_numeric_t pdata = (query_numeric_t) pd;
1798  g_string_append_printf (gs, " Match type %s",
1799  qof_query_printNumericMatch (pdata->options));
1800  g_string_append_printf (gs, " gnc_numeric: %s",
1801  gnc_num_dbg_to_string (pdata->amount));
1802  return;
1803  }
1804  if (!g_strcmp0 (pd->type_name, QOF_TYPE_INT64))
1805  {
1806  query_int64_t pdata = (query_int64_t) pd;
1807  g_string_append_printf (gs, " int64: %" G_GINT64_FORMAT, pdata->val);
1808  return;
1809  }
1810  if (!g_strcmp0 (pd->type_name, QOF_TYPE_INT32))
1811  {
1812  query_int32_t pdata = (query_int32_t) pd;
1813  g_string_append_printf (gs, " int32: %d", pdata->val);
1814  return;
1815  }
1816  if (!g_strcmp0 (pd->type_name, QOF_TYPE_DOUBLE))
1817  {
1818  query_double_t pdata = (query_double_t) pd;
1819  g_string_append_printf (gs, " double: %.18g", pdata->val);
1820  return;
1821  }
1822  if (!g_strcmp0 (pd->type_name, QOF_TYPE_DATE))
1823  {
1824  query_date_t pdata = (query_date_t) pd;
1825  char datebuff[MAX_DATE_LENGTH + 1];
1826  memset (datebuff, 0, sizeof(datebuff));
1827  qof_print_date_buff (datebuff, sizeof(datebuff), pdata->date);
1828  g_string_append_printf (gs, " Match type %s",
1829  qof_query_printDateMatch (pdata->options));
1830  g_string_append_printf (gs, " query_date: %s", datebuff);
1831  return;
1832  }
1833  if (!g_strcmp0 (pd->type_name, QOF_TYPE_CHAR))
1834  {
1835  query_char_t pdata = (query_char_t) pd;
1836  g_string_append_printf (gs, " Match type %s",
1837  qof_query_printCharMatch (pdata->options));
1838  g_string_append_printf (gs, " char list: %s", pdata->char_list);
1839  return;
1840  }
1841  if (!g_strcmp0 (pd->type_name, QOF_TYPE_BOOLEAN))
1842  {
1843  query_boolean_t pdata = (query_boolean_t) pd;
1844  g_string_append_printf (gs, " boolean: %s", pdata->val ? "TRUE" : "FALSE");
1845  return;
1846  }
1848  return;
1849 } /* qof_query_printValueForParam */
1850 
1851 /*
1852  * Print out a string representation of the
1853  * QofStringMatch enum
1854  */
1855 static const char *
1856 qof_query_printStringMatch (QofStringMatch s)
1857 {
1858  switch (s)
1859  {
1860  case QOF_STRING_MATCH_NORMAL:
1861  return "QOF_STRING_MATCH_NORMAL";
1862  case QOF_STRING_MATCH_CASEINSENSITIVE:
1863  return "QOF_STRING_MATCH_CASEINSENSITIVE";
1864  }
1865  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 qof_query_printDateMatch (QofDateMatch d)
1874 {
1875  switch (d)
1876  {
1877  case QOF_DATE_MATCH_NORMAL:
1878  return "QOF_DATE_MATCH_NORMAL";
1879  case QOF_DATE_MATCH_DAY:
1880  return "QOF_DATE_MATCH_DAY";
1881  }
1882  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 qof_query_printNumericMatch (QofNumericMatch n)
1891 {
1892  switch (n)
1893  {
1894  case QOF_NUMERIC_MATCH_DEBIT:
1895  return "QOF_NUMERIC_MATCH_DEBIT";
1896  case QOF_NUMERIC_MATCH_CREDIT:
1897  return "QOF_NUMERIC_MATCH_CREDIT";
1898  case QOF_NUMERIC_MATCH_ANY:
1899  return "QOF_NUMERIC_MATCH_ANY";
1900  }
1901  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 qof_query_printGuidMatch (QofGuidMatch g)
1910 {
1911  switch (g)
1912  {
1913  case QOF_GUID_MATCH_ANY:
1914  return "QOF_GUID_MATCH_ANY";
1915  case QOF_GUID_MATCH_ALL:
1916  return "QOF_GUID_MATCH_ALL";
1917  case QOF_GUID_MATCH_NONE:
1918  return "QOF_GUID_MATCH_NONE";
1919  case QOF_GUID_MATCH_NULL:
1920  return "QOF_GUID_MATCH_NULL";
1922  return "QOF_GUID_MATCH_LIST_ANY";
1923  }
1924 
1925  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 qof_query_printCharMatch (QofCharMatch c)
1934 {
1935  switch (c)
1936  {
1937  case QOF_CHAR_MATCH_ANY:
1938  return "QOF_CHAR_MATCH_ANY";
1939  case QOF_CHAR_MATCH_NONE:
1940  return "QOF_CHAR_MATCH_NONE";
1941  }
1942  return "UNKNOWN MATCH TYPE";
1943 } /* qof_query_printGuidMatch */
1944 
1945 /* ======================== END OF FILE =================== */
void qof_query_add_term(QofQuery *q, QofQueryParamList *param_list, QofQueryPredData *pred_data, QofQueryOp op)
This is the general function that adds a new Query Term to a query.
Definition: qofquery.cpp:681
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void qof_query_core_predicate_free(QofQueryPredData *pdata)
Destroy a predicate.
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
When a query is run, the results are sorted before being returned.
Definition: qofquery.cpp:1249
void qof_query_purge_terms(QofQuery *q, QofQueryParamList *param_list)
Remove query terms of a particular type from q.
Definition: qofquery.cpp:705
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition: qofid.h:82
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void(* QofInstanceForeachCB)(QofInstance *, gpointer user_data)
Callback type for qof_collection_foreach.
Definition: qofid.h:146
QofQuery * qof_query_copy(QofQuery *q)
Make a copy of the indicated query.
Definition: qofquery.cpp:1018
gboolean qof_query_equal(const QofQuery *q1, const QofQuery *q2)
Compare two queries for equality.
Definition: qofquery.cpp:1472
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
QofStringMatch
List of known core query data-types...
Definition: qofquerycore.h:70
int(* QofSortFunc)(gconstpointer, gconstpointer)
This function is the default sort function for a particular object type.
Definition: qofclass.h:223
void qof_query_set_sort_increasing(QofQuery *q, gboolean prim_inc, gboolean sec_inc, gboolean tert_inc)
When a query is run, the results are sorted before being returned.
Definition: qofquery.cpp:1280
These expect a single object and expect the QofAccessFunc returns GncGUID*.
Definition: qofquerycore.h:113
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
#define QOF_PARAM_BOOK
"Known" Object Parameters – all objects must support these
Definition: qofquery.h:108
GSList QofQueryParamList
A list of parameters (QofIdType) used to describe a parameter to use in a predicate or when sorting...
Definition: qofquerycore.h:151
void qof_query_set_max_results(QofQuery *q, int n)
Set the maximum number of results that should be returned.
Definition: qofquery.cpp:1289
gboolean qof_log_check(QofLogModule domain, QofLogLevel level)
Check to see if the given log_module is configured to log at the given log_level. ...
Definition: qoflog.cpp:324
const gchar * QofIdType
QofIdType declaration.
Definition: qofid.h:80
int qof_query_num_terms(QofQuery *q)
Return the number of terms in the canonical form of the query.
Definition: qofquery.cpp:954
const QofParam * qof_class_get_parameter(QofIdTypeConst obj_name, const char *parameter)
Return the registered Parameter Definition for the requested parameter.
Definition: qofclass.cpp:136
GList * qof_query_run_subquery(QofQuery *subq, const QofQuery *primaryq)
Perform a subquery, return the results.
Definition: qofquery.cpp:880
void qof_query_destroy(QofQuery *q)
Frees the resources associate with a Query object.
Definition: qofquery.cpp:1009
QofGuidMatch
Definition: qofquerycore.h:109
void qof_query_init(void)
Subsystem initialization and shutdown.
Definition: qofquery.cpp:1360
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
void qof_query_set_book(QofQuery *q, QofBook *book)
Set the book to be searched.
Definition: qofquery.cpp:1326
void qof_object_foreach(QofIdTypeConst type_name, QofBook *book, QofInstanceForeachCB cb, gpointer user_data)
Invoke the callback &#39;cb&#39; on every instance ov a particular object type.
Definition: qofobject.cpp:185
QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Combine two queries together using the Boolean set (logical) operator &#39;op&#39;.
Definition: qofquery.cpp:1129
QofQueryCompare
Standard Query comparators, for how to compare objects in a predicate.
Definition: qofquerycore.h:54
QofCharMatch
A CHAR type is for a RECNCell, Comparisons for QOF_TYPE_CHAR &#39;ANY&#39; will match any character in the st...
Definition: qofquerycore.h:132
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
These expect a single object and expect the QofAccessFunc function to return a GList* of GncGUID* (th...
Definition: qofquerycore.h:121
QofQuery * qof_query_invert(QofQuery *q)
Make a copy of the indicated query, inverting the sense of the search.
Definition: qofquery.cpp:1050
void qof_query_clear(QofQuery *query)
Remove all query terms from query.
Definition: qofquery.cpp:905
gboolean qof_query_has_term_type(QofQuery *q, QofQueryParamList *term_param)
DOCUMENT ME !!
Definition: qofquery.cpp:964
These expect a GList* of objects and calls the QofAccessFunc routine on each item in the list to obta...
Definition: qofquerycore.h:118
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
DOCUMENT ME !!
Definition: qofquery.cpp:1310
GList * qof_query_last_run(QofQuery *query)
Return the results of the last query, without causing the query to be re-run.
Definition: qofquery.cpp:897
QofQueryOp
Query Term Operators, for combining Query Terms.
Definition: qofquery.h:92
GList * qof_query_run(QofQuery *q)
Perform the query, return the results.
Definition: qofquery.cpp:865
QofIdType qof_query_get_search_for(const QofQuery *q)
Return the type of data we&#39;re querying for.
Definition: qofquery.cpp:1380
QofDateMatch
Comparisons for QOF_TYPE_DATE The QOF_DATE_MATCH_DAY comparison rounds the two time values to mid-day...
Definition: qofquerycore.h:83
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
#define QUERY_DEFAULT_SORT
Default sort object type.
Definition: qofquery.h:105
int qof_query_has_terms(QofQuery *q)
Return boolean FALSE if there are no terms in the query Can be used as a predicate to see if the quer...
Definition: qofquery.cpp:948
void qof_query_add_boolean_match(QofQuery *q, QofQueryParamList *param_list, gboolean value, QofQueryOp op)
Handy-dandy convenience routines, avoids having to create a separate predicate for boolean matches...
Definition: qofquery.cpp:1347
QofQueryPredData * qof_query_core_predicate_copy(const QofQueryPredData *pdata)
Copy a predicate.
The type used to store guids in C.
Definition: guid.h:75
QofNumericMatch
Comparisons for QOF_TYPE_NUMERIC, QOF_TYPE_DEBCRED.
Definition: qofquerycore.h:101
QofQuery * qof_query_create(void)
Create a new query.
Definition: qofquery.cpp:918
A Query.
Definition: qofquery.cpp:74
void qof_query_add_guid_list_match(QofQuery *q, QofQueryParamList *param_list, GList *guid_list, QofGuidMatch options, QofQueryOp op)
DOCUMENT ME !!
Definition: qofquery.cpp:1295
void qof_query_search_for(QofQuery *q, QofIdTypeConst obj_type)
Set the object type to be searched for.
Definition: qofquery.cpp:926
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:572
GList * qof_query_get_books(QofQuery *q)
Return the list of books we&#39;re using.
Definition: qofquery.cpp:1341
void qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Like qof_query_merge, but this will merge a copy of q2 into q1.
Definition: qofquery.cpp:1236