PageRenderTime 55ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/libqof/qof/qofquery.c

http://github.com/mchochlov/Gnucash
C | 1934 lines | 1404 code | 314 blank | 216 comment | 244 complexity | f442f31e42307f279aee901e164bc12b MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  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 "config.h"
  24. #include <sys/types.h>
  25. #include <time.h>
  26. #include <glib.h>
  27. #include <regex.h>
  28. #include <string.h>
  29. #include "qof.h"
  30. #include "qofbackend-p.h"
  31. #include "qofbook-p.h"
  32. #include "qofclass-p.h"
  33. #include "qofquery-p.h"
  34. #include "qofquerycore-p.h"
  35. static QofLogModule log_module = QOF_MOD_QUERY;
  36. struct _QofQueryTerm
  37. {
  38. QofQueryParamList * param_list;
  39. QofQueryPredData *pdata;
  40. gboolean invert;
  41. /* These values are filled in during "compilation" of the query
  42. * term, based upon the obj_name, param_name, and searched-for
  43. * object type. If conv_fcn is NULL, then we don't know how to
  44. * convert types.
  45. */
  46. GSList * param_fcns;
  47. QofQueryPredicateFunc pred_fcn;
  48. };
  49. struct _QofQuerySort
  50. {
  51. QofQueryParamList * param_list;
  52. gint options;
  53. gboolean increasing;
  54. /* These values are filled in during "compilation" of the query
  55. * term, based upon the obj_name, param_name, and searched-for
  56. * object type. If conv_fcn is NULL, then we don't know how to
  57. * convert types.
  58. */
  59. gboolean use_default;
  60. GSList * param_fcns; /* Chain of paramters to walk */
  61. QofSortFunc obj_cmp; /* In case you are comparing objects */
  62. QofCompareFunc comp_fcn; /* When you are comparing core types */
  63. };
  64. /* The QUERY structure */
  65. struct _QofQuery
  66. {
  67. /* The object type that we're searching for */
  68. QofIdType search_for;
  69. /* terms is a list of the OR-terms in a sum-of-products
  70. * logical expression. */
  71. GList * terms;
  72. /* sorting and chopping is independent of the search filter */
  73. QofQuerySort primary_sort;
  74. QofQuerySort secondary_sort;
  75. QofQuerySort tertiary_sort;
  76. QofSortFunc defaultSort; /* <- Computed from search_for */
  77. /* The maximum number of results to return */
  78. gint max_results;
  79. /* list of books that will be participating in the query */
  80. GList * books;
  81. /* a map of book to backend-compiled queries */
  82. GHashTable* be_compiled;
  83. /* cache the results so we don't have to run the whole search
  84. * again until it's really necessary */
  85. gint changed;
  86. GList * results;
  87. };
  88. typedef struct _QofQueryCB
  89. {
  90. QofQuery * query;
  91. GList * list;
  92. gint count;
  93. } QofQueryCB;
  94. /* initial_term will be owned by the new Query */
  95. static void query_init (QofQuery *q, QofQueryTerm *initial_term)
  96. {
  97. GList * or = NULL;
  98. GList *and = NULL;
  99. GHashTable *ht;
  100. if (initial_term)
  101. {
  102. or = g_list_alloc ();
  103. and = g_list_alloc ();
  104. and->data = initial_term;
  105. or->data = and;
  106. }
  107. if (q->terms)
  108. qof_query_clear (q);
  109. g_list_free (q->results);
  110. g_list_free (q->books);
  111. g_slist_free (q->primary_sort.param_list);
  112. g_slist_free (q->secondary_sort.param_list);
  113. g_slist_free (q->tertiary_sort.param_list);
  114. g_slist_free (q->primary_sort.param_fcns);
  115. g_slist_free (q->secondary_sort.param_fcns);
  116. g_slist_free (q->tertiary_sort.param_fcns);
  117. ht = q->be_compiled;
  118. memset (q, 0, sizeof (*q));
  119. q->be_compiled = ht;
  120. q->terms = or;
  121. q->changed = 1;
  122. q->max_results = -1;
  123. q->primary_sort.param_list = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
  124. q->primary_sort.increasing = TRUE;
  125. q->secondary_sort.increasing = TRUE;
  126. q->tertiary_sort.increasing = TRUE;
  127. }
  128. static void swap_terms (QofQuery *q1, QofQuery *q2)
  129. {
  130. GList *g;
  131. if (!q1 || !q2) return;
  132. g = q1->terms;
  133. q1->terms = q2->terms;
  134. q2->terms = g;
  135. g = q1->books;
  136. q1->books = q2->books;
  137. q2->books = g;
  138. q1->changed = 1;
  139. q2->changed = 1;
  140. }
  141. static void free_query_term (QofQueryTerm *qt)
  142. {
  143. if (!qt) return;
  144. qof_query_core_predicate_free (qt->pdata);
  145. g_slist_free (qt->param_list);
  146. g_slist_free (qt->param_fcns);
  147. g_free (qt);
  148. }
  149. static QofQueryTerm * copy_query_term (const QofQueryTerm *qt)
  150. {
  151. QofQueryTerm *new_qt;
  152. if (!qt) return NULL;
  153. new_qt = g_new0 (QofQueryTerm, 1);
  154. memcpy (new_qt, qt, sizeof(QofQueryTerm));
  155. new_qt->param_list = g_slist_copy (qt->param_list);
  156. new_qt->param_fcns = g_slist_copy (qt->param_fcns);
  157. new_qt->pdata = qof_query_core_predicate_copy (qt->pdata);
  158. return new_qt;
  159. }
  160. static GList * copy_and_terms (const GList *and_terms)
  161. {
  162. GList *and = NULL;
  163. const GList *cur_and;
  164. for (cur_and = and_terms; cur_and; cur_and = cur_and->next)
  165. {
  166. and = g_list_prepend(and, copy_query_term (cur_and->data));
  167. }
  168. return g_list_reverse(and);
  169. }
  170. static GList *
  171. copy_or_terms(const GList * or_terms)
  172. {
  173. GList * or = NULL;
  174. const GList * cur_or;
  175. for (cur_or = or_terms; cur_or; cur_or = cur_or->next)
  176. {
  177. or = g_list_prepend(or, copy_and_terms(cur_or->data));
  178. }
  179. return g_list_reverse(or);
  180. }
  181. static void copy_sort (QofQuerySort *dst, const QofQuerySort *src)
  182. {
  183. memcpy (dst, src, sizeof (*dst));
  184. dst->param_list = g_slist_copy (src->param_list);
  185. dst->param_fcns = g_slist_copy (src->param_fcns);
  186. }
  187. static void free_sort (QofQuerySort *s)
  188. {
  189. g_slist_free (s->param_list);
  190. s->param_list = NULL;
  191. g_slist_free (s->param_fcns);
  192. s->param_fcns = NULL;
  193. }
  194. static void free_members (QofQuery *q)
  195. {
  196. GList * cur_or;
  197. if (q == NULL) return;
  198. for (cur_or = q->terms; cur_or; cur_or = cur_or->next)
  199. {
  200. GList * cur_and;
  201. for (cur_and = cur_or->data; cur_and; cur_and = cur_and->next)
  202. {
  203. free_query_term(cur_and->data);
  204. cur_and->data = NULL;
  205. }
  206. g_list_free(cur_or->data);
  207. cur_or->data = NULL;
  208. }
  209. free_sort (&(q->primary_sort));
  210. free_sort (&(q->secondary_sort));
  211. free_sort (&(q->tertiary_sort));
  212. g_list_free(q->terms);
  213. q->terms = NULL;
  214. g_list_free(q->books);
  215. q->books = NULL;
  216. g_list_free(q->results);
  217. q->results = NULL;
  218. }
  219. static int cmp_func (const QofQuerySort *sort, QofSortFunc default_sort,
  220. const gconstpointer a, const gconstpointer b)
  221. {
  222. QofParam *param = NULL;
  223. GSList *node;
  224. gpointer conva, convb;
  225. g_return_val_if_fail (sort, 0);
  226. /* See if this is a default sort */
  227. if (sort->use_default)
  228. {
  229. if (default_sort) return default_sort (a, b);
  230. return 0;
  231. }
  232. /* If no parameters, consider them equal */
  233. if (!sort->param_fcns) return 0;
  234. /* no compare function, consider the two objects equal */
  235. if (!sort->comp_fcn && !sort->obj_cmp) return 0;
  236. /* Do the list of conversions */
  237. conva = (gpointer)a;
  238. convb = (gpointer)b;
  239. for (node = sort->param_fcns; node; node = node->next)
  240. {
  241. param = node->data;
  242. /* The last term is really the "parameter getter",
  243. * unless we're comparing objects ;) */
  244. if (!node->next && !sort->obj_cmp)
  245. break;
  246. /* Do the converstions */
  247. conva = (param->param_getfcn) (conva, param);
  248. convb = (param->param_getfcn) (convb, param);
  249. }
  250. /* And now return the (appropriate) compare */
  251. if (sort->comp_fcn)
  252. {
  253. int rc = sort->comp_fcn (conva, convb, sort->options, param);
  254. return rc;
  255. }
  256. return sort->obj_cmp (conva, convb);
  257. }
  258. static int sort_func (const gconstpointer a, const gconstpointer b, const gpointer q)
  259. {
  260. int retval;
  261. const QofQuery *sortQuery = q;
  262. g_return_val_if_fail (sortQuery, 0);
  263. retval = cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, b);
  264. if (retval == 0)
  265. {
  266. retval = cmp_func (&(sortQuery->secondary_sort), sortQuery->defaultSort,
  267. a, b);
  268. if (retval == 0)
  269. {
  270. retval = cmp_func (&(sortQuery->tertiary_sort), sortQuery->defaultSort,
  271. a, b);
  272. return sortQuery->tertiary_sort.increasing ? retval : -retval;
  273. }
  274. else
  275. {
  276. return sortQuery->secondary_sort.increasing ? retval : -retval;
  277. }
  278. }
  279. else
  280. {
  281. return sortQuery->primary_sort.increasing ? retval : -retval;
  282. }
  283. }
  284. /* ==================================================================== */
  285. /* This is the main workhorse for performing the query. For each
  286. * object, it walks over all of the query terms to see if the
  287. * object passes the seive.
  288. */
  289. static int
  290. check_object (const QofQuery *q, gpointer object)
  291. {
  292. const GList * and_ptr;
  293. const GList * or_ptr;
  294. const QofQueryTerm * qt;
  295. int and_terms_ok = 1;
  296. for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
  297. {
  298. and_terms_ok = 1;
  299. for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next)
  300. {
  301. qt = (QofQueryTerm *)(and_ptr->data);
  302. if (qt->param_fcns && qt->pred_fcn)
  303. {
  304. const GSList *node;
  305. QofParam *param = NULL;
  306. gpointer conv_obj = object;
  307. /* iterate through the conversions */
  308. for (node = qt->param_fcns; node; node = node->next)
  309. {
  310. param = node->data;
  311. /* The last term is the actual parameter getter */
  312. if (!node->next) break;
  313. conv_obj = param->param_getfcn (conv_obj, param);
  314. }
  315. if (((qt->pred_fcn)(conv_obj, param, qt->pdata)) == qt->invert)
  316. {
  317. and_terms_ok = 0;
  318. break;
  319. }
  320. }
  321. else
  322. {
  323. /* XXX: Don't know how to do this conversion -- do we care? */
  324. }
  325. }
  326. if (and_terms_ok)
  327. {
  328. return 1;
  329. }
  330. }
  331. /* If there are no terms, assume a "match any" applies.
  332. * A query with no terms is still meaningful, since the user
  333. * may want to get all objects, but in a particular sorted
  334. * order.
  335. */
  336. if (NULL == q->terms) return 1;
  337. return 0;
  338. }
  339. /* walk the list of parameters, starting with the given object, and
  340. * compile the list of parameter get-functions. Save the last valid
  341. * parameter definition in "final" and return the list of functions.
  342. *
  343. * returns NULL if the first parameter is bad (and final is unchanged).
  344. */
  345. static GSList *
  346. compile_params (QofQueryParamList *param_list, QofIdType start_obj,
  347. QofParam const **final)
  348. {
  349. const QofParam *objDef = NULL;
  350. GSList *fcns = NULL;
  351. ENTER ("param_list=%p id=%s", param_list, start_obj);
  352. g_return_val_if_fail (param_list, NULL);
  353. g_return_val_if_fail (start_obj, NULL);
  354. g_return_val_if_fail (final, NULL);
  355. for (; param_list; param_list = param_list->next)
  356. {
  357. QofIdType param_name = param_list->data;
  358. objDef = qof_class_get_parameter (start_obj, param_name);
  359. /* If it doesn't exist, then we've reached the end */
  360. if (!objDef) break;
  361. /* Save off this parameter */
  362. fcns = g_slist_prepend (fcns, (gpointer) objDef);
  363. /* Save this off, just in case */
  364. *final = objDef;
  365. /* And reset for the next parameter */
  366. start_obj = (QofIdType) objDef->param_type;
  367. }
  368. LEAVE ("fcns=%p", fcns);
  369. return (g_slist_reverse (fcns));
  370. }
  371. static void
  372. compile_sort (QofQuerySort *sort, QofIdType obj)
  373. {
  374. const QofParam *resObj = NULL;
  375. ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list);
  376. sort->use_default = FALSE;
  377. g_slist_free (sort->param_fcns);
  378. sort->param_fcns = NULL;
  379. sort->comp_fcn = NULL;
  380. sort->obj_cmp = NULL;
  381. /* An empty param_list implies "no sort" */
  382. if (!sort->param_list)
  383. {
  384. LEAVE (" ");
  385. return;
  386. }
  387. /* Walk the parameter list of obtain the parameter functions */
  388. sort->param_fcns = compile_params (sort->param_list, obj, &resObj);
  389. /* If we have valid parameters, grab the compare function,
  390. * If not, check if this is the default sort.
  391. */
  392. if (sort->param_fcns)
  393. {
  394. /* First, check if this parameter has a sort function override.
  395. * if not then check if there's a global compare function for the type
  396. */
  397. if (resObj->param_compfcn)
  398. sort->comp_fcn = resObj->param_compfcn;
  399. else
  400. sort->comp_fcn = qof_query_core_get_compare (resObj->param_type);
  401. /* Next, perhaps this is an object compare, not a core type compare? */
  402. if (sort->comp_fcn == NULL)
  403. sort->obj_cmp = qof_class_get_default_sort (resObj->param_type);
  404. }
  405. else if (!safe_strcmp (sort->param_list->data, QUERY_DEFAULT_SORT))
  406. {
  407. sort->use_default = TRUE;
  408. }
  409. LEAVE ("sort=%p id=%s", sort, obj);
  410. }
  411. static void compile_terms (QofQuery *q)
  412. {
  413. GList *or_ptr, *and_ptr, *node;
  414. ENTER (" query=%p", q);
  415. /* Find the specific functions for this Query. Note that the
  416. * Query's search_for should now be set to the new type.
  417. */
  418. for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next)
  419. {
  420. for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next)
  421. {
  422. QofQueryTerm *qt = and_ptr->data;
  423. const QofParam *resObj = NULL;
  424. g_slist_free (qt->param_fcns);
  425. qt->param_fcns = NULL;
  426. /* Walk the parameter list of obtain the parameter functions */
  427. qt->param_fcns = compile_params (qt->param_list, q->search_for,
  428. &resObj);
  429. /* If we have valid parameters, grab the predicate function,
  430. * If not, see if this is the default sort.
  431. */
  432. if (qt->param_fcns)
  433. qt->pred_fcn = qof_query_core_get_predicate (resObj->param_type);
  434. else
  435. qt->pred_fcn = NULL;
  436. }
  437. }
  438. /* Update the sort functions */
  439. compile_sort (&(q->primary_sort), q->search_for);
  440. compile_sort (&(q->secondary_sort), q->search_for);
  441. compile_sort (&(q->tertiary_sort), q->search_for);
  442. q->defaultSort = qof_class_get_default_sort (q->search_for);
  443. /* Now compile the backend instances */
  444. for (node = q->books; node; node = node->next)
  445. {
  446. QofBook *book = node->data;
  447. QofBackend *be = book->backend;
  448. if (be && be->compile_query)
  449. {
  450. gpointer result = (be->compile_query)(be, q);
  451. if (result)
  452. g_hash_table_insert (q->be_compiled, book, result);
  453. }
  454. }
  455. LEAVE (" query=%p", q);
  456. }
  457. static void check_item_cb (gpointer object, gpointer user_data)
  458. {
  459. QofQueryCB *ql = user_data;
  460. if (!object || !ql) return;
  461. if (check_object (ql->query, object))
  462. {
  463. ql->list = g_list_prepend (ql->list, object);
  464. ql->count++;
  465. }
  466. return;
  467. }
  468. static int param_list_cmp (const QofQueryParamList *l1, const QofQueryParamList *l2)
  469. {
  470. while (1)
  471. {
  472. int ret;
  473. /* Check the easy stuff */
  474. if (!l1 && !l2) return 0;
  475. if (!l1 && l2) return -1;
  476. if (l1 && !l2) return 1;
  477. ret = safe_strcmp (l1->data, l2->data);
  478. if (ret)
  479. return ret;
  480. l1 = l1->next;
  481. l2 = l2->next;
  482. }
  483. }
  484. static GList * merge_books (GList *l1, GList *l2)
  485. {
  486. GList *res = NULL;
  487. GList *node;
  488. res = g_list_copy (l1);
  489. for (node = l2; node; node = node->next)
  490. {
  491. if (g_list_index (res, node->data) == -1)
  492. res = g_list_prepend (res, node->data);
  493. }
  494. return res;
  495. }
  496. static gboolean
  497. query_free_compiled (gpointer key, gpointer value, gpointer not_used)
  498. {
  499. QofBook* book = key;
  500. QofBackend* be = book->backend;
  501. if (be && be->free_query)
  502. (be->free_query)(be, value);
  503. return TRUE;
  504. }
  505. /* clear out any cached query_compilations */
  506. static void query_clear_compiles (QofQuery *q)
  507. {
  508. g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, NULL);
  509. }
  510. /********************************************************************/
  511. /* PUBLISHED API FUNCTIONS */
  512. QofQueryParamList *
  513. qof_query_build_param_list (char const *param, ...)
  514. {
  515. QofQueryParamList *param_list = NULL;
  516. char const *this_param;
  517. va_list ap;
  518. if (!param)
  519. return NULL;
  520. va_start (ap, param);
  521. for (this_param = param; this_param; this_param = va_arg (ap, const char *))
  522. param_list = g_slist_prepend (param_list, (gpointer)this_param);
  523. va_end (ap);
  524. return g_slist_reverse (param_list);
  525. }
  526. void qof_query_add_term (QofQuery *q, QofQueryParamList *param_list,
  527. QofQueryPredData *pred_data, QofQueryOp op)
  528. {
  529. QofQueryTerm *qt;
  530. QofQuery *qr, *qs;
  531. if (!q || !param_list || !pred_data) return;
  532. qt = g_new0 (QofQueryTerm, 1);
  533. qt->param_list = param_list;
  534. qt->pdata = pred_data;
  535. qs = qof_query_create ();
  536. query_init (qs, qt);
  537. if (qof_query_has_terms (q))
  538. qr = qof_query_merge (q, qs, op);
  539. else
  540. qr = qof_query_merge (q, qs, QOF_QUERY_OR);
  541. swap_terms (q, qr);
  542. qof_query_destroy (qs);
  543. qof_query_destroy (qr);
  544. }
  545. void qof_query_purge_terms (QofQuery *q, QofQueryParamList *param_list)
  546. {
  547. QofQueryTerm *qt;
  548. GList *or, *and;
  549. if (!q || !param_list) return;
  550. for (or = q->terms; or; or = or->next)
  551. {
  552. for (and = or->data; and; and = and->next)
  553. {
  554. qt = and->data;
  555. if (!param_list_cmp (qt->param_list, param_list))
  556. {
  557. if (g_list_length (or->data) == 1)
  558. {
  559. q->terms = g_list_remove_link (q->terms, or);
  560. g_list_free_1 (or);
  561. or = q->terms;
  562. break;
  563. }
  564. else
  565. {
  566. or->data = g_list_remove_link (or->data, and);
  567. g_list_free_1 (and);
  568. and = or->data;
  569. if (!and) break;
  570. }
  571. q->changed = 1;
  572. free_query_term (qt);
  573. }
  574. }
  575. if (!or) break;
  576. }
  577. }
  578. static GList * qof_query_run_internal (QofQuery *q,
  579. void(*run_cb)(QofQueryCB*, gpointer),
  580. gpointer cb_arg)
  581. {
  582. GList *matching_objects = NULL;
  583. int object_count = 0;
  584. if (!q) return NULL;
  585. g_return_val_if_fail (q->search_for, NULL);
  586. g_return_val_if_fail (q->books, NULL);
  587. g_return_val_if_fail (run_cb, NULL);
  588. ENTER (" q=%p", q);
  589. /* XXX: Prioritize the query terms? */
  590. /* prepare the Query for processing */
  591. if (q->changed)
  592. {
  593. query_clear_compiles (q);
  594. compile_terms (q);
  595. }
  596. /* Maybe log this sucker */
  597. if (qof_log_check (log_module, QOF_LOG_DEBUG))
  598. qof_query_print (q);
  599. /* Now run the query over all the objects and save the results */
  600. {
  601. QofQueryCB qcb;
  602. memset (&qcb, 0, sizeof (qcb));
  603. qcb.query = q;
  604. /* Run the query callback */
  605. run_cb(&qcb, cb_arg);
  606. matching_objects = qcb.list;
  607. object_count = qcb.count;
  608. }
  609. PINFO ("matching objects=%p count=%d", matching_objects, object_count);
  610. /* There is no absolute need to reverse this list, since it's being
  611. * sorted below. However, in the common case, we will be searching
  612. * in a confined location where the objects are already in order,
  613. * thus reversing will put us in the correct order we want and make
  614. * the sorting go much faster.
  615. */
  616. matching_objects = g_list_reverse(matching_objects);
  617. /* Now sort the matching objects based on the search criteria */
  618. if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp ||
  619. (q->primary_sort.use_default && q->defaultSort))
  620. {
  621. matching_objects = g_list_sort_with_data(matching_objects, sort_func, q);
  622. }
  623. /* Crop the list to limit the number of splits. */
  624. if ((object_count > q->max_results) && (q->max_results > -1))
  625. {
  626. if (q->max_results > 0)
  627. {
  628. GList *mptr;
  629. /* mptr is set to the first node of what will be the new list */
  630. mptr = g_list_nth(matching_objects, object_count - q->max_results);
  631. /* mptr should not be NULL, but let's be safe */
  632. if (mptr != NULL)
  633. {
  634. if (mptr->prev != NULL) mptr->prev->next = NULL;
  635. mptr->prev = NULL;
  636. }
  637. g_list_free(matching_objects);
  638. matching_objects = mptr;
  639. }
  640. else
  641. {
  642. /* q->max_results == 0 */
  643. g_list_free(matching_objects);
  644. matching_objects = NULL;
  645. }
  646. object_count = q->max_results;
  647. }
  648. q->changed = 0;
  649. g_list_free(q->results);
  650. q->results = matching_objects;
  651. LEAVE (" q=%p", q);
  652. return matching_objects;
  653. }
  654. static void qof_query_run_cb(QofQueryCB* qcb, gpointer cb_arg)
  655. {
  656. GList *node;
  657. (void)cb_arg; /* unused */
  658. g_return_if_fail(qcb);
  659. for (node = qcb->query->books; node; node = node->next)
  660. {
  661. QofBook *book = node->data;
  662. QofBackend *be = book->backend;
  663. /* run the query in the backend */
  664. if (be)
  665. {
  666. gpointer compiled_query = g_hash_table_lookup (qcb->query->be_compiled,
  667. book);
  668. if (compiled_query && be->run_query)
  669. {
  670. (be->run_query) (be, compiled_query);
  671. }
  672. }
  673. /* And then iterate over all the objects */
  674. qof_object_foreach (qcb->query->search_for, book,
  675. (QofInstanceForeachCB) check_item_cb, qcb);
  676. }
  677. }
  678. GList * qof_query_run (QofQuery *q)
  679. {
  680. /* Just a wrapper */
  681. return qof_query_run_internal(q, qof_query_run_cb, NULL);
  682. }
  683. static void qof_query_run_subq_cb(QofQueryCB* qcb, gpointer cb_arg)
  684. {
  685. QofQuery* pq = cb_arg;
  686. g_return_if_fail(pq);
  687. g_list_foreach(qof_query_last_run(pq), check_item_cb, qcb);
  688. }
  689. GList *
  690. qof_query_run_subquery (QofQuery *subq, const QofQuery* primaryq)
  691. {
  692. if (!subq) return NULL;
  693. if (!primaryq) return NULL;
  694. /* Make sure we're searching for the same thing */
  695. g_return_val_if_fail (subq->search_for, NULL);
  696. g_return_val_if_fail (primaryq->search_for, NULL);
  697. g_return_val_if_fail(!safe_strcmp(subq->search_for, primaryq->search_for),
  698. NULL);
  699. /* Perform the subquery */
  700. return qof_query_run_internal(subq, qof_query_run_subq_cb,
  701. (gpointer)primaryq);
  702. }
  703. GList *
  704. qof_query_last_run (QofQuery *query)
  705. {
  706. if (!query)
  707. return NULL;
  708. return query->results;
  709. }
  710. void qof_query_clear (QofQuery *query)
  711. {
  712. QofQuery *q2 = qof_query_create ();
  713. swap_terms (query, q2);
  714. qof_query_destroy (q2);
  715. g_list_free (query->books);
  716. query->books = NULL;
  717. g_list_free (query->results);
  718. query->results = NULL;
  719. query->changed = 1;
  720. }
  721. QofQuery * qof_query_create (void)
  722. {
  723. QofQuery *qp = g_new0 (QofQuery, 1);
  724. qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal);
  725. query_init (qp, NULL);
  726. return qp;
  727. }
  728. void qof_query_search_for (QofQuery *q, QofIdTypeConst obj_type)
  729. {
  730. if (!q || !obj_type)
  731. return;
  732. if (safe_strcmp (q->search_for, obj_type))
  733. {
  734. q->search_for = (QofIdType) obj_type;
  735. q->changed = 1;
  736. }
  737. }
  738. QofQuery * qof_query_create_for (QofIdTypeConst obj_type)
  739. {
  740. QofQuery *q;
  741. if (!obj_type)
  742. return NULL;
  743. q = qof_query_create ();
  744. qof_query_search_for (q, obj_type);
  745. return q;
  746. }
  747. int qof_query_has_terms (QofQuery *q)
  748. {
  749. if (!q) return 0;
  750. return g_list_length (q->terms);
  751. }
  752. int qof_query_num_terms (QofQuery *q)
  753. {
  754. GList *o;
  755. int n = 0;
  756. if (!q) return 0;
  757. for (o = q->terms; o; o = o->next)
  758. n += g_list_length(o->data);
  759. return n;
  760. }
  761. gboolean qof_query_has_term_type (QofQuery *q, QofQueryParamList *term_param)
  762. {
  763. GList *or;
  764. GList *and;
  765. if (!q || !term_param)
  766. return FALSE;
  767. for (or = q->terms; or; or = or->next)
  768. {
  769. for (and = or->data; and; and = and->next)
  770. {
  771. QofQueryTerm *qt = and->data;
  772. if (!param_list_cmp (term_param, qt->param_list))
  773. return TRUE;
  774. }
  775. }
  776. return FALSE;
  777. }
  778. GSList * qof_query_get_term_type (QofQuery *q, QofQueryParamList *term_param)
  779. {
  780. GList *or;
  781. GList *and;
  782. GSList *results = NULL;
  783. if (!q || !term_param)
  784. return FALSE;
  785. for (or = q->terms; or; or = or->next)
  786. {
  787. for (and = or->data; and; and = and->next)
  788. {
  789. QofQueryTerm *qt = and->data;
  790. if (!param_list_cmp (term_param, qt->param_list))
  791. results = g_slist_append(results, qt->pdata);
  792. }
  793. }
  794. return results;
  795. }
  796. void qof_query_destroy (QofQuery *q)
  797. {
  798. if (!q) return;
  799. free_members (q);
  800. query_clear_compiles (q);
  801. g_hash_table_destroy (q->be_compiled);
  802. g_free (q);
  803. }
  804. QofQuery * qof_query_copy (QofQuery *q)
  805. {
  806. QofQuery *copy;
  807. GHashTable *ht;
  808. if (!q) return NULL;
  809. copy = qof_query_create ();
  810. ht = copy->be_compiled;
  811. free_members (copy);
  812. memcpy (copy, q, sizeof (QofQuery));
  813. copy->be_compiled = ht;
  814. copy->terms = copy_or_terms (q->terms);
  815. copy->books = g_list_copy (q->books);
  816. copy->results = g_list_copy (q->results);
  817. copy_sort (&(copy->primary_sort), &(q->primary_sort));
  818. copy_sort (&(copy->secondary_sort), &(q->secondary_sort));
  819. copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort));
  820. copy->changed = 1;
  821. return copy;
  822. }
  823. /* *******************************************************************
  824. * qof_query_invert
  825. * return a newly-allocated Query object which is the
  826. * logical inverse of the original.
  827. ********************************************************************/
  828. QofQuery * qof_query_invert (QofQuery *q)
  829. {
  830. QofQuery * retval;
  831. QofQuery * right, * left, * iright, * ileft;
  832. QofQueryTerm * qt;
  833. GList * aterms;
  834. GList * cur;
  835. GList * new_oterm;
  836. int num_or_terms;
  837. if (!q)
  838. return NULL;
  839. num_or_terms = g_list_length(q->terms);
  840. switch (num_or_terms)
  841. {
  842. case 0:
  843. retval = qof_query_create();
  844. retval->max_results = q->max_results;
  845. break;
  846. /* This is the DeMorgan expansion for a single AND expression. */
  847. /* !(abc) = !a + !b + !c */
  848. case 1:
  849. retval = qof_query_create();
  850. retval->max_results = q->max_results;
  851. retval->books = g_list_copy (q->books);
  852. retval->search_for = q->search_for;
  853. retval->changed = 1;
  854. aterms = g_list_nth_data(q->terms, 0);
  855. new_oterm = NULL;
  856. for (cur = aterms; cur; cur = cur->next)
  857. {
  858. qt = copy_query_term(cur->data);
  859. qt->invert = !(qt->invert);
  860. new_oterm = g_list_append(NULL, qt);
  861. retval->terms = g_list_prepend(retval->terms, new_oterm);
  862. }
  863. retval->terms = g_list_reverse(retval->terms);
  864. break;
  865. /* If there are multiple OR-terms, we just recurse by
  866. * breaking it down to !(a + b + c) =
  867. * !a * !(b + c) = !a * !b * !c. */
  868. default:
  869. right = qof_query_create();
  870. right->terms = copy_or_terms(g_list_nth(q->terms, 1));
  871. left = qof_query_create();
  872. left->terms = g_list_append(NULL,
  873. copy_and_terms(g_list_nth_data(q->terms, 0)));
  874. iright = qof_query_invert(right);
  875. ileft = qof_query_invert(left);
  876. retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
  877. retval->books = g_list_copy (q->books);
  878. retval->max_results = q->max_results;
  879. retval->search_for = q->search_for;
  880. retval->changed = 1;
  881. qof_query_destroy(iright);
  882. qof_query_destroy(ileft);
  883. qof_query_destroy(right);
  884. qof_query_destroy(left);
  885. break;
  886. }
  887. return retval;
  888. }
  889. /* *******************************************************************
  890. * qof_query_merge
  891. * combine 2 Query objects by the logical operation in "op".
  892. ********************************************************************/
  893. QofQuery *
  894. qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
  895. {
  896. QofQuery * retval = NULL;
  897. QofQuery * i1, * i2;
  898. QofQuery * t1, * t2;
  899. GList * i, * j;
  900. QofIdType search_for;
  901. if (!q1) return q2;
  902. if (!q2) return q1;
  903. if (q1->search_for && q2->search_for)
  904. g_return_val_if_fail (safe_strcmp (q1->search_for, q2->search_for) == 0,
  905. NULL);
  906. search_for = (q1->search_for ? q1->search_for : q2->search_for);
  907. /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
  908. * The goal of this tweak is to allow the user to start with
  909. * an empty q1 and then append to it recursively
  910. * (and q1 (and q2 (and q3 (and q4 ....))))
  911. * without bombing out because the append started with an
  912. * empty list.
  913. * We do essentially the same check in qof_query_add_term()
  914. * so that the first term added to an empty query doesn't screw up.
  915. */
  916. if ((QOF_QUERY_AND == op) &&
  917. ( (0 == qof_query_has_terms (q1)) || (0 == qof_query_has_terms (q2)) ))
  918. {
  919. op = QOF_QUERY_OR;
  920. }
  921. switch (op)
  922. {
  923. case QOF_QUERY_OR:
  924. retval = qof_query_create();
  925. retval->terms =
  926. g_list_concat(copy_or_terms(q1->terms), copy_or_terms(q2->terms));
  927. retval->books = merge_books (q1->books, q2->books);
  928. retval->max_results = q1->max_results;
  929. retval->changed = 1;
  930. break;
  931. case QOF_QUERY_AND:
  932. retval = qof_query_create();
  933. retval->books = merge_books (q1->books, q2->books);
  934. retval->max_results = q1->max_results;
  935. retval->changed = 1;
  936. /* g_list_append() can take forever, so let's build the list in
  937. * reverse and then reverse it at the end, to deal better with
  938. * "large" queries.
  939. */
  940. for (i = q1->terms; i; i = i->next)
  941. {
  942. for (j = q2->terms; j; j = j->next)
  943. {
  944. retval->terms =
  945. g_list_prepend(retval->terms,
  946. g_list_concat
  947. (copy_and_terms(i->data),
  948. copy_and_terms(j->data)));
  949. }
  950. }
  951. retval->terms = g_list_reverse(retval->terms);
  952. break;
  953. case QOF_QUERY_NAND:
  954. /* !(a*b) = (!a + !b) */
  955. i1 = qof_query_invert(q1);
  956. i2 = qof_query_invert(q2);
  957. retval = qof_query_merge(i1, i2, QOF_QUERY_OR);
  958. qof_query_destroy(i1);
  959. qof_query_destroy(i2);
  960. break;
  961. case QOF_QUERY_NOR:
  962. /* !(a+b) = (!a*!b) */
  963. i1 = qof_query_invert(q1);
  964. i2 = qof_query_invert(q2);
  965. retval = qof_query_merge(i1, i2, QOF_QUERY_AND);
  966. qof_query_destroy(i1);
  967. qof_query_destroy(i2);
  968. break;
  969. case QOF_QUERY_XOR:
  970. /* a xor b = (a * !b) + (!a * b) */
  971. i1 = qof_query_invert(q1);
  972. i2 = qof_query_invert(q2);
  973. t1 = qof_query_merge(q1, i2, QOF_QUERY_AND);
  974. t2 = qof_query_merge(i1, q2, QOF_QUERY_AND);
  975. retval = qof_query_merge(t1, t2, QOF_QUERY_OR);
  976. qof_query_destroy(i1);
  977. qof_query_destroy(i2);
  978. qof_query_destroy(t1);
  979. qof_query_destroy(t2);
  980. break;
  981. }
  982. retval->search_for = search_for;
  983. return retval;
  984. }
  985. void
  986. qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op)
  987. {
  988. QofQuery *tmp_q;
  989. if (!q1 || !q2)
  990. return;
  991. tmp_q = qof_query_merge (q1, q2, op);
  992. swap_terms (q1, tmp_q);
  993. qof_query_destroy (tmp_q);
  994. }
  995. void
  996. qof_query_set_sort_order (QofQuery *q,
  997. QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
  998. {
  999. if (!q) return;
  1000. if (q->primary_sort.param_list)
  1001. g_slist_free (q->primary_sort.param_list);
  1002. q->primary_sort.param_list = params1;
  1003. q->primary_sort.options = 0;
  1004. if (q->secondary_sort.param_list)
  1005. g_slist_free (q->secondary_sort.param_list);
  1006. q->secondary_sort.param_list = params2;
  1007. q->secondary_sort.options = 0;
  1008. if (q->tertiary_sort.param_list)
  1009. g_slist_free (q->tertiary_sort.param_list);
  1010. q->tertiary_sort.param_list = params3;
  1011. q->tertiary_sort.options = 0;
  1012. q->changed = 1;
  1013. }
  1014. void qof_query_set_sort_options (QofQuery *q, gint prim_op, gint sec_op,
  1015. gint tert_op)
  1016. {
  1017. if (!q) return;
  1018. q->primary_sort.options = prim_op;
  1019. q->secondary_sort.options = sec_op;
  1020. q->tertiary_sort.options = tert_op;
  1021. }
  1022. void qof_query_set_sort_increasing (QofQuery *q, gboolean prim_inc,
  1023. gboolean sec_inc, gboolean tert_inc)
  1024. {
  1025. if (!q) return;
  1026. q->primary_sort.increasing = prim_inc;
  1027. q->secondary_sort.increasing = sec_inc;
  1028. q->tertiary_sort.increasing = tert_inc;
  1029. }
  1030. void qof_query_set_max_results (QofQuery *q, int n)
  1031. {
  1032. if (!q) return;
  1033. q->max_results = n;
  1034. }
  1035. void qof_query_add_guid_list_match (QofQuery *q, QofQueryParamList *param_list,
  1036. GList *guid_list, QofGuidMatch options,
  1037. QofQueryOp op)
  1038. {
  1039. QofQueryPredData *pdata;
  1040. if (!q || !param_list) return;
  1041. if (!guid_list)
  1042. g_return_if_fail (options == QOF_GUID_MATCH_NULL);
  1043. pdata = qof_query_guid_predicate (options, guid_list);
  1044. qof_query_add_term (q, param_list, pdata, op);
  1045. }
  1046. void qof_query_add_guid_match (QofQuery *q, QofQueryParamList *param_list,
  1047. const GncGUID *guid, QofQueryOp op)
  1048. {
  1049. GList *g = NULL;
  1050. if (!q || !param_list) return;
  1051. if (guid)
  1052. g = g_list_prepend (g, (gpointer)guid);
  1053. qof_query_add_guid_list_match (q, param_list, g,
  1054. g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op);
  1055. g_list_free (g);
  1056. }
  1057. void qof_query_set_book (QofQuery *q, QofBook *book)
  1058. {
  1059. QofQueryParamList *slist = NULL;
  1060. if (!q || !book) return;
  1061. /* Make sure this book is only in the list once */
  1062. if (g_list_index (q->books, book) == -1)
  1063. q->books = g_list_prepend (q->books, book);
  1064. slist = g_slist_prepend (slist, QOF_PARAM_GUID);
  1065. slist = g_slist_prepend (slist, QOF_PARAM_BOOK);
  1066. qof_query_add_guid_match (q, slist,
  1067. qof_instance_get_guid(book), QOF_QUERY_AND);
  1068. }
  1069. GList * qof_query_get_books (QofQuery *q)
  1070. {
  1071. if (!q) return NULL;
  1072. return q->books;
  1073. }
  1074. void qof_query_add_boolean_match (QofQuery *q, QofQueryParamList *param_list, gboolean value,
  1075. QofQueryOp op)
  1076. {
  1077. QofQueryPredData *pdata;
  1078. if (!q || !param_list) return;
  1079. pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value);
  1080. qof_query_add_term (q, param_list, pdata, op);
  1081. }
  1082. /**********************************************************************/
  1083. /* PRIVATE PUBLISHED API FUNCTIONS */
  1084. void qof_query_init (void)
  1085. {
  1086. ENTER (" ");
  1087. qof_query_core_init ();
  1088. qof_class_init ();
  1089. LEAVE ("Completed initialization of QofQuery");
  1090. }
  1091. void qof_query_shutdown (void)
  1092. {
  1093. qof_class_shutdown ();
  1094. qof_query_core_shutdown ();
  1095. }
  1096. int qof_query_get_max_results (const QofQuery *q)
  1097. {
  1098. if (!q) return 0;
  1099. return q->max_results;
  1100. }
  1101. QofIdType qof_query_get_search_for (const QofQuery *q)
  1102. {
  1103. if (!q) return NULL;
  1104. return q->search_for;
  1105. }
  1106. GList * qof_query_get_terms (const QofQuery *q)
  1107. {
  1108. if (!q) return NULL;
  1109. return q->terms;
  1110. }
  1111. QofQueryParamList * qof_query_term_get_param_path (const QofQueryTerm *qt)
  1112. {
  1113. if (!qt)
  1114. return NULL;
  1115. return qt->param_list;
  1116. }
  1117. QofQueryPredData *qof_query_term_get_pred_data (const QofQueryTerm *qt)
  1118. {
  1119. if (!qt)
  1120. return NULL;
  1121. return qt->pdata;
  1122. }
  1123. gboolean qof_query_term_is_inverted (const QofQueryTerm *qt)
  1124. {
  1125. if (!qt)
  1126. return FALSE;
  1127. return qt->invert;
  1128. }
  1129. void qof_query_get_sorts (QofQuery *q, QofQuerySort **primary,
  1130. QofQuerySort **secondary, QofQuerySort **tertiary)
  1131. {
  1132. if (!q)
  1133. return;
  1134. if (primary)
  1135. *primary = &(q->primary_sort);
  1136. if (secondary)
  1137. *secondary = &(q->secondary_sort);
  1138. if (tertiary)
  1139. *tertiary = &(q->tertiary_sort);
  1140. }
  1141. QofQueryParamList * qof_query_sort_get_param_path (const QofQuerySort *qs)
  1142. {
  1143. if (!qs)
  1144. return NULL;
  1145. return qs->param_list;
  1146. }
  1147. gint qof_query_sort_get_sort_options (const QofQuerySort *qs)
  1148. {
  1149. if (!qs)
  1150. return 0;
  1151. return qs->options;
  1152. }
  1153. gboolean qof_query_sort_get_increasing (const QofQuerySort *qs)
  1154. {
  1155. if (!qs)
  1156. return FALSE;
  1157. return qs->increasing;
  1158. }
  1159. static gboolean
  1160. qof_query_term_equal (const QofQueryTerm *qt1, const QofQueryTerm *qt2)
  1161. {
  1162. if (qt1 == qt2) return TRUE;
  1163. if (!qt1 || !qt2) return FALSE;
  1164. if (qt1->invert != qt2->invert) return FALSE;
  1165. if (param_list_cmp (qt1->param_list, qt2->param_list)) return FALSE;
  1166. return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata);
  1167. }
  1168. static gboolean
  1169. qof_query_sort_equal (const QofQuerySort* qs1, const QofQuerySort* qs2)
  1170. {
  1171. if (qs1 == qs2) return TRUE;
  1172. if (!qs1 || !qs2) return FALSE;
  1173. /* "Empty" sorts are equivalent, regardless of the flags */
  1174. if (!qs1->param_list && !qs2->param_list) return TRUE;
  1175. if (qs1->options != qs2->options) return FALSE;
  1176. if (qs1->increasing != qs2->increasing) return FALSE;
  1177. return (param_list_cmp (qs1->param_list, qs2->param_list) == 0);
  1178. }
  1179. gboolean qof_query_equal (const QofQuery *q1, const QofQuery *q2)
  1180. {
  1181. GList *or1, *or2;
  1182. if (q1 == q2) return TRUE;
  1183. if (!q1 || !q2) return FALSE;
  1184. if (g_list_length (q1->terms) != g_list_length (q2->terms)) return FALSE;
  1185. if (q1->max_results != q2->max_results) return FALSE;
  1186. for (or1 = q1->terms, or2 = q2->terms; or1;
  1187. or1 = or1->next, or2 = or2->next)
  1188. {
  1189. GList *and1, *and2;
  1190. and1 = or1->data;
  1191. and2 = or2->data;
  1192. if (g_list_length (and1) != g_list_length (and2)) return FALSE;
  1193. for ( ; and1; and1 = and1->next, and2 = and2->next)
  1194. if (!qof_query_term_equal (and1->data, and2->data))
  1195. return FALSE;
  1196. }
  1197. if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort)))
  1198. return FALSE;
  1199. if (!qof_query_sort_equal (&(q1->secondary_sort), &(q2->secondary_sort)))
  1200. return FALSE;
  1201. if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort)))
  1202. return FALSE;
  1203. return TRUE;
  1204. }
  1205. /* **************************************************************************/
  1206. /* Query Print functions for use with qof_log_set_level.
  1207. */
  1208. /* Static prototypes */
  1209. static GList *qof_query_printSearchFor (QofQuery * query, GList * output);
  1210. static GList *qof_query_printTerms (QofQuery * query, GList * output);
  1211. static GList *qof_query_printSorts (QofQuerySort *s[], const gint numSorts,
  1212. GList * output);
  1213. static GList *qof_query_printAndTerms (GList * terms, GList * output);
  1214. static gchar *qof_query_printStringForHow (QofQueryCompare how);
  1215. static gchar *qof_query_printStringMatch (QofStringMatch s);
  1216. static gchar *qof_query_printDateMatch (QofDateMatch d);
  1217. static gchar *qof_query_printNumericMatch (QofNumericMatch n);
  1218. static gchar *qof_query_printGuidMatch (QofGuidMatch g);
  1219. static gchar *qof_query_printCharMatch (QofCharMatch c);
  1220. static GList *qof_query_printPredData (QofQueryPredData *pd, GList *lst);
  1221. static GString *qof_query_printParamPath (QofQueryParamList * parmList);
  1222. static void qof_query_printValueForParam (QofQueryPredData *pd, GString * gs);
  1223. static void qof_query_printOutput (GList * output);
  1224. /** \deprecated access via qof_log instead.
  1225. The query will be logged automatically if qof_log_set_default
  1226. or qof_log_set_level(QOF_MOD_QUERY, ...) are set to QOF_LOG_DEBUG
  1227. or higher.
  1228. This function cycles through a QofQuery object, and
  1229. prints out the values of the various members of the query
  1230. */
  1231. void
  1232. qof_query_print (QofQuery * query)
  1233. {
  1234. GList *output;
  1235. GString *str;
  1236. QofQuerySort *s[3];
  1237. gint maxResults = 0, numSorts = 3;
  1238. ENTER (" ");
  1239. if (!query)
  1240. {
  1241. LEAVE("query is (null)");
  1242. return;
  1243. }
  1244. output = NULL;
  1245. str = NULL;
  1246. maxResults = qof_query_get_max_results (query);
  1247. output = qof_query_printSearchFor (query, output);
  1248. output = qof_query_printTerms (query, output);
  1249. qof_query_get_sorts (query, &s[0], &s[1], &s[2]);
  1250. if (s[0])
  1251. {
  1252. output = qof_query_printSorts (s, numSorts, output);
  1253. }
  1254. str = g_string_new (" ");
  1255. g_string_printf (str, "Maximum number of results: %d", maxResults);
  1256. output = g_list_append (output, str);
  1257. qof_query_printOutput (output);
  1258. LEAVE (" ");
  1259. }
  1260. static void
  1261. qof_query_printOutput (GList * output)
  1262. {
  1263. GList *lst;
  1264. for (lst = output; lst; lst = lst->next)
  1265. {
  1266. GString *line = (GString *) lst->data;
  1267. DEBUG (" %s", line->str);
  1268. g_string_free (line, TRUE);
  1269. line = NULL;
  1270. }
  1271. }
  1272. /*
  1273. Get the search_for type--This is the type of Object
  1274. we are searching for (SPLIT, TRANS, etc)
  1275. */
  1276. static GList *
  1277. qof_query_printSearchFor (QofQuery * query, GList * output)
  1278. {
  1279. QofIdType searchFor;
  1280. GString *gs;
  1281. searchFor = qof_query_get_search_for (query);
  1282. gs = g_string_new ("Query Object Type: ");
  1283. g_string_append (gs, (NULL == searchFor) ? "(null)" : searchFor);
  1284. output = g_list_append (output, gs);
  1285. return output;
  1286. } /* qof_query_printSearchFor */
  1287. /*
  1288. Run through the terms of the query. This is a outer-inner
  1289. loop. The elements of the outer loop are ORed, and the
  1290. elements of the inner loop are ANDed.
  1291. */
  1292. static GList *
  1293. qof_query_printTerms (QofQuery * query, GList * output)
  1294. {
  1295. GList *terms, *lst;
  1296. terms = qof_query_get_terms (query);
  1297. for (lst = terms; lst; lst = lst->next)
  1298. {
  1299. output = g_list_append (output, g_string_new ("OR and AND Terms:"));
  1300. if (lst->data)
  1301. {
  1302. output = qof_query_printAndTerms (lst->data, output);
  1303. }
  1304. else
  1305. {
  1306. output =
  1307. g_list_append (output, g_string_new (" No data for AND terms"));
  1308. }
  1309. }
  1310. return output;
  1311. } /* qof_query_printTerms */
  1312. /*
  1313. Process the sort parameters
  1314. If this function is called, the assumption is that the first sort
  1315. not null.
  1316. */
  1317. static GList *
  1318. qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
  1319. {
  1320. QofQueryParamList *gsl, *n = NULL;
  1321. gint curSort;
  1322. GString *gs = g_string_new ("Sort Parameters: ");
  1323. for (curSort = 0; curSort < numSorts; curSort++)
  1324. {
  1325. gboolean increasing;
  1326. if (!s[curSort])
  1327. {
  1328. break;
  1329. }
  1330. increasing = qof_query_sort_get_increasing (s[curSort]);
  1331. gsl = qof_query_sort_get_param_path (s[curSort]);
  1332. if (gsl) g_string_append_printf (gs, " Param: ");
  1333. for (n = gsl; n; n = n->next)
  1334. {
  1335. QofIdType param_name = n->data;
  1336. if (gsl != n) g_string_append_printf (gs, " ");
  1337. g_string_append_printf (gs, "%s", param_name);
  1338. }
  1339. if (gsl)
  1340. {
  1341. g_string_append_printf (gs, " %s ", increasing ? "DESC" : "ASC");
  1342. g_string_append_printf (gs, " Options: 0x%x ", s[curSort]->options);
  1343. }
  1344. }
  1345. output = g_list_append (output, gs);
  1346. return output;
  1347. } /* qof_query_printSorts */
  1348. /*
  1349. Process the AND terms of the query. This is a GList
  1350. of WHERE terms that will be ANDed
  1351. */
  1352. static GList *
  1353. qof_query_printAndTerms (GList * terms, GList * output)
  1354. {
  1355. const char *prefix = "AND Terms:";
  1356. QofQueryTerm *qt;
  1357. QofQueryPredData *pd;
  1358. QofQueryParamList *path;
  1359. GList *lst;
  1360. gboolean invert;
  1361. output = g_list_append (output, g_string_new (prefix));
  1362. for (lst = terms; lst; lst = lst->next)
  1363. {
  1364. qt = (QofQueryTerm *) lst->data;
  1365. pd = qof_query_term_get_pred_data (qt);
  1366. path = qof_query_term_get_param_path (qt);
  1367. invert = qof_query_term_is_inverted (qt);
  1368. if (invert) output = g_list_append (output,
  1369. g_string_new(" INVERT SENSE "));
  1370. output = g_list_append (output, qof_query_printParamPath (path));
  1371. output = qof_query_printPredData (pd, output);
  1372. // output = g_list_append (output, g_string_new(" "));
  1373. }
  1374. return output;
  1375. } /* qof_query_printAndTerms */
  1376. /*
  1377. Process the parameter types of the predicate data
  1378. */
  1379. static GString *
  1380. qof_query_printParamPath (QofQueryParamList * parmList)
  1381. {
  1382. QofQueryParamList *list = NULL;
  1383. GString *gs = g_string_new ("Param List: ");
  1384. g_string_append (gs, " ");
  1385. for (list = parmList; list; list = list->next)
  1386. {
  1387. g_string_append (gs, (gchar *) list->data);
  1388. if (list->next)
  1389. g_string_append (gs, "->");
  1390. }
  1391. return gs;
  1392. } /* qof_query_printParamPath */
  1393. /*
  1394. Process the PredData of the AND terms
  1395. */
  1396. static GList *
  1397. qof_query_printPredData (QofQueryPredData *pd, GList *lst)
  1398. {
  1399. GString *gs;
  1400. gs = g_string_new ("Pred Data: ");
  1401. g_string_append (gs, (gchar *) pd->type_name);
  1402. /* Char Predicate and GncGUID predicate don't use the 'how' field. */
  1403. if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
  1404. safe_strcmp (pd->type_name, QOF_TYPE_GUID))
  1405. {
  1406. g_string_append_printf (gs, " how: %s",
  1407. qof_query_printStringForHow (pd->how));
  1408. }
  1409. lst = g_list_append(lst, gs);
  1410. gs = g_string_new ("");
  1411. qof_query_printValueForParam (pd, gs);
  1412. lst = g_list_append(lst, gs);
  1413. return lst;
  1414. } /* qof_query_printPredData */
  1415. /*
  1416. Get a string representation for the
  1417. QofCompareFunc enum type.
  1418. */
  1419. static gchar *
  1420. qof_query_printStringForHow (QofQueryCompare how)
  1421. {
  1422. switch (how)
  1423. {
  1424. case QOF_COMPARE_LT:
  1425. return "QOF_COMPARE_LT";
  1426. case QOF_COMPARE_LTE:
  1427. return "QOF_COMPARE_LTE";
  1428. case QOF_COMPARE_EQUAL:
  1429. return "QOF_COMPARE_EQUAL";
  1430. case QOF_COMPARE_GT:
  1431. return "QOF_COMPARE_GT";
  1432. case QOF_COMPARE_GTE:
  1433. return "QOF_COMPARE_GTE";
  1434. case QOF_COMPARE_NEQ:
  1435. return "QOF_COMPARE_NEQ";
  1436. }
  1437. return "INVALID HOW";
  1438. } /* qncQueryPrintStringForHow */
  1439. static void
  1440. qof_query_printValueForParam (QofQueryPredData *pd, GString * gs)
  1441. {
  1442. if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
  1443. {
  1444. GList *node;
  1445. query_guid_t pdata = (query_guid_t) pd;
  1446. g_string_append_printf (gs, "Match type %s",
  1447. qof_query_printGuidMatch (pdata->options));
  1448. for (node = pdata->guids; node; node = node->next)
  1449. {
  1450. /* THREAD-UNSAFE */
  1451. g_string_append_printf (gs, ", guids: %s",
  1452. guid_to_string ((GncGUID *) node->data));
  1453. }
  1454. return;
  1455. }
  1456. if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
  1457. {
  1458. query_string_t pdata = (query_string_t) pd;
  1459. g_string_append_printf (gs, " Match type %s",
  1460. qof_query_printStringMatch (pdata->options));
  1461. g_string_append_printf (gs, " %s string: %s",
  1462. pdata->is_regex ? "Regex" : "Not regex",
  1463. pdata->matchstring);
  1464. return;
  1465. }
  1466. if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
  1467. {
  1468. query_numeric_t pdata = (query_numeric_t) pd;
  1469. g_string_append_printf (gs, " Match type %s",
  1470. qof_query_printNumericMatch (pdata->options));
  1471. g_string_append_printf (gs, " gnc_numeric: %s",
  1472. gnc_num_dbg_to_string (pdata->amount));
  1473. return;
  1474. }
  1475. if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
  1476. {
  1477. GSList *node;
  1478. query_kvp_t pdata = (query_kvp_t) pd;
  1479. g_string_append_printf (gs, " kvp path: ");
  1480. for (node = pdata->path; node; node = node->next)
  1481. {
  1482. g_string_append_printf (gs, "/%s", (gchar *)

Large files files are truncated, but you can click here to view the full file