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