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