Work on adding kvp queries.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@5770 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Dave Peticolas
2001-11-05 09:18:07 +00:00
parent 213639c861
commit 2176d53e99
8 changed files with 453 additions and 22 deletions

View File

@@ -86,6 +86,7 @@ static int xaccClearedMatchPredicate(Split * s, PredicateData * pd);
static int xaccDateMatchPredicate(Split * s, PredicateData * pd);
static int xaccDescriptionMatchPredicate(Split * s, PredicateData * pd);
static int xaccGUIDMatchPredicate(Split * s, PredicateData * pd);
static int xaccKVPMatchPredicate(Split * s, PredicateData * pd);
static int xaccMemoMatchPredicate(Split * s, PredicateData * pd);
static int xaccNumberMatchPredicate(Split * s, PredicateData * pd);
static int xaccSharePriceMatchPredicate(Split * s, PredicateData * pd);
@@ -150,18 +151,23 @@ xaccQueryPrint(Query * q)
break;
}
case PR_ACTION:
printf ("action sense=%d case sensitive=%d\n", qt->data.str.sense, qt->data.str.case_sens);
printf ("action sense=%d case sensitive=%d\n", qt->data.str.sense,
qt->data.str.case_sens);
printf ("\tmatch string=%s \n", qt->data.str.matchstring);
break;
case PR_AMOUNT:
printf ("amount sense=%d how=%d\n", qt->data.amount.sense, qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, qt->data.amount.amount);
printf ("amount sense=%d how=%d\n", qt->data.amount.sense,
qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn,
qt->data.amount.amount);
break;
case PR_BALANCE:
printf ("balance sense=%d how=%d\n", qt->data.balance.sense, qt->data.balance.how);
printf ("balance sense=%d how=%d\n", qt->data.balance.sense,
qt->data.balance.how);
break;
case PR_CLEARED:
printf ("cleared sense=%d how=%d\n", qt->data.cleared.sense, qt->data.cleared.how);
printf ("cleared sense=%d how=%d\n", qt->data.cleared.sense,
qt->data.cleared.how);
break;
case PR_DATE: {
char buff[40];
@@ -181,7 +187,8 @@ xaccQueryPrint(Query * q)
break;
}
case PR_DESC:
printf ("desc sense=%d case sensitive=%d\n", qt->data.str.sense, qt->data.str.case_sens);
printf ("desc sense=%d case sensitive=%d\n", qt->data.str.sense,
qt->data.str.case_sens);
printf ("\tmatch string=%s \n", qt->data.str.matchstring);
break;
@@ -192,24 +199,48 @@ xaccQueryPrint(Query * q)
printf ("\tguid %s\n", buff);
break;
}
case PR_KVP: {
GSList *node;
char *str;
printf ("kvp sense=%d how=%d where=%d\n", qt->data.kvp.sense,
qt->data.kvp.how, qt->data.kvp.where);
printf ("path:");
for (node = qt->data.kvp.path; node; node = node->next)
{
printf (node->data);
if (node->next)
printf ("/");
}
printf ("\n");
str = kvp_value_to_string (qt->data.kvp.value);
printf ("value: %s\n", str);
g_free (str);
break;
}
case PR_MEMO:
printf ("memo sense=%d case sensitive=%d\n", qt->data.str.sense, qt->data.str.case_sens);
printf ("memo sense=%d case sensitive=%d\n", qt->data.str.sense,
qt->data.str.case_sens);
printf ("\tmatch string=%s \n", qt->data.str.matchstring);
break;
case PR_MISC:
printf ("misc\n");
break;
case PR_NUM:
printf ("num sense=%d case sensitive=%d\n", qt->data.str.sense, qt->data.str.case_sens);
printf ("num sense=%d case sensitive=%d\n", qt->data.str.sense,
qt->data.str.case_sens);
printf ("\tmatch string=%s \n", qt->data.str.matchstring);
break;
case PR_PRICE:
printf ("price sense=%d how=%d\n", qt->data.amount.sense, qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, qt->data.amount.amount);
printf ("price sense=%d how=%d\n", qt->data.amount.sense,
qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn,
qt->data.amount.amount);
break;
case PR_SHRS:
printf ("shrs sense=%d how=%d\n", qt->data.amount.sense, qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn, qt->data.amount.amount);
printf ("shrs sense=%d how=%d\n", qt->data.amount.sense,
qt->data.amount.how);
printf ("\tsign=%d amount=%f\n", qt->data.amount.amt_sgn,
qt->data.amount.amount);
break;
default:
@@ -376,6 +407,13 @@ free_query_term(QueryTerm *qt)
qt->data.acct.account_guids = NULL;
break;
case PD_KVP:
g_slist_free (qt->data.kvp.path);
qt->data.kvp.path = NULL;
kvp_value_delete (qt->data.kvp.value);
qt->data.kvp.value = NULL;
break;
case PD_STRING:
g_free(qt->data.str.matchstring);
qt->data.str.matchstring = NULL;
@@ -416,6 +454,18 @@ copy_query_term(QueryTerm * qt) {
}
break;
case PD_KVP: {
GSList *node;
nqt->data.kvp.path = g_slist_copy (nqt->data.kvp.path);
nqt->data.kvp.value = kvp_value_copy (nqt->data.kvp.value);
for (node = nqt->data.kvp.path; node; node = node->next)
node->data = g_strdup (node->data);
break;
}
case PD_STRING:
nqt->data.str.matchstring = g_strdup(nqt->data.str.matchstring);
break;
@@ -1377,6 +1427,25 @@ xaccQueryTermEqual (QueryTerm *qt1, QueryTerm *qt2)
return FALSE;
break;
case PD_KVP: {
GSList *n1, *n2;
n1 = qt1->data.kvp.path;
n2 = qt2->data.kvp.path;
for ( ; n1 && n2; n1 = n1->next, n2 = n2->next)
if (!safe_strcmp (n1->data, n2->data))
return FALSE;
if (n1 || n2)
return FALSE;
if (kvp_value_compare (qt1->data.kvp.value, qt2->data.kvp.value) != 0)
return FALSE;
break;
}
case PD_MISC:
if (qt1->data.misc.how != qt2->data.misc.how) return FALSE;
if (qt1->data.misc.data != qt2->data.misc.data) return FALSE;
@@ -1463,6 +1532,9 @@ xaccQueryGetPredicate (pr_type_t term_type)
case PR_GUID:
p = & xaccGUIDMatchPredicate;
break;
case PR_KVP:
p = & xaccKVPMatchPredicate;
break;
case PR_MEMO:
p = & xaccMemoMatchPredicate;
break;
@@ -2160,6 +2232,46 @@ xaccQueryAddGUIDMatch(Query * q, const GUID *guid, QueryOp op)
xaccFreeQuery(qr);
}
/********************************************************************
* xaccQueryAddKVPMatch
* Add a 'kvp' filter to an existing query.
********************************************************************/
void
xaccQueryAddKVPMatch(Query *q, GSList *path, const kvp_value *value,
kvp_match_t how, kvp_match_where_t where, QueryOp op)
{
Query * qs = xaccMallocQuery();
QueryTerm * qt = g_new0(QueryTerm, 1);
Query * qr;
GSList * node;
qt->p = &xaccKVPMatchPredicate;
qt->data.type = PD_KVP;
qt->data.base.term_type = PR_KVP;
qt->data.base.sense = 1;
qt->data.kvp.how = how;
qt->data.kvp.where = where;
qt->data.kvp.path = g_slist_copy (path);
qt->data.kvp.value = kvp_value_copy (value);
for (node = qt->data.kvp.path; node; node = node->next)
node->data = g_strdup (node->data);
xaccInitQuery(qs, qt);
xaccQuerySetGroup(qs, q->acct_group);
if(xaccQueryHasTerms(q)) {
qr = xaccQueryMerge(q, qs, op);
}
else {
qr = xaccQueryMerge(q, qs, QUERY_OR);
}
xaccQuerySwapTerms(q, qr);
xaccFreeQuery(qs);
xaccFreeQuery(qr);
}
/*******************************************************************
* xaccQueryPurgeTerms
* delete any terms of a particular type
@@ -2353,7 +2465,6 @@ xaccDescriptionMatchPredicate(Split * s, PredicateData * pd) {
static int
xaccGUIDMatchPredicate(Split * s, PredicateData * pd)
{
GUIDPredicateData *gpd;
GUID *guid;
g_return_val_if_fail (s, 0);
@@ -2382,6 +2493,85 @@ xaccGUIDMatchPredicate(Split * s, PredicateData * pd)
}
}
/*******************************************************************
* xaccKVPMatchPredicate
*******************************************************************/
static int
kvp_match_helper (GSList *path, kvp_value *value, kvp_match_t how,
kvp_frame *frame)
{
kvp_value *value_2;
int compare;
if (!path || !value || !frame || !how) return 0;
value_2 = kvp_frame_get_slot_path_gslist (frame, path);
if (!value_2)
return 0;
if (kvp_value_get_type (value) != kvp_value_get_type (value_2))
return 0;
compare = kvp_value_compare (value_2, value);
switch (how)
{
case KVP_MATCH_LT:
return (compare < 0);
case KVP_MATCH_LTE:
return (compare <= 0);
case KVP_MATCH_EQ:
return (compare == 0);
case KVP_MATCH_GTE:
return (compare >= 0);
case KVP_MATCH_GT:
return (compare > 0);
default:
PWARN ("bad match type: %d", how);
return FALSE;
}
}
static int
xaccKVPMatchPredicate(Split * s, PredicateData * pd)
{
KVPPredicateData *kpd;
g_return_val_if_fail (s, FALSE);
g_return_val_if_fail (pd, FALSE);
g_return_val_if_fail (pd->type == PD_KVP, FALSE);
kpd = &pd->kvp;
if (kpd->where && KVP_MATCH_SPLIT)
{
kvp_frame *frame = xaccSplitGetSlots (s);
if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame))
return TRUE;
}
if (kpd->where && KVP_MATCH_TRANS)
{
Transaction *trans = xaccSplitGetParent (s);
kvp_frame *frame = xaccTransGetSlots (trans);
if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame))
return TRUE;
}
if (kpd->where && KVP_MATCH_ACCOUNT)
{
Account *account = xaccSplitGetAccount (s);
kvp_frame *frame = xaccAccountGetSlots (account);
if (kvp_match_helper (kpd->path, kpd->value, kpd->how, frame))
return TRUE;
}
return 0;
}
/*******************************************************************
* xaccNumberMatchPredicate
*******************************************************************/
@@ -2467,15 +2657,12 @@ xaccSharePriceMatchPredicate(Split * s, PredicateData * pd) {
g_return_val_if_fail(s && pd, FALSE);
g_return_val_if_fail(pd->type == PD_AMOUNT, FALSE);
acct = xaccSplitGetAccount(s);
type = xaccAccountGetType(acct);
if((type != STOCK) && (type != MUTUAL)) {
return 0;
}
splitamt = DxaccSplitGetSharePrice(s);
return value_match_predicate(splitamt, pd);
}
@@ -2495,10 +2682,6 @@ xaccSharesMatchPredicate(Split * s, PredicateData * pd) {
acct = xaccSplitGetAccount(s);
type = xaccAccountGetType(acct);
if((type != STOCK) && (type != MUTUAL)) {
return 0;
}
splitamt = DxaccSplitGetShareAmount(s);
return value_match_predicate(splitamt, pd);

View File

@@ -67,6 +67,7 @@ typedef enum {
PD_CLEARED,
PD_BALANCE,
PD_GUID,
PD_KVP,
PD_MISC
} pd_type_t;
@@ -79,6 +80,7 @@ typedef enum {
PR_DATE,
PR_DESC,
PR_GUID,
PR_KVP,
PR_MEMO,
PR_MISC,
PR_NUM,
@@ -123,6 +125,20 @@ typedef enum {
BALANCE_UNBALANCED = 1 << 1
} balance_match_t;
typedef enum {
KVP_MATCH_LT=1,
KVP_MATCH_LTE,
KVP_MATCH_EQ,
KVP_MATCH_GTE,
KVP_MATCH_GT
} kvp_match_t;
typedef enum {
KVP_MATCH_SPLIT = 1 << 0,
KVP_MATCH_TRANS = 1 << 1,
KVP_MATCH_ACCOUNT = 1 << 2
} kvp_match_where_t;
/* query_run_t describes whether to require all splits or
* any to match for a transaction to be returned by
* xaccQueryGetTransactions */
@@ -199,6 +215,16 @@ typedef struct {
GUID guid;
} GUIDPredicateData;
typedef struct {
pd_type_t type;
pr_type_t term_type;
int sense;
kvp_match_t how;
kvp_match_where_t where;
GSList *path;
kvp_value *value;
} KVPPredicateData;
typedef struct {
pd_type_t type;
pr_type_t term_type;
@@ -217,6 +243,7 @@ typedef union {
ClearedPredicateData cleared;
BalancePredicateData balance;
GUIDPredicateData guid;
KVPPredicateData kvp;
MiscPredicateData misc;
} PredicateData;
@@ -307,6 +334,10 @@ void xaccQueryAddMemoMatch(Query * q, const char * matchstring,
void xaccQueryAddClearedMatch(Query * q, cleared_match_t how, QueryOp op);
void xaccQueryAddBalanceMatch(Query * q, balance_match_t how, QueryOp op);
void xaccQueryAddGUIDMatch(Query * q, const GUID *guid, QueryOp op);
/* given kvp value is on right side of comparison */
void xaccQueryAddKVPMatch(Query *q, GSList *path, const kvp_value *value,
kvp_match_t how, kvp_match_where_t where,
QueryOp op);
void xaccQueryAddMiscMatch(Query * q, Predicate p, int how, int data,
QueryOp op);

View File

@@ -23,6 +23,7 @@ TESTS = \
test-commodities \
test-freq-spec \
test-group-vs-book \
test-query \
test-resolve-file-path \
test-scm-query \
test-split-vs-account \
@@ -39,6 +40,7 @@ noinst_PROGRAMS = \
test-freq-spec \
test-group-vs-book \
test-load-engine \
test-query \
test-resolve-file-path \
test-scm-query \
test-split-vs-account \

View File

@@ -3,6 +3,7 @@
#include <guile/gh.h>
#include "GNCIdP.h"
#include "TransLog.h"
#include "gnc-book.h"
#include "gnc-engine.h"
#include "gnc-module.h"
@@ -145,6 +146,8 @@ main_helper (int argc, char **argv)
gnc_module_load("gnucash/engine", 0);
xaccLogDisable ();
for (i = 0; i < 10; i++)
run_test ();

View File

@@ -0,0 +1,202 @@
#include <glib.h>
#include <guile/gh.h>
#include "TransLog.h"
#include "Transaction.h"
#include "gnc-engine.h"
#include "gnc-module.h"
#include "gnc-session.h"
#include "test-engine-stuff.h"
#include "test-stuff.h"
typedef struct
{
kvp_match_where_t where;
GSList *path;
Query *q;
} KVPQueryData;
static void
add_kvp_value_query (const char *key, kvp_value *value, gpointer data)
{
KVPQueryData *kqd = data;
GSList *node;
kqd->path = g_slist_append (kqd->path, (gpointer) key);
if (kvp_value_get_type (value) == KVP_TYPE_FRAME)
kvp_frame_for_each_slot (kvp_value_get_frame (value),
add_kvp_value_query, data);
else
xaccQueryAddKVPMatch (kqd->q, kqd->path, value,
KVP_MATCH_EQ, kqd->where,
QUERY_AND);
node = g_slist_last (kqd->path);
kqd->path = g_slist_remove_link (kqd->path, node);
g_slist_free_1 (node);
}
static void
add_kvp_query (Query *q, kvp_frame *frame, kvp_match_where_t where)
{
KVPQueryData kqd;
kqd.where = where;
kqd.path = NULL;
kqd.q = q;
kvp_frame_for_each_slot (frame, add_kvp_value_query, &kqd);
}
static Query *
make_trans_query (Transaction *trans)
{
Account *a;
double d;
Query *q;
Split *s;
q = xaccMallocQuery ();
s = xaccTransGetSplit (trans, 0);
a = xaccSplitGetAccount (s);
xaccQueryAddSingleAccountMatch (q, xaccSplitGetAccount (s), QUERY_AND);
xaccQueryAddDescriptionMatch (q, xaccTransGetDescription (trans),
TRUE, FALSE, QUERY_AND);
xaccQueryAddNumberMatch (q, xaccTransGetNum (trans), TRUE, FALSE, QUERY_AND);
xaccQueryAddActionMatch (q, xaccSplitGetAction (s), TRUE, FALSE, QUERY_AND);
d = gnc_numeric_to_double (xaccSplitGetValue (s));
DxaccQueryAddAmountMatch (q, d, AMT_SGN_MATCH_EITHER,
AMT_MATCH_EXACTLY, QUERY_AND);
d = gnc_numeric_to_double (xaccSplitGetSharePrice (s));
DxaccQueryAddSharePriceMatch (q, d, AMT_MATCH_EXACTLY, QUERY_AND);
d = gnc_numeric_to_double (xaccSplitGetAmount (s));
DxaccQueryAddSharesMatch (q, d, AMT_MATCH_EXACTLY, QUERY_AND);
{
Timespec ts;
xaccTransGetDatePostedTS (trans, &ts);
xaccQueryAddDateMatchTS (q, TRUE, ts, TRUE, ts, QUERY_AND);
}
xaccQueryAddMemoMatch (q, xaccSplitGetMemo (s), TRUE, FALSE, QUERY_AND);
{
cleared_match_t how;
switch (xaccSplitGetReconcile (s))
{
case NREC:
how = CLEARED_NO;
break;
case CREC:
how = CLEARED_CLEARED;
break;
case YREC:
how = CLEARED_RECONCILED;
break;
case FREC:
how = CLEARED_FROZEN;
break;
case VREC:
how = CLEARED_VOIDED;
break;
default:
failure ("bad reconcile flag");
xaccFreeQuery (q);
return NULL;
}
xaccQueryAddClearedMatch (q, how, QUERY_AND);
}
add_kvp_query (q, xaccSplitGetSlots (s), KVP_MATCH_SPLIT);
add_kvp_query (q, xaccTransGetSlots (trans), KVP_MATCH_TRANS);
add_kvp_query (q, xaccAccountGetSlots (a), KVP_MATCH_ACCOUNT);
return q;
}
static gboolean
test_trans_query (Transaction *trans, gpointer data)
{
GNCSession *session = data;
AccountGroup *group;
GNCBook *book;
GList *list;
Query *q;
book = gnc_session_get_book (session);
group = gnc_book_get_group (book);
q = make_trans_query (trans);
xaccQuerySetGroup (q, group);
list = xaccQueryGetTransactions (q, QUERY_MATCH_ANY);
if (g_list_length (list) != 1)
{
failure ("number of matching transactions not 1");
return FALSE;
}
if (list->data != trans)
{
failure ("matching transaction is wrong");
return FALSE;
}
success ("found right transaction");
xaccFreeQuery (q);
return TRUE;
}
static void
run_test (void)
{
GNCSession *session;
AccountGroup *group;
GNCBook *book;
session = get_random_session ();
book = gnc_session_get_book (session);
group = gnc_book_get_group (book);
add_random_transactions_to_session (session, 20);
xaccGroupForEachTransaction (group, test_trans_query, session);
gnc_session_destroy (session);
}
static void
main_helper (int argc, char **argv)
{
gnc_module_load("gnucash/engine", 0);
xaccLogDisable ();
run_test ();
success("queries seem to work");
print_test_results();
exit(get_rv());
}
int
main (int argc, char **argv)
{
gh_enter (argc, argv, main_helper);
return 0;
}

View File

@@ -7,6 +7,7 @@
#include "test-engine-stuff.h"
#include "test-stuff.h"
#include "Query.h"
#include "TransLog.h"
static void
@@ -59,6 +60,9 @@ static void
main_helper (int argc, char **argv)
{
gnc_module_load("gnucash/engine", 0);
xaccLogDisable ();
run_tests ();
print_test_results ();

View File

@@ -5,6 +5,7 @@
#include "GNCIdP.h"
#include "Account.h"
#include "TransLog.h"
#include "gnc-engine.h"
#include "gnc-module.h"
#include "gnc-session.h"
@@ -106,6 +107,8 @@ main_helper (int argc, char **argv)
{
gnc_module_load("gnucash/engine", 0);
xaccLogDisable ();
run_test ();
success("split account crap seems to work");

View File

@@ -5,6 +5,7 @@
#include "GNCIdP.h"
#include "Account.h"
#include "TransLog.h"
#include "Transaction.h"
#include "gnc-engine.h"
#include "gnc-module.h"
@@ -124,6 +125,8 @@ main_helper (int argc, char **argv)
{
gnc_module_load("gnucash/engine", 0);
xaccLogDisable ();
run_test ();
success("transaction voiding seems OK");