QOF
0.8.7
|
00001 /********************************************************************\ 00002 * qof_query.c -- Implement predicate API for searching for objects * 00003 * Copyright (C) 2002 Derek Atkins <warlord@MIT.EDU> * 00004 * Copyright (C) 2006-2008 Neil Williams <linux@codehelp.co.uk> * 00005 * * 00006 * This program is free software; you can redistribute it and/or * 00007 * modify it under the terms of the GNU General Public License as * 00008 * published by the Free Software Foundation; either version 2 of * 00009 * the License, or (at your option) any later version. * 00010 * * 00011 * This program is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00014 * GNU General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU General Public License* 00017 * along with this program; if not, contact: * 00018 * * 00019 * Free Software Foundation Voice: +1-617-542-5942 * 00020 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00021 * Boston, MA 02110-1301, USA gnu@gnu.org * 00022 * * 00023 \********************************************************************/ 00024 00025 #include "config.h" 00026 00027 #include <sys/types.h> 00028 #include <time.h> 00029 #include <glib.h> 00030 #include <regex.h> 00031 #include <string.h> 00032 00033 #include "qof.h" 00034 #include "qofbackend-p.h" 00035 #include "qofbook-p.h" 00036 #include "qofclass-p.h" 00037 #include "qofquery-p.h" 00038 #include "qofquerycore-p.h" 00039 00040 static QofLogModule log_module = QOF_MOD_QUERY; 00041 00042 struct _QofQueryTerm 00043 { 00044 GSList *param_list; 00045 QofQueryPredData *pdata; 00046 gboolean invert; 00047 00048 /* These values are filled in during "compilation" of the query 00049 * term, based upon the obj_name, param_name, and searched-for 00050 * object type. If conv_fcn is NULL, then we don't know how to 00051 * convert types. 00052 */ 00053 GSList *param_fcns; 00054 QofQueryPredicateFunc pred_fcn; 00055 }; 00056 00057 struct _QofQuerySort 00058 { 00059 GSList *param_list; 00060 gint options; 00061 gboolean increasing; 00062 00063 /* These values are filled in during "compilation" of the query 00064 * term, based upon the obj_name, param_name, and searched-for 00065 * object type. If conv_fcn is NULL, then we don't know how to 00066 * convert types. 00067 */ 00068 gboolean use_default; 00069 GSList *param_fcns; /* Chain of paramters to walk */ 00070 QofSortFunc obj_cmp; /* In case you are comparing objects */ 00071 QofCompareFunc comp_fcn; /* When you are comparing core types */ 00072 }; 00073 00074 /* The QUERY structure */ 00075 struct _QofQuery 00076 { 00077 /* The object type that we're searching for */ 00078 QofIdType search_for; 00079 00080 /* terms is a list of the OR-terms in a sum-of-products 00081 * logical expression. */ 00082 GList *terms; 00083 00084 /* sorting and chopping is independent of the search filter */ 00085 00086 QofQuerySort primary_sort; 00087 QofQuerySort secondary_sort; 00088 QofQuerySort tertiary_sort; 00089 QofSortFunc defaultSort; /* <- Computed from search_for */ 00090 00091 /* The maximum number of results to return */ 00092 gint max_results; 00093 00094 /* list of books that will be participating in the query */ 00095 GList *books; 00096 00097 /* a map of book to backend-compiled queries */ 00098 GHashTable *be_compiled; 00099 00100 /* cache the results so we don't have to run the whole search 00101 * again until it's really necessary */ 00102 gint changed; 00103 00104 GList *results; 00105 }; 00106 00107 typedef struct _QofQueryCB 00108 { 00109 QofQuery *query; 00110 GList *list; 00111 gint count; 00112 } QofQueryCB; 00113 00114 /* initial_term will be owned by the new Query */ 00115 static void 00116 query_init (QofQuery * q, QofQueryTerm * initial_term) 00117 { 00118 GList *or = NULL; 00119 GList *and = NULL; 00120 GHashTable *ht; 00121 00122 if (initial_term) 00123 { 00124 or = g_list_alloc (); 00125 and = g_list_alloc (); 00126 and->data = initial_term; 00127 or->data = and; 00128 } 00129 00130 if (q->terms) 00131 qof_query_clear (q); 00132 00133 g_list_free (q->results); 00134 g_list_free (q->books); 00135 00136 g_slist_free (q->primary_sort.param_list); 00137 g_slist_free (q->secondary_sort.param_list); 00138 g_slist_free (q->tertiary_sort.param_list); 00139 00140 g_slist_free (q->primary_sort.param_fcns); 00141 g_slist_free (q->secondary_sort.param_fcns); 00142 g_slist_free (q->tertiary_sort.param_fcns); 00143 00144 ht = q->be_compiled; 00145 memset (q, 0, sizeof (*q)); 00146 q->be_compiled = ht; 00147 00148 q->terms = or; 00149 q->changed = 1; 00150 q->max_results = -1; 00151 00152 q->primary_sort.param_list = 00153 g_slist_prepend (NULL, QUERY_DEFAULT_SORT); 00154 q->primary_sort.increasing = TRUE; 00155 q->secondary_sort.increasing = TRUE; 00156 q->tertiary_sort.increasing = TRUE; 00157 } 00158 00159 static void 00160 swap_terms (QofQuery * q1, QofQuery * q2) 00161 { 00162 GList *g; 00163 00164 if (!q1 || !q2) 00165 return; 00166 00167 g = q1->terms; 00168 q1->terms = q2->terms; 00169 q2->terms = g; 00170 00171 g = q1->books; 00172 q1->books = q2->books; 00173 q2->books = g; 00174 00175 q1->changed = 1; 00176 q2->changed = 1; 00177 } 00178 00179 static void 00180 free_query_term (QofQueryTerm * qt) 00181 { 00182 if (!qt) 00183 return; 00184 00185 qof_query_core_predicate_free (qt->pdata); 00186 g_slist_free (qt->param_list); 00187 g_slist_free (qt->param_fcns); 00188 g_free (qt); 00189 } 00190 00191 static QofQueryTerm * 00192 copy_query_term (QofQueryTerm * qt) 00193 { 00194 QofQueryTerm *new_qt; 00195 if (!qt) 00196 return NULL; 00197 00198 new_qt = g_new0 (QofQueryTerm, 1); 00199 memcpy (new_qt, qt, sizeof (QofQueryTerm)); 00200 new_qt->param_list = g_slist_copy (qt->param_list); 00201 new_qt->param_fcns = g_slist_copy (qt->param_fcns); 00202 new_qt->pdata = qof_query_core_predicate_copy (qt->pdata); 00203 return new_qt; 00204 } 00205 00206 static GList * 00207 copy_and_terms (GList * and_terms) 00208 { 00209 GList *and = NULL; 00210 GList *cur_and; 00211 00212 for (cur_and = and_terms; cur_and; cur_and = cur_and->next) 00213 { 00214 and = g_list_prepend (and, copy_query_term (cur_and->data)); 00215 } 00216 00217 return g_list_reverse (and); 00218 } 00219 00220 static GList * 00221 copy_or_terms (GList * or_terms) 00222 { 00223 GList *or = NULL; 00224 GList *cur_or; 00225 00226 for (cur_or = or_terms; cur_or; cur_or = cur_or->next) 00227 { 00228 or = g_list_prepend (or, copy_and_terms (cur_or->data)); 00229 } 00230 00231 return g_list_reverse (or); 00232 } 00233 00234 static void 00235 copy_sort (QofQuerySort * dst, const QofQuerySort * src) 00236 { 00237 memcpy (dst, src, sizeof (*dst)); 00238 dst->param_list = g_slist_copy (src->param_list); 00239 dst->param_fcns = g_slist_copy (src->param_fcns); 00240 } 00241 00242 static void 00243 free_sort (QofQuerySort * s) 00244 { 00245 g_slist_free (s->param_list); 00246 s->param_list = NULL; 00247 00248 g_slist_free (s->param_fcns); 00249 s->param_fcns = NULL; 00250 } 00251 00252 static void 00253 free_members (QofQuery * q) 00254 { 00255 GList *cur_or; 00256 00257 if (q == NULL) 00258 return; 00259 00260 for (cur_or = q->terms; cur_or; cur_or = cur_or->next) 00261 { 00262 GList *cur_and; 00263 00264 for (cur_and = cur_or->data; cur_and; cur_and = cur_and->next) 00265 { 00266 free_query_term (cur_and->data); 00267 cur_and->data = NULL; 00268 } 00269 00270 g_list_free (cur_or->data); 00271 cur_or->data = NULL; 00272 } 00273 00274 free_sort (&(q->primary_sort)); 00275 free_sort (&(q->secondary_sort)); 00276 free_sort (&(q->tertiary_sort)); 00277 00278 g_list_free (q->terms); 00279 q->terms = NULL; 00280 00281 g_list_free (q->books); 00282 q->books = NULL; 00283 00284 g_list_free (q->results); 00285 q->results = NULL; 00286 } 00287 00288 static gint 00289 cmp_func (QofQuerySort * sort, QofSortFunc default_sort, 00290 gconstpointer a, gconstpointer b) 00291 { 00292 QofParam *param = NULL; 00293 GSList *node; 00294 gpointer conva, convb; 00295 00296 g_return_val_if_fail (sort, 0); 00297 00298 /* See if this is a default sort */ 00299 if (sort->use_default) 00300 { 00301 if (default_sort) 00302 return default_sort (a, b); 00303 return 0; 00304 } 00305 00306 /* If no parameters, consider them equal */ 00307 if (!sort->param_fcns) 00308 return 0; 00309 00310 /* no compare function, consider the two objects equal */ 00311 if (!sort->comp_fcn && !sort->obj_cmp) 00312 return 0; 00313 00314 /* Do the list of conversions */ 00315 conva = (gpointer) a; 00316 convb = (gpointer) b; 00317 for (node = sort->param_fcns; node; node = node->next) 00318 { 00319 param = node->data; 00320 00321 /* The last term is really the "parameter getter", 00322 * unless we're comparing objects ;) */ 00323 if (!node->next && !sort->obj_cmp) 00324 break; 00325 00326 /* Do the converstions */ 00327 conva = (param->param_getfcn) (conva, param); 00328 convb = (param->param_getfcn) (convb, param); 00329 } 00330 00331 /* And now return the (appropriate) compare */ 00332 if (sort->comp_fcn) 00333 { 00334 gint rc = sort->comp_fcn (conva, convb, sort->options, param); 00335 return rc; 00336 } 00337 00338 return sort->obj_cmp (conva, convb); 00339 } 00340 00341 static QofQuery *sortQuery = NULL; 00342 00343 static gint 00344 sort_func (gconstpointer a, gconstpointer b) 00345 { 00346 gint retval; 00347 00348 g_return_val_if_fail (sortQuery, 0); 00349 00350 retval = 00351 cmp_func (&(sortQuery->primary_sort), sortQuery->defaultSort, a, 00352 b); 00353 if (retval == 0) 00354 { 00355 retval = 00356 cmp_func (&(sortQuery->secondary_sort), 00357 sortQuery->defaultSort, 00358 a, b); 00359 if (retval == 0) 00360 { 00361 retval = 00362 cmp_func (&(sortQuery->tertiary_sort), 00363 sortQuery->defaultSort, a, b); 00364 return sortQuery->tertiary_sort.increasing ? 00365 retval : -retval; 00366 } 00367 else 00368 { 00369 return sortQuery->secondary_sort.increasing ? 00370 retval : -retval; 00371 } 00372 } 00373 else 00374 { 00375 return sortQuery->primary_sort.increasing ? retval : -retval; 00376 } 00377 } 00378 00379 /* ==================================================================== */ 00380 /* This is the main workhorse for performing the query. For each 00381 * object, it walks over all of the query terms to see if the 00382 * object passes the seive. 00383 */ 00384 00385 static gint 00386 check_object (QofQuery * q, gpointer object) 00387 { 00388 GList *and_ptr; 00389 GList *or_ptr; 00390 QofQueryTerm *qt; 00391 gint and_terms_ok = 1; 00392 00393 for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) 00394 { 00395 and_terms_ok = 1; 00396 for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) 00397 { 00398 qt = (QofQueryTerm *) (and_ptr->data); 00399 if (qt->param_fcns && qt->pred_fcn) 00400 { 00401 GSList *node; 00402 QofParam *param = NULL; 00403 gpointer conv_obj = object; 00404 00405 /* iterate through the conversions */ 00406 for (node = qt->param_fcns; node; node = node->next) 00407 { 00408 param = node->data; 00409 00410 /* The last term is the actual parameter getter */ 00411 if (!node->next) 00412 break; 00413 00414 conv_obj = param->param_getfcn (conv_obj, param); 00415 } 00416 00417 if (((qt->pred_fcn) (conv_obj, param, 00418 qt->pdata)) == qt->invert) 00419 { 00420 and_terms_ok = 0; 00421 break; 00422 } 00423 } 00424 else 00425 { 00426 /* XXX: Don't know how to do this conversion -- do we care? */ 00427 } 00428 } 00429 if (and_terms_ok) 00430 { 00431 return 1; 00432 } 00433 } 00434 00435 /* If there are no terms, assume a "match any" applies. 00436 * A query with no terms is still meaningful, since the user 00437 * may want to get all objects, but in a particular sorted 00438 * order. 00439 */ 00440 if (NULL == q->terms) 00441 return 1; 00442 return 0; 00443 } 00444 00445 /* walk the list of parameters, starting with the given object, and 00446 * compile the list of parameter get-functions. Save the last valid 00447 * parameter definition in "final" and return the list of functions. 00448 * 00449 * returns NULL if the first parameter is bad (and final is unchanged). 00450 */ 00451 static GSList * 00452 compile_params (GSList * param_list, QofIdType start_obj, 00453 QofParam const **final) 00454 { 00455 const QofParam *objDef = NULL; 00456 GSList *fcns = NULL; 00457 00458 ENTER ("param_list=%p id=%s", param_list, start_obj); 00459 g_return_val_if_fail (param_list, NULL); 00460 g_return_val_if_fail (start_obj, NULL); 00461 g_return_val_if_fail (final, NULL); 00462 00463 for (; param_list; param_list = param_list->next) 00464 { 00465 QofIdType param_name = param_list->data; 00466 objDef = qof_class_get_parameter (start_obj, param_name); 00467 00468 /* If it doesn't exist, then we've reached the end */ 00469 if (!objDef) 00470 break; 00471 00472 /* Save off this parameter */ 00473 fcns = g_slist_prepend (fcns, (gpointer) objDef); 00474 00475 /* Save this off, just in case */ 00476 *final = objDef; 00477 00478 /* And reset for the next parameter */ 00479 start_obj = (QofIdType) objDef->param_type; 00480 } 00481 00482 LEAVE ("fcns=%p", fcns); 00483 return (g_slist_reverse (fcns)); 00484 } 00485 00486 static void 00487 compile_sort (QofQuerySort * sort, QofIdType obj) 00488 { 00489 const QofParam *resObj = NULL; 00490 00491 ENTER ("sort=%p id=%s params=%p", sort, obj, sort->param_list); 00492 sort->use_default = FALSE; 00493 00494 g_slist_free (sort->param_fcns); 00495 sort->param_fcns = NULL; 00496 sort->comp_fcn = NULL; 00497 sort->obj_cmp = NULL; 00498 00499 /* An empty param_list implies "no sort" */ 00500 if (!sort->param_list) 00501 { 00502 LEAVE (" "); 00503 return; 00504 } 00505 00506 /* Walk the parameter list of obtain the parameter functions */ 00507 sort->param_fcns = compile_params (sort->param_list, obj, &resObj); 00508 00509 /* If we have valid parameters, grab the compare function, 00510 * If not, check if this is the default sort. 00511 */ 00512 if (sort->param_fcns) 00513 { 00514 sort->comp_fcn = qof_query_core_get_compare (resObj->param_type); 00515 00516 /* Hrm, perhaps this is an object compare, not a core compare? */ 00517 if (sort->comp_fcn == NULL) 00518 { 00519 sort->obj_cmp = 00520 qof_class_get_default_sort (resObj->param_type); 00521 } 00522 } 00523 else if (!safe_strcmp (sort->param_list->data, QUERY_DEFAULT_SORT)) 00524 { 00525 sort->use_default = TRUE; 00526 } 00527 LEAVE ("sort=%p id=%s", sort, obj); 00528 } 00529 00530 static void 00531 compile_terms (QofQuery * q) 00532 { 00533 GList *or_ptr, *and_ptr, *node; 00534 00535 ENTER (" query=%p", q); 00536 /* Find the specific functions for this Query. Note that the 00537 * Query's search_for should now be set to the new type. 00538 */ 00539 for (or_ptr = q->terms; or_ptr; or_ptr = or_ptr->next) 00540 { 00541 for (and_ptr = or_ptr->data; and_ptr; and_ptr = and_ptr->next) 00542 { 00543 QofQueryTerm *qt = and_ptr->data; 00544 const QofParam *resObj = NULL; 00545 00546 g_slist_free (qt->param_fcns); 00547 qt->param_fcns = NULL; 00548 00549 /* Walk the parameter list of obtain the parameter functions */ 00550 qt->param_fcns = compile_params (qt->param_list, q->search_for, 00551 &resObj); 00552 00553 /* If we have valid parameters, grab the predicate function, 00554 * If not, see if this is the default sort. 00555 */ 00556 00557 if (qt->param_fcns) 00558 qt->pred_fcn = 00559 qof_query_core_get_predicate (resObj->param_type); 00560 else 00561 qt->pred_fcn = NULL; 00562 } 00563 } 00564 00565 /* Update the sort functions */ 00566 compile_sort (&(q->primary_sort), q->search_for); 00567 compile_sort (&(q->secondary_sort), q->search_for); 00568 compile_sort (&(q->tertiary_sort), q->search_for); 00569 00570 q->defaultSort = qof_class_get_default_sort (q->search_for); 00571 00572 /* Now compile the backend instances */ 00573 for (node = q->books; node; node = node->next) 00574 { 00575 QofBook *book = node->data; 00576 QofBackend *be = book->backend; 00577 00578 if (be && be->compile_query) 00579 { 00580 gpointer result = (be->compile_query) (be, q); 00581 if (result) 00582 g_hash_table_insert (q->be_compiled, book, result); 00583 } 00584 } 00585 LEAVE (" query=%p", q); 00586 } 00587 00588 static void 00589 check_item_cb (gpointer object, gpointer user_data) 00590 { 00591 QofQueryCB *ql = user_data; 00592 00593 if (!object || !ql) 00594 return; 00595 00596 if (check_object (ql->query, object)) 00597 { 00598 ql->list = g_list_prepend (ql->list, object); 00599 ql->count++; 00600 } 00601 return; 00602 } 00603 00604 static int 00605 param_list_cmp (GSList * l1, GSList * l2) 00606 { 00607 while (1) 00608 { 00609 int ret; 00610 00611 /* Check the easy stuff */ 00612 if (!l1 && !l2) 00613 return 0; 00614 if (!l1 && l2) 00615 return -1; 00616 if (l1 && !l2) 00617 return 1; 00618 00619 ret = safe_strcmp (l1->data, l2->data); 00620 if (ret) 00621 return ret; 00622 00623 l1 = l1->next; 00624 l2 = l2->next; 00625 } 00626 } 00627 00628 static GList * 00629 merge_books (GList * l1, GList * l2) 00630 { 00631 GList *res = NULL; 00632 GList *node; 00633 00634 res = g_list_copy (l1); 00635 00636 for (node = l2; node; node = node->next) 00637 { 00638 if (g_list_index (res, node->data) == -1) 00639 res = g_list_prepend (res, node->data); 00640 } 00641 00642 return res; 00643 } 00644 00645 static gboolean 00646 query_free_compiled (gpointer key, gpointer value, 00647 gpointer user_data __attribute__ ((unused))) 00648 { 00649 QofBook *book = key; 00650 QofBackend *be = book->backend; 00651 00652 if (be && be->free_query) 00653 (be->free_query) (be, value); 00654 00655 return TRUE; 00656 } 00657 00658 /* clear out any cached query_compilations */ 00659 static void 00660 query_clear_compiles (QofQuery * q) 00661 { 00662 g_hash_table_foreach_remove (q->be_compiled, query_free_compiled, 00663 NULL); 00664 } 00665 00666 /********************************************************************/ 00667 /* PUBLISHED API FUNCTIONS */ 00668 00669 GSList * 00670 qof_query_build_param_list (gchar const *param, ...) 00671 { 00672 GSList *param_list = NULL; 00673 gchar const *this_param; 00674 va_list ap; 00675 00676 if (!param) 00677 return NULL; 00678 00679 va_start (ap, param); 00680 00681 for (this_param = param; this_param; 00682 this_param = va_arg (ap, const gchar *)) 00683 param_list = g_slist_prepend (param_list, (gpointer) this_param); 00684 00685 va_end (ap); 00686 00687 return g_slist_reverse (param_list); 00688 } 00689 00690 void 00691 qof_query_add_term (QofQuery * q, GSList * param_list, 00692 QofQueryPredData * pred_data, QofQueryOp op) 00693 { 00694 QofQueryTerm *qt; 00695 QofQuery *qr, *qs; 00696 00697 if (!q || !param_list || !pred_data) 00698 return; 00699 00700 qt = g_new0 (QofQueryTerm, 1); 00701 qt->param_list = param_list; 00702 qt->pdata = pred_data; 00703 qs = qof_query_create (); 00704 query_init (qs, qt); 00705 00706 if (qof_query_has_terms (q)) 00707 qr = qof_query_merge (q, qs, op); 00708 else 00709 qr = qof_query_merge (q, qs, QOF_QUERY_OR); 00710 00711 swap_terms (q, qr); 00712 qof_query_destroy (qs); 00713 qof_query_destroy (qr); 00714 } 00715 00716 void 00717 qof_query_purge_terms (QofQuery * q, GSList * param_list) 00718 { 00719 QofQueryTerm *qt; 00720 GList *or, *and; 00721 00722 if (!q || !param_list) 00723 return; 00724 00725 for (or = q->terms; or; or = or->next) 00726 { 00727 for (and = or->data; and; and = and->next) 00728 { 00729 qt = and->data; 00730 if (!param_list_cmp (qt->param_list, param_list)) 00731 { 00732 if (g_list_length (or->data) == 1) 00733 { 00734 q->terms = g_list_remove_link (q->terms, or); 00735 g_list_free_1 (or); 00736 or = q->terms; 00737 break; 00738 } 00739 else 00740 { 00741 or->data = g_list_remove_link (or->data, and); 00742 g_list_free_1 (and); 00743 and = or->data; 00744 if (!and) 00745 break; 00746 } 00747 q->changed = 1; 00748 free_query_term (qt); 00749 } 00750 } 00751 if (!or) 00752 break; 00753 } 00754 } 00755 00756 GList * 00757 qof_query_run (QofQuery * q) 00758 { 00759 GList *matching_objects = NULL; 00760 GList *node; 00761 gint object_count = 0; 00762 00763 if (!q) 00764 return NULL; 00765 g_return_val_if_fail (q->search_for, NULL); 00766 g_return_val_if_fail (q->books, NULL); 00767 ENTER (" q=%p", q); 00768 00769 /* XXX: Prioritize the query terms? */ 00770 00771 /* prepare the Query for processing */ 00772 if (q->changed) 00773 { 00774 query_clear_compiles (q); 00775 compile_terms (q); 00776 } 00777 00778 /* Maybe log this sucker */ 00779 if (qof_log_check (log_module, QOF_LOG_DETAIL)) 00780 qof_query_print (q); 00781 00782 /* Now run the query over all the objects and save the results */ 00783 { 00784 QofQueryCB qcb; 00785 00786 memset (&qcb, 0, sizeof (qcb)); 00787 qcb.query = q; 00788 00789 /* For each book */ 00790 for (node = q->books; node; node = node->next) 00791 { 00792 QofBook *book = node->data; 00793 QofBackend *be = book->backend; 00794 00795 /* run the query in the backend */ 00796 if (be) 00797 { 00798 gpointer compiled_query = 00799 g_hash_table_lookup (q->be_compiled, book); 00800 00801 if (compiled_query && be->run_query) 00802 { 00803 (be->run_query) (be, compiled_query); 00804 } 00805 } 00806 00807 /* And then iterate over all the objects */ 00808 qof_object_foreach (q->search_for, book, 00809 (QofEntityForeachCB) check_item_cb, &qcb); 00810 } 00811 00812 matching_objects = qcb.list; 00813 object_count = qcb.count; 00814 } 00815 PINFO ("matching objects=%p count=%d", matching_objects, object_count); 00816 00817 /* There is no absolute need to reverse this list, since it's being 00818 * sorted below. However, in the common case, we will be searching 00819 * in a confined location where the objects are already in order, 00820 * thus reversing will put us in the correct order we want and make 00821 * the sorting go much faster. 00822 */ 00823 matching_objects = g_list_reverse (matching_objects); 00824 00825 /* Now sort the matching objects based on the search criteria 00826 * sortQuery is an unforgivable use of static global data... 00827 * I just can't figure out how else to do this sanely. 00828 */ 00829 if (q->primary_sort.comp_fcn || q->primary_sort.obj_cmp || 00830 (q->primary_sort.use_default && q->defaultSort)) 00831 { 00832 sortQuery = q; 00833 matching_objects = g_list_sort (matching_objects, sort_func); 00834 sortQuery = NULL; 00835 } 00836 00837 /* Crop the list to limit the number of splits. */ 00838 if ((object_count > q->max_results) && (q->max_results > -1)) 00839 { 00840 if (q->max_results > 0) 00841 { 00842 GList *mptr; 00843 00844 /* mptr is set to the first node of what will be the new list */ 00845 mptr = 00846 g_list_nth (matching_objects, 00847 object_count - q->max_results); 00848 /* mptr should not be NULL, but let's be safe */ 00849 if (mptr != NULL) 00850 { 00851 if (mptr->prev != NULL) 00852 mptr->prev->next = NULL; 00853 mptr->prev = NULL; 00854 } 00855 g_list_free (matching_objects); 00856 matching_objects = mptr; 00857 } 00858 else 00859 { 00860 /* q->max_results == 0 */ 00861 g_list_free (matching_objects); 00862 matching_objects = NULL; 00863 } 00864 object_count = q->max_results; 00865 } 00866 00867 q->changed = 0; 00868 00869 g_list_free (q->results); 00870 q->results = matching_objects; 00871 00872 LEAVE (" q=%p", q); 00873 return matching_objects; 00874 } 00875 00876 GList * 00877 qof_query_last_run (QofQuery * query) 00878 { 00879 if (!query) 00880 return NULL; 00881 00882 return query->results; 00883 } 00884 00885 void 00886 qof_query_clear (QofQuery * query) 00887 { 00888 QofQuery *q2 = qof_query_create (); 00889 swap_terms (query, q2); 00890 qof_query_destroy (q2); 00891 00892 g_list_free (query->books); 00893 query->books = NULL; 00894 g_list_free (query->results); 00895 query->results = NULL; 00896 query->changed = 1; 00897 } 00898 00899 QofQuery * 00900 qof_query_create (void) 00901 { 00902 QofQuery *qp = g_new0 (QofQuery, 1); 00903 qp->be_compiled = g_hash_table_new (g_direct_hash, g_direct_equal); 00904 query_init (qp, NULL); 00905 return qp; 00906 } 00907 00908 void 00909 qof_query_search_for (QofQuery * q, QofIdTypeConst obj_type) 00910 { 00911 if (!q || !obj_type) 00912 return; 00913 00914 if (safe_strcmp (q->search_for, obj_type)) 00915 { 00916 q->search_for = (QofIdType) obj_type; 00917 q->changed = 1; 00918 } 00919 } 00920 00921 QofQuery * 00922 qof_query_create_for (QofIdTypeConst obj_type) 00923 { 00924 QofQuery *q; 00925 if (!obj_type) 00926 return NULL; 00927 q = qof_query_create (); 00928 qof_query_search_for (q, obj_type); 00929 return q; 00930 } 00931 00932 gint 00933 qof_query_has_terms (QofQuery * q) 00934 { 00935 if (!q) 00936 return 0; 00937 return g_list_length (q->terms); 00938 } 00939 00940 gint 00941 qof_query_num_terms (QofQuery * q) 00942 { 00943 GList *o; 00944 gint n = 0; 00945 if (!q) 00946 return 0; 00947 for (o = q->terms; o; o = o->next) 00948 n += g_list_length (o->data); 00949 return n; 00950 } 00951 00952 gboolean 00953 qof_query_has_term_type (QofQuery * q, GSList * term_param) 00954 { 00955 GList *or; 00956 GList *and; 00957 00958 if (!q || !term_param) 00959 return FALSE; 00960 00961 for (or = q->terms; or; or = or->next) 00962 { 00963 for (and = or->data; and; and = and->next) 00964 { 00965 QofQueryTerm *qt = and->data; 00966 if (!param_list_cmp (term_param, qt->param_list)) 00967 return TRUE; 00968 } 00969 } 00970 00971 return FALSE; 00972 } 00973 00974 GSList * 00975 qof_query_get_term_type (QofQuery * q, GSList * term_param) 00976 { 00977 GList *or; 00978 GList *and; 00979 GSList *results = NULL; 00980 00981 if (!q || !term_param) 00982 return FALSE; 00983 00984 for (or = q->terms; or; or = or->next) 00985 { 00986 for (and = or->data; and; and = and->next) 00987 { 00988 QofQueryTerm *qt = and->data; 00989 if (!param_list_cmp (term_param, qt->param_list)) 00990 results = g_slist_append (results, qt->pdata); 00991 } 00992 } 00993 00994 return results; 00995 } 00996 00997 void 00998 qof_query_destroy (QofQuery * q) 00999 { 01000 if (!q) 01001 return; 01002 free_members (q); 01003 query_clear_compiles (q); 01004 g_hash_table_destroy (q->be_compiled); 01005 g_free (q); 01006 } 01007 01008 QofQuery * 01009 qof_query_copy (QofQuery * q) 01010 { 01011 QofQuery *copy; 01012 GHashTable *ht; 01013 01014 if (!q) 01015 return NULL; 01016 copy = qof_query_create (); 01017 ht = copy->be_compiled; 01018 free_members (copy); 01019 01020 memcpy (copy, q, sizeof (QofQuery)); 01021 01022 copy->be_compiled = ht; 01023 copy->terms = copy_or_terms (q->terms); 01024 copy->books = g_list_copy (q->books); 01025 copy->results = g_list_copy (q->results); 01026 01027 copy_sort (&(copy->primary_sort), &(q->primary_sort)); 01028 copy_sort (&(copy->secondary_sort), &(q->secondary_sort)); 01029 copy_sort (&(copy->tertiary_sort), &(q->tertiary_sort)); 01030 01031 copy->changed = 1; 01032 01033 return copy; 01034 } 01035 01036 /* ******************************************************************* 01037 * qof_query_invert 01038 * return a newly-allocated Query object which is the 01039 * logical inverse of the original. 01040 ********************************************************************/ 01041 01042 QofQuery * 01043 qof_query_invert (QofQuery * q) 01044 { 01045 QofQuery *retval; 01046 QofQuery *right, *left, *iright, *ileft; 01047 QofQueryTerm *qt; 01048 GList *aterms; 01049 GList *cur; 01050 GList *new_oterm; 01051 gint num_or_terms; 01052 01053 if (!q) 01054 return NULL; 01055 01056 num_or_terms = g_list_length (q->terms); 01057 01058 switch (num_or_terms) 01059 { 01060 case 0: 01061 retval = qof_query_create (); 01062 retval->max_results = q->max_results; 01063 break; 01064 01065 /* This is the DeMorgan expansion for a single AND expression. */ 01066 /* !(abc) = !a + !b + !c */ 01067 case 1: 01068 retval = qof_query_create (); 01069 retval->max_results = q->max_results; 01070 retval->books = g_list_copy (q->books); 01071 retval->search_for = q->search_for; 01072 retval->changed = 1; 01073 01074 aterms = g_list_nth_data (q->terms, 0); 01075 new_oterm = NULL; 01076 for (cur = aterms; cur; cur = cur->next) 01077 { 01078 qt = copy_query_term (cur->data); 01079 qt->invert = !(qt->invert); 01080 new_oterm = g_list_append (NULL, qt); 01081 01082 /* g_list_append() can take forever, so let's do this for speed 01083 * in "large" queries. 01084 */ 01085 retval->terms = g_list_reverse (retval->terms); 01086 retval->terms = g_list_prepend (retval->terms, new_oterm); 01087 retval->terms = g_list_reverse (retval->terms); 01088 } 01089 break; 01090 01091 /* If there are multiple OR-terms, we just recurse by 01092 * breaking it down to !(a + b + c) = 01093 * !a * !(b + c) = !a * !b * !c. */ 01094 default: 01095 right = qof_query_create (); 01096 right->terms = copy_or_terms (g_list_nth (q->terms, 1)); 01097 01098 left = qof_query_create (); 01099 left->terms = g_list_append (NULL, 01100 copy_and_terms (g_list_nth_data (q->terms, 0))); 01101 01102 iright = qof_query_invert (right); 01103 ileft = qof_query_invert (left); 01104 01105 retval = qof_query_merge (iright, ileft, QOF_QUERY_AND); 01106 retval->books = g_list_copy (q->books); 01107 retval->max_results = q->max_results; 01108 retval->search_for = q->search_for; 01109 retval->changed = 1; 01110 01111 qof_query_destroy (iright); 01112 qof_query_destroy (ileft); 01113 qof_query_destroy (right); 01114 qof_query_destroy (left); 01115 break; 01116 } 01117 01118 return retval; 01119 } 01120 01121 /* ******************************************************************* 01122 * qof_query_merge 01123 * combine 2 Query objects by the logical operation in "op". 01124 ********************************************************************/ 01125 01126 QofQuery * 01127 qof_query_merge (QofQuery * q1, QofQuery * q2, QofQueryOp op) 01128 { 01129 01130 QofQuery *retval = NULL; 01131 QofQuery *i1, *i2; 01132 QofQuery *t1, *t2; 01133 GList *i, *j; 01134 QofIdType search_for; 01135 01136 if (!q1) 01137 return q2; 01138 if (!q2) 01139 return q1; 01140 01141 if (q1->search_for && q2->search_for) 01142 g_return_val_if_fail (safe_strcmp (q1->search_for, 01143 q2->search_for) == 0, NULL); 01144 01145 search_for = (q1->search_for ? q1->search_for : q2->search_for); 01146 01147 /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty. 01148 * The goal of this tweak is to all the user to start with 01149 * an empty q1 and then append to it recursively 01150 * (and q1 (and q2 (and q3 (and q4 ....)))) 01151 * without bombing out because the append started with an 01152 * empty list. 01153 * We do essentially the same check in qof_query_add_term() 01154 * so that the first term added to an empty query doesn't screw up. 01155 */ 01156 if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1))) 01157 { 01158 op = QOF_QUERY_OR; 01159 } 01160 01161 switch (op) 01162 { 01163 case QOF_QUERY_OR: 01164 retval = qof_query_create (); 01165 retval->terms = 01166 g_list_concat (copy_or_terms (q1->terms), 01167 copy_or_terms (q2->terms)); 01168 retval->books = merge_books (q1->books, q2->books); 01169 retval->max_results = q1->max_results; 01170 retval->changed = 1; 01171 break; 01172 01173 case QOF_QUERY_AND: 01174 retval = qof_query_create (); 01175 retval->books = merge_books (q1->books, q2->books); 01176 retval->max_results = q1->max_results; 01177 retval->changed = 1; 01178 01179 /* g_list_append() can take forever, so let's build the list in 01180 * reverse and then reverse it at the end, to deal better with 01181 * "large" queries. 01182 */ 01183 for (i = q1->terms; i; i = i->next) 01184 { 01185 for (j = q2->terms; j; j = j->next) 01186 { 01187 retval->terms = 01188 g_list_prepend (retval->terms, 01189 g_list_concat 01190 (copy_and_terms (i->data), copy_and_terms (j->data))); 01191 } 01192 } 01193 retval->terms = g_list_reverse (retval->terms); 01194 break; 01195 01196 case QOF_QUERY_NAND: 01197 /* !(a*b) = (!a + !b) */ 01198 i1 = qof_query_invert (q1); 01199 i2 = qof_query_invert (q2); 01200 retval = qof_query_merge (i1, i2, QOF_QUERY_OR); 01201 qof_query_destroy (i1); 01202 qof_query_destroy (i2); 01203 break; 01204 01205 case QOF_QUERY_NOR: 01206 /* !(a+b) = (!a*!b) */ 01207 i1 = qof_query_invert (q1); 01208 i2 = qof_query_invert (q2); 01209 retval = qof_query_merge (i1, i2, QOF_QUERY_AND); 01210 qof_query_destroy (i1); 01211 qof_query_destroy (i2); 01212 break; 01213 01214 case QOF_QUERY_XOR: 01215 /* a xor b = (a * !b) + (!a * b) */ 01216 i1 = qof_query_invert (q1); 01217 i2 = qof_query_invert (q2); 01218 t1 = qof_query_merge (q1, i2, QOF_QUERY_AND); 01219 t2 = qof_query_merge (i1, q2, QOF_QUERY_AND); 01220 retval = qof_query_merge (t1, t2, QOF_QUERY_OR); 01221 01222 qof_query_destroy (i1); 01223 qof_query_destroy (i2); 01224 qof_query_destroy (t1); 01225 qof_query_destroy (t2); 01226 break; 01227 } 01228 01229 retval->search_for = search_for; 01230 return retval; 01231 } 01232 01233 void 01234 qof_query_merge_in_place (QofQuery * q1, QofQuery * q2, QofQueryOp op) 01235 { 01236 QofQuery *tmp_q; 01237 01238 if (!q1 || !q2) 01239 return; 01240 01241 tmp_q = qof_query_merge (q1, q2, op); 01242 swap_terms (q1, tmp_q); 01243 qof_query_destroy (tmp_q); 01244 } 01245 01246 void 01247 qof_query_set_sort_order (QofQuery * q, 01248 GSList * params1, GSList * params2, GSList * params3) 01249 { 01250 if (!q) 01251 return; 01252 if (q->primary_sort.param_list) 01253 g_slist_free (q->primary_sort.param_list); 01254 q->primary_sort.param_list = params1; 01255 q->primary_sort.options = 0; 01256 01257 if (q->secondary_sort.param_list) 01258 g_slist_free (q->secondary_sort.param_list); 01259 q->secondary_sort.param_list = params2; 01260 q->secondary_sort.options = 0; 01261 01262 if (q->tertiary_sort.param_list) 01263 g_slist_free (q->tertiary_sort.param_list); 01264 q->tertiary_sort.param_list = params3; 01265 q->tertiary_sort.options = 0; 01266 01267 q->changed = 1; 01268 } 01269 01270 void 01271 qof_query_set_sort_options (QofQuery * q, gint prim_op, gint sec_op, 01272 gint tert_op) 01273 { 01274 if (!q) 01275 return; 01276 q->primary_sort.options = prim_op; 01277 q->secondary_sort.options = sec_op; 01278 q->tertiary_sort.options = tert_op; 01279 } 01280 01281 void 01282 qof_query_set_sort_increasing (QofQuery * q, gboolean prim_inc, 01283 gboolean sec_inc, gboolean tert_inc) 01284 { 01285 if (!q) 01286 return; 01287 q->primary_sort.increasing = prim_inc; 01288 q->secondary_sort.increasing = sec_inc; 01289 q->tertiary_sort.increasing = tert_inc; 01290 } 01291 01292 void 01293 qof_query_set_max_results (QofQuery * q, gint n) 01294 { 01295 if (!q) 01296 return; 01297 q->max_results = n; 01298 } 01299 01300 void 01301 qof_query_add_guid_list_match (QofQuery * q, GSList * param_list, 01302 GList * guid_list, QofGuidMatch options, QofQueryOp op) 01303 { 01304 QofQueryPredData *pdata; 01305 01306 if (!q || !param_list) 01307 return; 01308 01309 if (!guid_list) 01310 g_return_if_fail (options == QOF_GUID_MATCH_NULL); 01311 01312 pdata = qof_query_guid_predicate (options, guid_list); 01313 qof_query_add_term (q, param_list, pdata, op); 01314 } 01315 01316 void 01317 qof_query_add_guid_match (QofQuery * q, GSList * param_list, 01318 const GUID * guid, QofQueryOp op) 01319 { 01320 GList *g = NULL; 01321 01322 if (!q || !param_list) 01323 return; 01324 01325 if (guid) 01326 g = g_list_prepend (g, (gpointer) guid); 01327 01328 qof_query_add_guid_list_match (q, param_list, g, 01329 g ? QOF_GUID_MATCH_ANY : QOF_GUID_MATCH_NULL, op); 01330 01331 g_list_free (g); 01332 } 01333 01334 void 01335 qof_query_set_book (QofQuery * q, QofBook * book) 01336 { 01337 GSList *slist = NULL; 01338 if (!q || !book) 01339 return; 01340 01341 /* Make sure this book is only in the list once */ 01342 if (g_list_index (q->books, book) == -1) 01343 q->books = g_list_prepend (q->books, book); 01344 01345 slist = g_slist_prepend (slist, QOF_PARAM_GUID); 01346 slist = g_slist_prepend (slist, QOF_PARAM_BOOK); 01347 qof_query_add_guid_match (q, slist, 01348 qof_entity_get_guid ((QofEntity*)book), QOF_QUERY_AND); 01349 } 01350 01351 GList * 01352 qof_query_get_books (QofQuery * q) 01353 { 01354 if (!q) 01355 return NULL; 01356 return q->books; 01357 } 01358 01359 void 01360 qof_query_add_boolean_match (QofQuery * q, GSList * param_list, 01361 gboolean value, QofQueryOp op) 01362 { 01363 QofQueryPredData *pdata; 01364 if (!q || !param_list) 01365 return; 01366 01367 pdata = qof_query_boolean_predicate (QOF_COMPARE_EQUAL, value); 01368 qof_query_add_term (q, param_list, pdata, op); 01369 } 01370 01371 /**********************************************************************/ 01372 /* PRIVATE PUBLISHED API FUNCTIONS */ 01373 01374 void 01375 qof_query_init (void) 01376 { 01377 ENTER (" "); 01378 qof_query_core_init (); 01379 qof_class_init (); 01380 qof_date_init (); 01381 LEAVE ("Completed initialization of QofQuery"); 01382 } 01383 01384 void 01385 qof_query_shutdown (void) 01386 { 01387 qof_class_shutdown (); 01388 qof_query_core_shutdown (); 01389 } 01390 01391 gint 01392 qof_query_get_max_results (QofQuery * q) 01393 { 01394 if (!q) 01395 return 0; 01396 return q->max_results; 01397 } 01398 01399 QofIdType 01400 qof_query_get_search_for (QofQuery * q) 01401 { 01402 if (!q) 01403 return NULL; 01404 return q->search_for; 01405 } 01406 01407 GList * 01408 qof_query_get_terms (QofQuery * q) 01409 { 01410 if (!q) 01411 return NULL; 01412 return q->terms; 01413 } 01414 01415 GSList * 01416 qof_query_term_get_param_path (QofQueryTerm * qt) 01417 { 01418 if (!qt) 01419 return NULL; 01420 return qt->param_list; 01421 } 01422 01423 QofQueryPredData * 01424 qof_query_term_get_pred_data (QofQueryTerm * qt) 01425 { 01426 if (!qt) 01427 return NULL; 01428 return qt->pdata; 01429 } 01430 01431 gboolean 01432 qof_query_term_is_inverted (QofQueryTerm * qt) 01433 { 01434 if (!qt) 01435 return FALSE; 01436 return qt->invert; 01437 } 01438 01439 void 01440 qof_query_get_sorts (QofQuery * q, QofQuerySort ** primary, 01441 QofQuerySort ** secondary, QofQuerySort ** tertiary) 01442 { 01443 if (!q) 01444 return; 01445 if (primary) 01446 *primary = &(q->primary_sort); 01447 if (secondary) 01448 *secondary = &(q->secondary_sort); 01449 if (tertiary) 01450 *tertiary = &(q->tertiary_sort); 01451 } 01452 01453 GSList * 01454 qof_query_sort_get_param_path (QofQuerySort * qs) 01455 { 01456 if (!qs) 01457 return NULL; 01458 return qs->param_list; 01459 } 01460 01461 gint 01462 qof_query_sort_get_sort_options (QofQuerySort * qs) 01463 { 01464 if (!qs) 01465 return 0; 01466 return qs->options; 01467 } 01468 01469 gboolean 01470 qof_query_sort_get_increasing (QofQuerySort * qs) 01471 { 01472 if (!qs) 01473 return FALSE; 01474 return qs->increasing; 01475 } 01476 01477 static gboolean 01478 qof_query_term_equal (QofQueryTerm * qt1, QofQueryTerm * qt2) 01479 { 01480 if (qt1 == qt2) 01481 return TRUE; 01482 if (!qt1 || !qt2) 01483 return FALSE; 01484 01485 if (qt1->invert != qt2->invert) 01486 return FALSE; 01487 if (param_list_cmp (qt1->param_list, qt2->param_list)) 01488 return FALSE; 01489 return qof_query_core_predicate_equal (qt1->pdata, qt2->pdata); 01490 } 01491 01492 static gboolean 01493 qof_query_sort_equal (QofQuerySort * qs1, QofQuerySort * qs2) 01494 { 01495 if (qs1 == qs2) 01496 return TRUE; 01497 if (!qs1 || !qs2) 01498 return FALSE; 01499 01500 /* "Empty" sorts are equivalent, regardless of the flags */ 01501 if (!qs1->param_list && !qs2->param_list) 01502 return TRUE; 01503 01504 if (qs1->options != qs2->options) 01505 return FALSE; 01506 if (qs1->increasing != qs2->increasing) 01507 return FALSE; 01508 return (param_list_cmp (qs1->param_list, qs2->param_list) == 0); 01509 } 01510 01511 gboolean 01512 qof_query_equal (QofQuery * q1, QofQuery * q2) 01513 { 01514 GList *or1, *or2; 01515 01516 if (q1 == q2) 01517 return TRUE; 01518 if (!q1 || !q2) 01519 return FALSE; 01520 01521 if (g_list_length (q1->terms) != g_list_length (q2->terms)) 01522 return FALSE; 01523 if (q1->max_results != q2->max_results) 01524 return FALSE; 01525 01526 for (or1 = q1->terms, or2 = q2->terms; or1; 01527 or1 = or1->next, or2 = or2->next) 01528 { 01529 GList *and1, *and2; 01530 01531 and1 = or1->data; 01532 and2 = or2->data; 01533 01534 if (g_list_length (and1) != g_list_length (and2)) 01535 return FALSE; 01536 01537 for (; and1; and1 = and1->next, and2 = and2->next) 01538 if (!qof_query_term_equal (and1->data, and2->data)) 01539 return FALSE; 01540 } 01541 01542 if (!qof_query_sort_equal (&(q1->primary_sort), &(q2->primary_sort))) 01543 return FALSE; 01544 if (!qof_query_sort_equal (&(q1->secondary_sort), 01545 &(q2->secondary_sort))) 01546 return FALSE; 01547 if (!qof_query_sort_equal (&(q1->tertiary_sort), &(q2->tertiary_sort))) 01548 return FALSE; 01549 01550 return TRUE; 01551 } 01552 01553 /* **************************************************************************/ 01554 /* Query Print functions for use with qof_log_set_level. 01555 */ 01556 01557 /* Static prototypes */ 01558 static GList *qof_query_printSearchFor (QofQuery * query, GList * output); 01559 static GList *qof_query_printTerms (QofQuery * query, GList * output); 01560 static GList *qof_query_printSorts (QofQuerySort * s[], 01561 const gint numSorts, GList * output); 01562 static GList *qof_query_printAndTerms (GList * terms, GList * output); 01563 static gchar *qof_query_printStringForHow (QofQueryCompare how); 01564 static gchar *qof_query_printStringMatch (QofStringMatch s); 01565 static gchar *qof_query_printDateMatch (QofDateMatch d); 01566 static gchar *qof_query_printNumericMatch (QofNumericMatch n); 01567 static gchar *qof_query_printGuidMatch (QofGuidMatch g); 01568 static gchar *qof_query_printCharMatch (QofCharMatch c); 01569 static GList *qof_query_printPredData (QofQueryPredData * pd, GList * lst); 01570 static GString *qof_query_printParamPath (GSList * parmList); 01571 static void qof_query_printValueForParam (QofQueryPredData * pd, 01572 GString * gs); 01573 static void qof_query_printOutput (GList * output); 01574 01576 void 01577 qof_query_print (QofQuery * query) 01578 { 01579 GList *output; 01580 GString *str; 01581 QofQuerySort *s[3]; 01582 gint maxResults = 0, numSorts = 3; 01583 01584 ENTER (" "); 01585 01586 if (!query) 01587 { 01588 LEAVE ("query is (null)"); 01589 return; 01590 } 01591 01592 output = NULL; 01593 str = NULL; 01594 maxResults = qof_query_get_max_results (query); 01595 01596 output = qof_query_printSearchFor (query, output); 01597 output = qof_query_printTerms (query, output); 01598 01599 qof_query_get_sorts (query, &s[0], &s[1], &s[2]); 01600 01601 if (s[0]) 01602 { 01603 output = qof_query_printSorts (s, numSorts, output); 01604 } 01605 01606 str = g_string_new (" "); 01607 g_string_printf (str, "Maximum number of results: %d", maxResults); 01608 output = g_list_append (output, str); 01609 01610 qof_query_printOutput (output); 01611 LEAVE (" "); 01612 } 01613 01614 static void 01615 qof_query_printOutput (GList * output) 01616 { 01617 GList *lst; 01618 01619 for (lst = output; lst; lst = lst->next) 01620 { 01621 GString *line = (GString *) lst->data; 01622 01623 DEBUG (" %s", line->str); 01624 g_string_free (line, TRUE); 01625 line = NULL; 01626 } 01627 } 01628 01629 /* 01630 Get the search_for type--This is the type of Object 01631 we are searching for (SPLIT, TRANS, etc) 01632 */ 01633 static GList * 01634 qof_query_printSearchFor (QofQuery * query, GList * output) 01635 { 01636 QofIdType searchFor; 01637 GString *gs; 01638 01639 searchFor = qof_query_get_search_for (query); 01640 gs = g_string_new ("Query Object Type: "); 01641 g_string_append (gs, (NULL == searchFor) ? "(null)" : searchFor); 01642 output = g_list_append (output, gs); 01643 01644 return output; 01645 } /* qof_query_printSearchFor */ 01646 01647 /* 01648 Run through the terms of the query. This is a outer-inner 01649 loop. The elements of the outer loop are ORed, and the 01650 elements of the inner loop are ANDed. 01651 */ 01652 static GList * 01653 qof_query_printTerms (QofQuery * query, GList * output) 01654 { 01655 01656 GList *terms, *lst; 01657 01658 terms = qof_query_get_terms (query); 01659 01660 for (lst = terms; lst; lst = lst->next) 01661 { 01662 output = 01663 g_list_append (output, g_string_new ("OR Terms:")); 01664 01665 if (lst->data) 01666 { 01667 output = qof_query_printAndTerms (lst->data, output); 01668 } 01669 else 01670 { 01671 output = 01672 g_list_append (output, 01673 g_string_new (" No data for AND terms")); 01674 } 01675 } 01676 01677 return output; 01678 } /* qof_query_printTerms */ 01679 01680 /* 01681 Process the sort parameters 01682 If this function is called, the assumption is that the first sort 01683 not null. 01684 */ 01685 static GList * 01686 qof_query_printSorts (QofQuerySort * s[], const gint numSorts, 01687 GList * output) 01688 { 01689 GSList *gsl, *n = NULL; 01690 gint curSort; 01691 GString *gs = g_string_new ("Sort Parameters: "); 01692 01693 for (curSort = 0; curSort < numSorts; curSort++) 01694 { 01695 gboolean increasing; 01696 if (!s[curSort]) 01697 { 01698 break; 01699 } 01700 increasing = qof_query_sort_get_increasing (s[curSort]); 01701 01702 gsl = qof_query_sort_get_param_path (s[curSort]); 01703 if (gsl) 01704 g_string_append_printf (gs, " Param: "); 01705 for (n = gsl; n; n = n->next) 01706 { 01707 QofIdType param_name = n->data; 01708 if (gsl != n) 01709 g_string_append_printf (gs, " "); 01710 g_string_append_printf (gs, "%s", param_name); 01711 } 01712 if (gsl) 01713 { 01714 g_string_append_printf (gs, " %s ", 01715 increasing ? "DESC" : "ASC"); 01716 g_string_append_printf (gs, " Options: 0x%x ", 01717 s[curSort]->options); 01718 } 01719 } 01720 01721 output = g_list_append (output, gs); 01722 return output; 01723 01724 } /* qof_query_printSorts */ 01725 01726 /* 01727 Process the AND terms of the query. This is a GList 01728 of WHERE terms that will be ANDed 01729 */ 01730 static GList * 01731 qof_query_printAndTerms (GList * terms, GList * output) 01732 { 01733 const gchar *prefix = "AND Terms:"; 01734 QofQueryTerm *qt; 01735 QofQueryPredData *pd; 01736 GSList *path; 01737 GList *lst; 01738 gboolean invert; 01739 01740 output = g_list_append (output, g_string_new (prefix)); 01741 for (lst = terms; lst; lst = lst->next) 01742 { 01743 qt = (QofQueryTerm *) lst->data; 01744 pd = qof_query_term_get_pred_data (qt); 01745 path = qof_query_term_get_param_path (qt); 01746 invert = qof_query_term_is_inverted (qt); 01747 01748 if (invert) 01749 output = 01750 g_list_append (output, g_string_new (" INVERT SENSE ")); 01751 output = g_list_append (output, qof_query_printParamPath (path)); 01752 output = qof_query_printPredData (pd, output); 01753 // output = g_list_append (output, g_string_new(" ")); 01754 } 01755 01756 return output; 01757 } /* qof_query_printAndTerms */ 01758 01759 /* 01760 Process the parameter types of the predicate data 01761 */ 01762 static GString * 01763 qof_query_printParamPath (GSList * parmList) 01764 { 01765 GSList *list = NULL; 01766 GString *gs = g_string_new ("Param List: "); 01767 g_string_append (gs, " "); 01768 for (list = parmList; list; list = list->next) 01769 { 01770 g_string_append (gs, (gchar *) list->data); 01771 if (list->next) 01772 g_string_append (gs, ", "); 01773 } 01774 01775 return gs; 01776 } /* qof_query_printParamPath */ 01777 01778 /* 01779 Process the PredData of the AND terms 01780 */ 01781 static GList * 01782 qof_query_printPredData (QofQueryPredData * pd, GList * lst) 01783 { 01784 GString *gs; 01785 01786 gs = g_string_new ("Pred Data: "); 01787 g_string_append (gs, (gchar *) pd->type_name); 01788 01789 /* Char Predicate and GUID predicate don't use the 'how' field. */ 01790 if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) && 01791 safe_strcmp (pd->type_name, QOF_TYPE_GUID)) 01792 { 01793 g_string_append_printf (gs, " how: %s", 01794 qof_query_printStringForHow (pd->how)); 01795 } 01796 lst = g_list_append (lst, gs); 01797 gs = g_string_new (""); 01798 qof_query_printValueForParam (pd, gs); 01799 lst = g_list_append (lst, gs); 01800 return lst; 01801 } /* qof_query_printPredData */ 01802 01803 /* 01804 Get a string representation for the 01805 QofCompareFunc enum type. 01806 */ 01807 static gchar * 01808 qof_query_printStringForHow (QofQueryCompare how) 01809 { 01810 01811 switch (how) 01812 { 01813 AS_STRING_CASE(QOF_COMPARE_LT,) 01814 AS_STRING_CASE(QOF_COMPARE_LTE,) 01815 AS_STRING_CASE(QOF_COMPARE_EQUAL,) 01816 AS_STRING_CASE(QOF_COMPARE_GT,) 01817 AS_STRING_CASE(QOF_COMPARE_GTE,) 01818 AS_STRING_CASE(QOF_COMPARE_NEQ,) 01819 } 01820 return "INVALID HOW"; 01821 } /* qncQueryPrintStringForHow */ 01822 01823 01824 static void 01825 qof_query_printValueForParam (QofQueryPredData * pd, GString * gs) 01826 { 01827 01828 if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID)) 01829 { 01830 GList *node; 01831 query_guid_t pdata = (query_guid_t) pd; 01832 g_string_append_printf (gs, "Match type %s ", 01833 qof_query_printGuidMatch (pdata->options)); 01834 for (node = pdata->guids; node; node = node->next) 01835 { 01836 /* THREAD-UNSAFE */ 01837 g_string_append_printf (gs, ", guids: %s", 01838 guid_to_string ((GUID *) node->data)); 01839 } 01840 return; 01841 } 01842 if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING)) 01843 { 01844 query_string_t pdata = (query_string_t) pd; 01845 g_string_append_printf (gs, "Match type %s ", 01846 qof_query_printStringMatch (pdata->options)); 01847 g_string_append_printf (gs, " %s string: %s", 01848 pdata->is_regex ? "Regex" : "Not regex", pdata->matchstring); 01849 return; 01850 } 01851 if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC)) 01852 { 01853 query_numeric_t pdata = (query_numeric_t) pd; 01854 g_string_append_printf (gs, "Match type %s ", 01855 qof_query_printNumericMatch (pdata->options)); 01856 g_string_append_printf (gs, " numeric: %s", 01857 qof_numeric_dbg_to_string (pdata->amount)); 01858 return; 01859 } 01860 if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP)) 01861 { 01862 GSList *node; 01863 query_kvp_t pdata = (query_kvp_t) pd; 01864 g_string_append_printf (gs, " kvp path: "); 01865 for (node = pdata->path; node; node = node->next) 01866 { 01867 g_string_append_printf (gs, "/%s", (gchar *) node->data); 01868 } 01869 g_string_append_printf (gs, " kvp value: %s ", 01870 kvp_value_to_string (pdata->value)); 01871 return; 01872 } 01873 if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64)) 01874 { 01875 query_int64_t pdata = (query_int64_t) pd; 01876 g_string_append_printf (gs, " int64: %" G_GINT64_FORMAT, 01877 pdata->val); 01878 return; 01879 } 01880 if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32)) 01881 { 01882 query_int32_t pdata = (query_int32_t) pd; 01883 g_string_append_printf (gs, " int32: %d", pdata->val); 01884 return; 01885 } 01886 if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE)) 01887 { 01888 query_double_t pdata = (query_double_t) pd; 01889 g_string_append_printf (gs, " double: %.18g", pdata->val); 01890 return; 01891 } 01892 if (!safe_strcmp (pd->type_name, QOF_TYPE_TIME)) 01893 { 01894 query_time_t pdata; 01895 QofDate *qd; 01896 01897 pdata = (query_time_t) pd; 01898 qd = qof_date_from_qtime (pdata->qt); 01899 g_string_append_printf (gs, "Match type %s " , 01900 qof_query_printDateMatch (pdata->options)); 01901 g_string_append_printf (gs, "query date: %s", 01902 qof_date_print (qd, QOF_DATE_FORMAT_UTC)); 01903 qof_date_free (qd); 01904 } 01905 if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR)) 01906 { 01907 query_char_t pdata = (query_char_t) pd; 01908 g_string_append_printf (gs, "Match type %s ", 01909 qof_query_printCharMatch (pdata->options)); 01910 g_string_append_printf (gs, " char list: %s", 01911 pdata->char_list); 01912 return; 01913 } 01914 if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN)) 01915 { 01916 query_boolean_t pdata = (query_boolean_t) pd; 01917 g_string_append_printf (gs, " boolean: %s", 01918 pdata->val ? "TRUE" : "FALSE"); 01919 return; 01920 } 01922 return; 01923 } /* qof_query_printValueForParam */ 01924 01925 /* 01926 * Print out a string representation of the 01927 * QofStringMatch enum 01928 */ 01929 static gchar * 01930 qof_query_printStringMatch (QofStringMatch s) 01931 { 01932 switch (s) 01933 { 01934 AS_STRING_CASE(QOF_STRING_MATCH_NORMAL,) 01935 AS_STRING_CASE(QOF_STRING_MATCH_CASEINSENSITIVE,) 01936 } 01937 return "UNKNOWN MATCH TYPE"; 01938 } /* qof_query_printStringMatch */ 01939 01940 /* 01941 * Print out a string representation of the 01942 * QofDateMatch enum 01943 */ 01944 static gchar * 01945 qof_query_printDateMatch (QofDateMatch d) 01946 { 01947 switch (d) 01948 { 01949 AS_STRING_CASE(QOF_DATE_MATCH_NORMAL,) 01950 AS_STRING_CASE(QOF_DATE_MATCH_DAY,) 01951 } 01952 return "UNKNOWN MATCH TYPE"; 01953 } /* qof_query_printDateMatch */ 01954 01955 /* 01956 * Print out a string representation of the 01957 * QofNumericMatch enum 01958 */ 01959 static gchar * 01960 qof_query_printNumericMatch (QofNumericMatch n) 01961 { 01962 switch (n) 01963 { 01964 AS_STRING_CASE(QOF_NUMERIC_MATCH_DEBIT,) 01965 AS_STRING_CASE(QOF_NUMERIC_MATCH_CREDIT,) 01966 AS_STRING_CASE(QOF_NUMERIC_MATCH_ANY,) 01967 } 01968 return "UNKNOWN MATCH TYPE"; 01969 } /* qof_query_printNumericMatch */ 01970 01971 /* 01972 * Print out a string representation of the 01973 * QofGuidMatch enum 01974 */ 01975 static gchar * 01976 qof_query_printGuidMatch (QofGuidMatch g) 01977 { 01978 switch (g) 01979 { 01980 AS_STRING_CASE(QOF_GUID_MATCH_ANY,) 01981 AS_STRING_CASE(QOF_GUID_MATCH_ALL,) 01982 AS_STRING_CASE(QOF_GUID_MATCH_NONE,) 01983 AS_STRING_CASE(QOF_GUID_MATCH_NULL,) 01984 AS_STRING_CASE(QOF_GUID_MATCH_LIST_ANY,) 01985 } 01986 01987 return "UNKNOWN MATCH TYPE"; 01988 } /* qof_query_printGuidMatch */ 01989 01990 /* 01991 * Print out a string representation of the 01992 * QofCharMatch enum 01993 */ 01994 static gchar * 01995 qof_query_printCharMatch (QofCharMatch c) 01996 { 01997 switch (c) 01998 { 01999 AS_STRING_CASE(QOF_CHAR_MATCH_ANY,) 02000 AS_STRING_CASE(QOF_CHAR_MATCH_NONE,) 02001 } 02002 return "UNKNOWN MATCH TYPE"; 02003 } /* qof_query_printGuidMatch */ 02004 02005 /* ======================== END OF FILE =================== */