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