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
query_init(QofQuery * q,QofQueryTerm * initial_term)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
swap_terms(QofQuery * q1,QofQuery * q2)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
free_query_term(QofQueryTerm * qt)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 *
copy_query_term(QofQueryTerm * qt)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 *
copy_and_terms(GList * and_terms)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 *
copy_or_terms(GList * or_terms)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
copy_sort(QofQuerySort * dst,const QofQuerySort * src)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
free_sort(QofQuerySort * s)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
free_members(QofQuery * q)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
cmp_func(QofQuerySort * sort,QofSortFunc default_sort,gconstpointer a,gconstpointer b)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
sort_func(gconstpointer a,gconstpointer b)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
check_object(QofQuery * q,gpointer object)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 *
compile_params(GSList * param_list,QofIdType start_obj,QofParam const ** final)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
compile_sort(QofQuerySort * sort,QofIdType obj)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
compile_terms(QofQuery * q)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
check_item_cb(gpointer object,gpointer user_data)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
param_list_cmp(GSList * l1,GSList * l2)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 *
merge_books(GList * l1,GList * l2)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
query_free_compiled(gpointer key,gpointer value,gpointer user_data)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
query_clear_compiles(QofQuery * q)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 *
qof_query_build_param_list(gchar const * param,...)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
qof_query_add_term(QofQuery * q,GSList * param_list,QofQueryPredData * pred_data,QofQueryOp op)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
qof_query_purge_terms(QofQuery * q,GSList * param_list)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 *
qof_query_run(QofQuery * q)757 qof_query_run (QofQuery * q)
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 *
qof_query_last_run(QofQuery * query)877 qof_query_last_run (QofQuery * query)
878 {
879 if (!query)
880 return NULL;
881
882 return query->results;
883 }
884
885 void
qof_query_clear(QofQuery * query)886 qof_query_clear (QofQuery * query)
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 *
qof_query_create(void)900 qof_query_create (void)
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
qof_query_search_for(QofQuery * q,QofIdTypeConst obj_type)909 qof_query_search_for (QofQuery * q, QofIdTypeConst obj_type)
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 *
qof_query_create_for(QofIdTypeConst obj_type)922 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 gint
qof_query_has_terms(QofQuery * q)933 qof_query_has_terms (QofQuery * q)
934 {
935 if (!q)
936 return 0;
937 return g_list_length (q->terms);
938 }
939
940 gint
qof_query_num_terms(QofQuery * q)941 qof_query_num_terms (QofQuery * q)
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
qof_query_has_term_type(QofQuery * q,GSList * term_param)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 *
qof_query_get_term_type(QofQuery * q,GSList * term_param)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
qof_query_destroy(QofQuery * q)998 qof_query_destroy (QofQuery * q)
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 *
qof_query_copy(QofQuery * q)1009 qof_query_copy (QofQuery * q)
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 *
qof_query_invert(QofQuery * q)1043 qof_query_invert (QofQuery * q)
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 *
qof_query_merge(QofQuery * q1,QofQuery * q2,QofQueryOp op)1127 qof_query_merge (QofQuery * q1, QofQuery * q2, QofQueryOp op)
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
qof_query_merge_in_place(QofQuery * q1,QofQuery * q2,QofQueryOp op)1234 qof_query_merge_in_place (QofQuery * q1, QofQuery * q2, QofQueryOp op)
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
qof_query_set_sort_order(QofQuery * q,GSList * params1,GSList * params2,GSList * params3)1247 qof_query_set_sort_order (QofQuery * q,
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
qof_query_set_sort_options(QofQuery * q,gint prim_op,gint sec_op,gint tert_op)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
qof_query_set_sort_increasing(QofQuery * q,gboolean prim_inc,gboolean sec_inc,gboolean tert_inc)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
qof_query_set_max_results(QofQuery * q,gint n)1293 qof_query_set_max_results (QofQuery * q, gint n)
1294 {
1295 if (!q)
1296 return;
1297 q->max_results = n;
1298 }
1299
1300 void
qof_query_add_guid_list_match(QofQuery * q,GSList * param_list,GList * guid_list,QofGuidMatch options,QofQueryOp op)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
qof_query_add_guid_match(QofQuery * q,GSList * param_list,const GUID * guid,QofQueryOp op)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
qof_query_set_book(QofQuery * q,QofBook * book)1335 qof_query_set_book (QofQuery * q, QofBook * book)
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 *
qof_query_get_books(QofQuery * q)1352 qof_query_get_books (QofQuery * q)
1353 {
1354 if (!q)
1355 return NULL;
1356 return q->books;
1357 }
1358
1359 void
qof_query_add_boolean_match(QofQuery * q,GSList * param_list,gboolean value,QofQueryOp op)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
qof_query_init(void)1375 qof_query_init (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
qof_query_shutdown(void)1385 qof_query_shutdown (void)
1386 {
1387 qof_class_shutdown ();
1388 qof_query_core_shutdown ();
1389 }
1390
1391 gint
qof_query_get_max_results(QofQuery * q)1392 qof_query_get_max_results (QofQuery * q)
1393 {
1394 if (!q)
1395 return 0;
1396 return q->max_results;
1397 }
1398
1399 QofIdType
qof_query_get_search_for(QofQuery * q)1400 qof_query_get_search_for (QofQuery * q)
1401 {
1402 if (!q)
1403 return NULL;
1404 return q->search_for;
1405 }
1406
1407 GList *
qof_query_get_terms(QofQuery * q)1408 qof_query_get_terms (QofQuery * q)
1409 {
1410 if (!q)
1411 return NULL;
1412 return q->terms;
1413 }
1414
1415 GSList *
qof_query_term_get_param_path(QofQueryTerm * qt)1416 qof_query_term_get_param_path (QofQueryTerm * qt)
1417 {
1418 if (!qt)
1419 return NULL;
1420 return qt->param_list;
1421 }
1422
1423 QofQueryPredData *
qof_query_term_get_pred_data(QofQueryTerm * qt)1424 qof_query_term_get_pred_data (QofQueryTerm * qt)
1425 {
1426 if (!qt)
1427 return NULL;
1428 return qt->pdata;
1429 }
1430
1431 gboolean
qof_query_term_is_inverted(QofQueryTerm * qt)1432 qof_query_term_is_inverted (QofQueryTerm * qt)
1433 {
1434 if (!qt)
1435 return FALSE;
1436 return qt->invert;
1437 }
1438
1439 void
qof_query_get_sorts(QofQuery * q,QofQuerySort ** primary,QofQuerySort ** secondary,QofQuerySort ** tertiary)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 *
qof_query_sort_get_param_path(QofQuerySort * qs)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
qof_query_sort_get_sort_options(QofQuerySort * qs)1462 qof_query_sort_get_sort_options (QofQuerySort * qs)
1463 {
1464 if (!qs)
1465 return 0;
1466 return qs->options;
1467 }
1468
1469 gboolean
qof_query_sort_get_increasing(QofQuerySort * qs)1470 qof_query_sort_get_increasing (QofQuerySort * qs)
1471 {
1472 if (!qs)
1473 return FALSE;
1474 return qs->increasing;
1475 }
1476
1477 static gboolean
qof_query_term_equal(QofQueryTerm * qt1,QofQueryTerm * qt2)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
qof_query_sort_equal(QofQuerySort * qs1,QofQuerySort * qs2)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
qof_query_equal(QofQuery * q1,QofQuery * q2)1512 qof_query_equal (QofQuery * q1, QofQuery * q2)
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
1575 /** \deprecated this will be private in libqof2. */
1576 void
qof_query_print(QofQuery * query)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
qof_query_printOutput(GList * output)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 *
qof_query_printSearchFor(QofQuery * query,GList * output)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 *
qof_query_printTerms(QofQuery * query,GList * output)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 *
qof_query_printSorts(QofQuerySort * s[],const gint numSorts,GList * output)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 *
qof_query_printAndTerms(GList * terms,GList * output)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 *
qof_query_printParamPath(GSList * parmList)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 *
qof_query_printPredData(QofQueryPredData * pd,GList * lst)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 *
qof_query_printStringForHow(QofQueryCompare how)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
qof_query_printValueForParam(QofQueryPredData * pd,GString * gs)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",
1902 qof_date_print (qd, QOF_DATE_FORMAT_UTC));
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 }
1921 /** \todo QOF_TYPE_COLLECT */
1922 return;
1923 } /* qof_query_printValueForParam */
1924
1925 /*
1926 * Print out a string representation of the
1927 * QofStringMatch enum
1928 */
1929 static gchar *
qof_query_printStringMatch(QofStringMatch s)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 *
qof_query_printDateMatch(QofDateMatch d)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 *
qof_query_printNumericMatch(QofNumericMatch n)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 *
qof_query_printGuidMatch(QofGuidMatch g)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 *
qof_query_printCharMatch(QofCharMatch c)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 =================== */
2006