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