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