[Gnucash-changes] Changes: Synchronize with QOF cvs tree.

Linas Vepstas linas at cvs.gnucash.org
Sun May 2 23:33:45 EDT 2004


Log Message:
-----------

Changes: 
Synchronize with QOF cvs tree.

gnc-engine-util.h:
-- add string utility to remove whitespace
-- add string utility that recognizess booleans 'TRUE', 'true' etc

guid.h:
kvp_frame.h:
-- doc clarification

qofclass.c:
qofclass.h:
-- fix core dump
-- add qof_class_is_registered() inquiry

qofgobj.c:
-- fix whitespace/bad indentation

qofquery.c:
-- fix query printing
-- fix buglet when mergeing two queries, and one is empty

qofquerycore.c:
-- some subroutine arguments should be const
-- add a handy-dandy qof_query_kvp_predicate_path() convenience routine

qofquerycore.h:
-- fix whitespace/bad indentation

qofsql.c:
qofsql.h:
-- add support for KVP in SQL queries
-- fix problems with GUID's in sql queries
-- separate query parse step from the query execute step

qofquery-serialize.c
qofquery-serialize.h
qofquery-deserial.c
qofquery-deserial.h:
-- Add new functions to turn a query into XML and back.
   (Oh, don't ask why, needed for a gnomevfs integration stunt)

Modified Files:
--------------
    gnucash/src/engine:
        gnc-engine-util.c
        gnc-engine-util.h
        guid.c
        guid.h
        kvp_frame.h
        qofclass.c
        qofclass.h
        qofgobj.c
        qofquery.c
        qofquery.h
        qofquerycore.c
        qofquerycore.h
        qofsession.c
        qofsql.c
        qofsql.h

Added Files:
-----------
    gnucash/src/engine:
        qofquery-deserial.c
        qofquery-deserial.h
        qofquery-serialize.c
        qofquery-serialize.h

Revision Data
-------------
Index: qofsql.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsql.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -Lsrc/engine/qofsql.h -Lsrc/engine/qofsql.h -u -r1.3 -r1.4
--- src/engine/qofsql.h
+++ src/engine/qofsql.h
@@ -34,6 +34,7 @@
 #include <glib.h>
 #include <qof/kvp_frame.h>
 #include <qof/qofbook.h>
+#include <qof/qofquery.h>
 
 typedef struct _QofSqlQuery QofSqlQuery;
 
@@ -49,6 +50,7 @@
 void qof_sql_query_set_book (QofSqlQuery *q, QofBook *book);
 
 /** Perform the query, return the results.
+ *  The book must be set in order to be able to perform a query.
  *
  *  The returned list is a list of ... See below ... 
  *  The returned list will have been sorted using the indicated sort 
@@ -69,6 +71,14 @@
  * together with parenthesis can be used to construct arbitrarily 
  * nested predicates.
  *
+ * If the param is a KVP frame, then we use a special markup to 
+ * indicate frame values.  The markup should look like 
+ * /some/kvp/path:value. Thus, for example,
+ *   SELECT * FROM SomeObj WHERE (param_a < '/some/kvp:10.0')
+ * will search for the object where param_a is a KVP frame, and this
+ * KVP frame contains a path '/some/kvp' and the value stored at that
+ * path is floating-point and that float value is less than 10.0.
+ *
  * The following are types of queries are NOT supported:
  *   SELECT a,b,c FROM ...
  * I am thinking of implementing the above as a list of KVP's
@@ -85,6 +95,23 @@
  */
 
 GList * qof_sql_query_run (QofSqlQuery *query, const char * str);
+
+/** Same as above, but just parse/pre-process the query; do
+ *  not actually run it over the dataset.  The QofBook does not
+ *  need to be set before calling this function.
+ */
+void qof_sql_query_parse (QofSqlQuery *query, const char * str);
+
+/** Return the QofQuery form of the previously parsed query. */
+QofQuery * qof_sql_query_get_query (QofSqlQuery *);
+
+/** Run the previously parsed query.  The QofBook must be set 
+ *  before this function can be called.  Note, teh QofBook can
+ *  be changed between each successive call to this routine.
+ *  This routine can be called after either qof_sql_query_parse()
+ *  or qof_sql_query_run() because both will set up the parse.
+ */
+GList * qof_sql_query_rerun (QofSqlQuery *query);
 
 /** 
  * Set the kvp frame to be used for formulating 'indirect' predicates.
Index: qofquery.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquery.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -Lsrc/engine/qofquery.h -Lsrc/engine/qofquery.h -u -r1.8 -r1.9
--- src/engine/qofquery.h
+++ src/engine/qofquery.h
@@ -188,8 +188,8 @@
  */
 QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op);
 
-/** Like qof_query_merge, but this will merge q2 into q1.  q2 remains
- * unchanged.
+/** Like qof_query_merge, but this will merge a copy of q2 into q1.  
+ *   q2 remains unchanged.
  */
 void qof_query_merge_in_place(QofQuery *q1, QofQuery *q2, QofQueryOp op);
 
Index: qofquery.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquery.c,v
retrieving revision 1.22
retrieving revision 1.23
diff -Lsrc/engine/qofquery.c -Lsrc/engine/qofquery.c -u -r1.22 -r1.23
--- src/engine/qofquery.c
+++ src/engine/qofquery.c
@@ -1,5 +1,5 @@
 /********************************************************************\
- * QofQuery.c -- API for finding Gnucash objects                    *
+ * qof_query.c -- Implement predicate API for searching for objects *
  * Copyright (C) 2002 Derek Atkins <warlord at MIT.EDU>                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -958,7 +958,7 @@
     retval->max_results = q->max_results;
     break;
 
-    /* this is demorgan expansion for a single AND expression. */
+    /* This is the DeMorgan expansion for a single AND expression. */
     /* !(abc) = !a + !b + !c */
   case 1:
     retval = qof_query_create();
@@ -975,7 +975,7 @@
       new_oterm = g_list_append(NULL, qt);
 
       /* g_list_append() can take forever, so let's do this for speed
-       * in "large" queries
+       * in "large" queries.
        */
       retval->terms = g_list_reverse(retval->terms);
       retval->terms = g_list_prepend(retval->terms, new_oterm);
@@ -983,7 +983,7 @@
     }
     break;
 
-    /* if there are multiple OR-terms, we just recurse by 
+    /* If there are multiple OR-terms, we just recurse by 
      * breaking it down to !(a + b + c) = 
      * !a * !(b + c) = !a * !b * !c.  */
   default:
@@ -1000,7 +1000,7 @@
     retval = qof_query_merge(iright, ileft, QOF_QUERY_AND);
     retval->books          = g_list_copy (q->books);
     retval->max_results    = q->max_results;
-    retval->search_for           = q->search_for;
+    retval->search_for     = q->search_for;
     retval->changed        = 1;
 
     qof_query_destroy(iright);
@@ -1037,6 +1037,20 @@
 
   search_for = (q1->search_for ? q1->search_for : q2->search_for);
 
+  /* Avoid merge surprises if op==QOF_QUERY_AND but q1 is empty.
+   * The goal of this tweak is to all the user to start with
+   * an empty q1 and then append to it recursively
+   * (and q1 (and q2 (and q3 (and q4 ....))))
+   * without bombing out because the append started with an 
+   * empty list.
+   * We do essentially the same check in qof_query_add_term()
+   * so that the first term added to an empty query doesn't screw up.
+   */
+  if ((QOF_QUERY_AND == op) && (0 == qof_query_has_terms (q1)))
+  {
+    op = QOF_QUERY_OR;
+  }
+
   switch(op) 
   {
   case QOF_QUERY_OR:
@@ -1214,6 +1228,12 @@
                         qof_book_get_guid(book), QOF_QUERY_AND);
 }
 
+GList * qof_query_get_books (QofQuery *q)
+{
+  if (!q) return NULL;
+  return q->books;
+}
+
 void qof_query_add_boolean_match (QofQuery *q, GSList *param_list, gboolean value,
                               QofQueryOp op)
 {
@@ -1374,6 +1394,7 @@
 }
 
 /***************************************************************************/
+/***************************************************************************/
 /* Query Print functions.  qof_query_print is public; everthing else supports
  * that.
  * Just call qof_query_print(QofQuery *q), and it will print out the query 
@@ -1465,7 +1486,7 @@
 
   searchFor = qof_query_get_search_for (query);
   gs = g_string_new ("Query Object Type: ");
-  g_string_append (gs, searchFor);
+  g_string_append (gs, (NULL == searchFor)? "(null)" : searchFor);
   output = g_list_append (output, gs);
 
   return output;
@@ -1510,7 +1531,7 @@
 static GList *
 qof_query_printSorts (QofQuerySort *s[], const gint numSorts, GList * output)
 {
-  GSList *gsl = NULL;
+  GSList *gsl, *n = NULL;
   gint curSort;
   GString *gs = g_string_new ("  Sort Parameters:\n");
 
@@ -1523,14 +1544,19 @@
     }
     increasing = qof_query_sort_get_increasing (s[curSort]);
 
-    for (gsl = qof_query_sort_get_param_path (s[curSort]); gsl; gsl = gsl->next)
+    gsl = qof_query_sort_get_param_path (s[curSort]); 
+    if (gsl) g_string_sprintfa (gs, "    Param: ");
+    for (n=gsl; n; n = n->next)
     {
-      GString *sortParm = g_string_new (gsl->data);
-      g_string_sprintfa (gs, "    Param: %s %s\n", sortParm->str,
-                         increasing ? "DESC" : "ASC");
-      g_string_free (sortParm, TRUE);
+      QofIdType param_name = n->data;
+      if (gsl != n)g_string_sprintfa (gs, "\n           ");
+      g_string_sprintfa (gs, "%s", param_name);
+    }
+    if (gsl) 
+    {
+      g_string_sprintfa (gs, " %s\n", increasing ? "DESC" : "ASC");
+      g_string_sprintfa (gs, "    Options: 0x%x\n", s[curSort]->options);
     }
-    /* TODO: finish this for loop */
   }
 
   output = g_list_append (output, gs);
@@ -1560,6 +1586,8 @@
     path = qof_query_term_get_param_path (qt);
     invert = qof_query_term_is_inverted (qt);
 
+    if (invert) output = g_list_append (output, 
+                                     g_string_new("    INVERT SENSE "));
     output = g_list_append (output, qof_query_printParamPath (path));
     output = g_list_append (output, qof_query_printPredData (pd));
     output = g_list_append (output, g_string_new("\n"));
@@ -1597,8 +1625,14 @@
 
   gs = g_string_new ("    Pred Data:\n      ");
   g_string_append (gs, (gchar *) pd->type_name);
-  g_string_sprintfa (gs, "\n      how: %s",
-                     qof_query_printStringForHow (pd->how));
+
+  /* Char Predicate and GUID predicate dosn't use the 'how' field. */
+  if (safe_strcmp (pd->type_name, QOF_TYPE_CHAR) &&
+      safe_strcmp (pd->type_name, QOF_TYPE_GUID))
+  {
+    g_string_sprintfa (gs, "\n      how: %s",
+                       qof_query_printStringForHow (pd->how));
+  }
 
   qof_query_printValueForParam (pd, gs);
 
@@ -1673,11 +1707,15 @@
   {
     GSList *node;
     query_kvp_t pdata = (query_kvp_t) pd;
+    g_string_sprintfa (gs, "\n      kvp path: ");
     for (node = pdata->path; node; node = node->next)
     {
-      g_string_sprintfa (gs, "\n      kvp path: %s", (gchar *) node->data);
-      return;
+      g_string_sprintfa (gs, "/%s", (gchar *) node->data);
     }
+    g_string_sprintfa (gs, "\n");
+    g_string_sprintfa (gs, "      kvp value: %s\n", 
+                         kvp_value_to_string (pdata->value));
+    return;
   }
   if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
   {
@@ -1694,7 +1732,7 @@
   if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
   {
     query_double_t pdata = (query_double_t) pd;
-    g_string_sprintfa (gs, " double: %20.16g", pdata->val);
+    g_string_sprintfa (gs, " double: %.18g", pdata->val);
     return;
   }
   if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
@@ -1816,8 +1854,4 @@
   return "UNKNOWN MATCH TYPE";
 }                               /* qof_query_printGuidMatch */
 
-GList * qof_query_get_books (QofQuery *q)
-{
-  if (!q) return NULL;
-  return q->books;
-}
+/* ======================== END OF FILE =================== */
Index: qofquerycore.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquerycore.h,v
retrieving revision 1.12
retrieving revision 1.13
diff -Lsrc/engine/qofquerycore.h -Lsrc/engine/qofquerycore.h -u -r1.12 -r1.13
--- src/engine/qofquerycore.h
+++ src/engine/qofquerycore.h
@@ -1,5 +1,5 @@
 /********************************************************************\
- * qofquerycore.h -- API for providing core Query data types           *
+ * qofquerycore.h -- API for providing core Query data types        *
  * Copyright (C) 2002 Derek Atkins <warlord at MIT.EDU>                *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -114,28 +114,40 @@
 
 
 /** Core Data Type Predicates */
-QofQueryPredData *qof_query_string_predicate (QofQueryCompare how, char *str,
-					 QofStringMatch options,
-					 gboolean is_regex);
+QofQueryPredData *qof_query_string_predicate (QofQueryCompare how, 
+                                              const char *str,
+                                              QofStringMatch options,
+                                              gboolean is_regex);
+
 QofQueryPredData *qof_query_date_predicate (QofQueryCompare how,
-				       QofDateMatch options, Timespec date);
+                                            QofDateMatch options, 
+                                            Timespec date);
+
 QofQueryPredData *qof_query_numeric_predicate (QofQueryCompare how,
-					  QofNumericMatch options,
-					  gnc_numeric value);
+                                               QofNumericMatch options,
+                                               gnc_numeric value);
+
 QofQueryPredData *qof_query_guid_predicate (QofGuidMatch options, GList *guids);
 QofQueryPredData *qof_query_int32_predicate (QofQueryCompare how, gint32 val);
 QofQueryPredData *qof_query_int64_predicate (QofQueryCompare how, gint64 val);
 QofQueryPredData *qof_query_double_predicate (QofQueryCompare how, double val);
 QofQueryPredData *qof_query_boolean_predicate (QofQueryCompare how, gboolean val);
 QofQueryPredData *qof_query_char_predicate (QofCharMatch options,
-				       const char *chars);
+                                            const char *chars);
 
 /** The qof_query_kvp_predicate() matches the object that has
  *  the value 'value' located at the path 'path'.  In a certain
  *  sense, the 'path' is handled as if it were a paramter.
  */
 QofQueryPredData *qof_query_kvp_predicate (QofQueryCompare how,
-				      GSList *path, const KvpValue *value);
+                                           GSList *path, 
+                                           const KvpValue *value);
+
+/** Same predicate as above, except that 'path' is assumed to be 
+ * a string containing slash-separated pathname. */
+QofQueryPredData *qof_query_kvp_predicate_path (QofQueryCompare how,
+                                                const char *path, 
+                                                const KvpValue *value);
 
 /** Copy a predicate. */
 QofQueryPredData *qof_query_core_predicate_copy (QofQueryPredData *pdata);
Index: qofquerycore.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofquerycore.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -Lsrc/engine/qofquerycore.c -Lsrc/engine/qofquerycore.c -u -r1.13 -r1.14
--- src/engine/qofquerycore.c
+++ src/engine/qofquerycore.c
@@ -219,7 +219,7 @@
 
 QofQueryPredData *
 qof_query_string_predicate (QofQueryCompare how,
-                            char *str, QofStringMatch options,
+                            const char *str, QofStringMatch options,
                             gboolean is_regex)
 {
   query_string_t pdata;
@@ -656,16 +656,19 @@
 }
 
 QofQueryPredData *
-qof_query_guid_predicate (QofGuidMatch options, GList *guids)
+qof_query_guid_predicate (QofGuidMatch options, GList *guid_list)
 {
   query_guid_t pdata;
   GList *node;
 
+  if (NULL == guid_list) return NULL;
+
   pdata = g_new0 (query_guid_def, 1);
   pdata->pd.how = QOF_COMPARE_EQUAL;
   pdata->pd.type_name = query_guid_type;
   pdata->options = options;
-  pdata->guids = g_list_copy (guids);
+
+  pdata->guids = g_list_copy (guid_list);
   for (node = pdata->guids; node; node = node->next) 
   {
     GUID *guid = guid_malloc ();
@@ -1226,6 +1229,33 @@
     node->data = g_strdup (node->data);
 
   return ((QofQueryPredData*)pdata);
+}
+
+QofQueryPredData *
+qof_query_kvp_predicate_path (QofQueryCompare how,
+                              const char *path, const KvpValue *value)
+{
+  QofQueryPredData *pd;
+  GSList *spath = NULL;
+  char *str, *p;
+
+  if (!path) return NULL;
+
+  str = g_strdup (path);
+  p = str;
+  if (0 == *p) return NULL;
+  if ('/' == *p) p++;
+
+  while (p)
+  {
+    spath = g_slist_append (spath, p);
+    p = strchr (p, '/');
+    if (p) { *p = 0; p++; }
+  }
+
+  pd = qof_query_kvp_predicate (how, spath, value);
+  g_free (str);
+  return pd;
 }
 
 /* initialization ================================================== */
Index: qofsql.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsql.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lsrc/engine/qofsql.c -Lsrc/engine/qofsql.c -u -r1.4 -r1.5
--- src/engine/qofsql.c
+++ src/engine/qofsql.c
@@ -27,6 +27,8 @@
 
 */
 
+#include <stdlib.h>   /* for working atoll */
+
 #include <glib.h>
 #include <libsql/sql_parser.h>
 #include <qof/kvp_frame.h>
@@ -36,7 +38,9 @@
 #include <qof/guid.h>
 #include <qof/qofbook.h>
 #include <qof/qofquery.h>
+#include <qof/qofquerycore.h>
 #include <qof/qofsql.h>
+#include <qof/gnc-engine-util.h>
 
 static short module = MOD_QUERY;
 
@@ -56,7 +60,7 @@
 QofSqlQuery *
 qof_sql_query_new(void)
 {
-	QofSqlQuery * sqn = (QofSqlQuery *) g_new (QofSqlQuery, 1);
+	QofSqlQuery * sqn = (QofSqlQuery *) g_new0 (QofSqlQuery, 1);
 	
 	sqn->qof_query = NULL;
 	sqn->parse_result = NULL;
@@ -80,6 +84,15 @@
 
 /* ========================================================== */
 
+QofQuery * 
+qof_sql_query_get_query (QofSqlQuery *q)
+{
+	if (!q) return NULL;
+	return q->qof_query;
+}
+
+/* ========================================================== */
+
 void 
 qof_sql_query_set_book (QofSqlQuery *q, QofBook *book)
 {
@@ -96,41 +109,6 @@
 	q->kvp_join = kvp;
 }
 
-/* =================================================================== */
-/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)  
- * Else return pointer to first non-whitespace character. */
-
-static const char *
-whitespace_filter (const char * val)
-{
-	size_t len;
-	if (!val) return NULL;
-
-	len = strspn (val, "\a\b\t\n\v\f\r ");
-	if (0 == val[len]) return NULL;
-	return val+len;
-}
-
-/* =================================================================== */
-/* Return integer 1 if the string starts with 't' or 'T" or contians the 
- * word 'true' or 'TRUE'; if string is a number, return that number. */
-
-static int
-util_bool_to_int (const char * val)
-{
-	const char * p = whitespace_filter (val);
-	if (!p) return 0;
-	if ('t' == p[0]) return 1;
-	if ('T' == p[0]) return 1;
-	if ('y' == p[0]) return 1;
-	if ('Y' == p[0]) return 1;
-	if (strstr (p, "true")) return 1;
-	if (strstr (p, "TRUE")) return 1;
-	if (strstr (p, "yes")) return 1;
-	if (strstr (p, "YES")) return 1;
-	return atoi (val);
-}
-
 /* ========================================================== */
 
 static inline void
@@ -218,7 +196,7 @@
 		return NULL;
 	}
 	qvalue_name = dequote_string (qvalue_name);
-	qvalue_name = whitespace_filter (qvalue_name);
+	qvalue_name = (char *) qof_util_whitespace_filter (qvalue_name);
 
 	/* Look to see if its the special KVP value holder.
 	 * If it is, look up the value. */
@@ -305,9 +283,20 @@
 		PWARN ("Need to specify an object class to query");
 		return NULL;
 	}
-			
+
+	if (FALSE == qof_class_is_registered (table_name)) 
+	{
+		PWARN ("The query object \'%s\' is not known", table_name);
+		return NULL;
+	}
+
 	QofType param_type = qof_class_get_parameter_type (table_name, param_name);
-	if (!param_type) return NULL;  /* Can't happen */
+	if (!param_type) 
+	{
+		PWARN ("The parameter \'%s\' on object \'%s\' is not known", 
+		       param_name, table_name);
+		return NULL;
+	}
 
 	if (!strcmp (param_type, QOF_TYPE_STRING))
 	{
@@ -319,7 +308,9 @@
 	}
 	else if (!strcmp (param_type, QOF_TYPE_CHAR))
 	{
-		pred_data = qof_query_char_predicate (qop, qvalue_name);
+		QofCharMatch cm = QOF_CHAR_MATCH_ANY;
+		if (QOF_COMPARE_NEQ == qop) cm = QOF_CHAR_MATCH_NONE;
+		pred_data = qof_query_char_predicate (cm, qvalue_name);
 	}
 	else if (!strcmp (param_type, QOF_TYPE_INT32))
 	{
@@ -338,7 +329,7 @@
 	}
 	else if (!strcmp (param_type, QOF_TYPE_BOOLEAN))
 	{
-		gboolean ival = util_bool_to_int (qvalue_name);
+		gboolean ival = qof_util_bool_to_int (qvalue_name);
 		pred_data = qof_query_boolean_predicate (qop, ival);
 	}
 	else if (!strcmp (param_type, QOF_TYPE_DATE))
@@ -375,29 +366,89 @@
 	}
 	else if (!strcmp (param_type, QOF_TYPE_GUID))
 	{
-		GUID *guid = guid_malloc();
-		gboolean rc = string_to_guid (qvalue_name, guid);
+		GUID guid;
+		gboolean rc = string_to_guid (qvalue_name, &guid);
 		if (0 == rc)
 		{
 			PWARN ("unable to parse guid: %s", qvalue_name);
 			return NULL;
 		}
 
-		// XXX match any means eqal, what about not equal ?? 
 		// XXX less, than greater than don't make sense,
 		// should check for those bad conditions
-		GList *guid_list = g_list_append (NULL, guid);
-		pred_data = qof_query_guid_predicate (QOF_GUID_MATCH_ANY, guid_list);
-		// XXX FIXME the above is a memory leak! we leak both guid and glist.
+
+		QofGuidMatch gm = QOF_GUID_MATCH_ANY;
+		if (QOF_COMPARE_NEQ == qop) gm = QOF_GUID_MATCH_NONE;
+		GList *guid_list = g_list_append (NULL, &guid);
+		pred_data = qof_query_guid_predicate (gm, guid_list);
+
+		g_list_free (guid_list);
 	}
-#if 0
 	else if (!strcmp (param_type, QOF_TYPE_KVP))
 	{
-xxxxxhd
-		xxxx gboolean ival = 
-		pred_data = qof_query_kvp_predicate (qop, ival);
+		/* We are expecting an encoded value that looks like
+		 * /some/path/string:value
+		 */
+		char *sep = strchr (qvalue_name, ':');
+		if (!sep) return NULL;
+		*sep = 0;
+		char * path = qvalue_name;
+		char * str = sep +1;
+		char * p;
+		/* If str has only digits, we know its a plain number.
+		 * If its numbers and a decimal point, assume a float
+		 * If its numbers and a slash, assume numeric
+		 * If its 32 bytes of hex, assume GUID
+		 * If it looks like an iso date ... 
+		 * else assume its a string.
+		 */
+		KvpValue *kval = NULL;
+		int len = strlen (str);
+		if ((32 == len) && (32 == strspn (str, "0123456789abcdef")))
+		{
+			GUID guid;
+			string_to_guid (str, &guid);
+			kval = kvp_value_new_guid (&guid);
+		}
+		else
+		if (len == strspn (str, "0123456789"))
+		{
+			kval = kvp_value_new_gint64 (atoll(str));
+		}
+		else
+		if ((p=strchr (str, '.')) && 
+		    ((len-1) == (strspn (str, "0123456789") + 
+		                 strspn (p+1, "0123456789"))))
+		{
+			kval = kvp_value_new_double (atof(str));
+		}
+
+		else
+		if ((p=strchr (str, '/')) && 
+		    ((len-1) == (strspn (str, "0123456789") + 
+		                 strspn (p+1, "0123456789"))))
+		{
+			gnc_numeric num;
+			string_to_gnc_numeric (str, &num);
+			kval = kvp_value_new_gnc_numeric (num);
+		}
+		else
+		if ((p=strchr (str, '-')) && 
+		    (p=strchr (p+1, '-')) && 
+		    (p=strchr (p+1, ' ')) && 
+		    (p=strchr (p+1, ':')) && 
+		    (p=strchr (p+1, ':')))
+		{
+			kval = kvp_value_new_timespec (gnc_iso8601_to_timespec_gmt(str));
+		}
+
+		/* The default handler is a string */
+		if (NULL == kval)
+		{
+			kval = kvp_value_new_string (str);
+		}
+		pred_data = qof_query_kvp_predicate_path (qop, path, kval);
 	}
-#endif
 	else
 	{
 		PWARN ("The predicate type \"%s\" is unsupported for now", param_type);
@@ -505,25 +556,33 @@
 
 /* ========================================================== */
 
-GList * 
-qof_sql_query_run (QofSqlQuery *query, const char *str)
+void 
+qof_sql_query_parse (QofSqlQuery *query, const char *str)
 {
-	GList *node;
+	if (!query) return;
 
-	if (!query) return NULL;
+	/* Delete old query, if any */
+   /* XXX FIXME we should also delete the parse_result as well */
+	if (query->qof_query)
+	{
+		qof_query_destroy (query->qof_query);
+		query->qof_query = NULL;
+	}
+
+	/* Parse the SQL string */
 	query->parse_result = sql_parse (str);
 
 	if (!query->parse_result) 
 	{
 		PWARN ("parse error"); 
-		return NULL;
+		return;
 	}
 
 	if (SQL_select != query->parse_result->type)
 	{
 		PWARN("currently, only SELECT statements are supported, "
 		                     "got type=%d", query->parse_result);
-		return NULL;
+		return;
 	}
 
 	/* If the user wrote "SELECT * FROM tablename WHERE ..."
@@ -543,7 +602,7 @@
 	{
 		/* Walk over the where terms, turn them into QOF predicates */
 		query->qof_query = handle_where (query, swear);
-		if (NULL == query->qof_query) return NULL;
+		if (NULL == query->qof_query) return;
 	}
 	else
 	{
@@ -559,6 +618,37 @@
 	 * XXX all this needs fixing.
 	 */
 	qof_query_search_for (query->qof_query, query->single_global_tablename);
+}
+
+/* ========================================================== */
+
+GList * 
+qof_sql_query_run (QofSqlQuery *query, const char *str)
+{
+	GList *node;
+
+	if (!query) return NULL;
+
+	qof_sql_query_parse (query, str);
+	if (NULL == query->qof_query) return NULL;
+
+	qof_query_set_book (query->qof_query, query->book);
+
+	// qof_query_print (query->qof_query);
+	GList *results = qof_query_run (query->qof_query);
+
+	return results;
+}
+
+GList * 
+qof_sql_query_rerun (QofSqlQuery *query)
+{
+	GList *node;
+
+	if (!query) return NULL;
+
+	if (NULL == query->qof_query) return NULL;
+
 	qof_query_set_book (query->qof_query, query->book);
 
 	// qof_query_print (query->qof_query);
Index: guid.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/guid.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -Lsrc/engine/guid.c -Lsrc/engine/guid.c -u -r1.32 -r1.33
--- src/engine/guid.c
+++ src/engine/guid.c
@@ -89,6 +89,7 @@
 GUID *
 guid_malloc (void)
 {
+  if (!guid_memchunk) guid_memchunk_init();
   return g_chunk_new (GUID, guid_memchunk);
 }
 
--- /dev/null
+++ src/engine/qofquery-deserial.c
@@ -0,0 +1,719 @@
+/********************************************************************\
+ * qofquery-deserial.c -- Convert Qof-Query XML to QofQuery         *
+ * Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>     *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+// #include "config.h"
+
+#include <stdlib.h>
+#include <glib.h>
+#include <libxml/parser.h>
+
+#include "qofquery-deserial.h"
+#include "qofquery-serialize.h"
+#include "qofquery-p.h"
+#include "qofquerycore-p.h"
+#include "gnc-engine-util.h"
+
+#define CACHE_INSERT(str)  \
+    g_cache_insert(gnc_engine_get_string_cache(), (gpointer)(str))
+#define CACHE_REMOVE(str)  \
+    g_cache_remove(gnc_engine_get_string_cache(), (gpointer)(str))
+
+/* =========================================================== */
+
+#define GET_TEXT(node)  ({                                   \
+   char * sstr = NULL;                                       \
+   xmlNodePtr text;                                          \
+   text = node->xmlChildrenNode;                             \
+   if (text && 0 == strcmp ("text", text->name)) {           \
+      sstr = text->content;                                  \
+   }                                                         \
+   sstr;                                                     \
+})
+
+#define GET_STR(SELF,FN,TOK)                                 \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      FN (SELF, str);                                        \
+   }                                                         \
+   else
+
+#define GET_DBL(SELF,FN,TOK)                                 \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      double rate = atof (str);                              \
+      FN (SELF, rate);                                       \
+   }                                                         \
+   else
+
+#define GET_INT32(SELF,FN,TOK)                               \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gint32 ival = atoi (str);                              \
+      FN (SELF, ival);                                       \
+   }                                                         \
+   else
+
+#define GET_INT64(SELF,FN,TOK)                               \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gint64 ival = atoll (str);                             \
+      FN (SELF, ival);                                       \
+   }                                                         \
+   else
+
+#define GET_DATE(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      Timespec tval = gnc_iso8601_to_timespec_gmt (str);     \
+      FN (SELF, tval);                                       \
+   }                                                         \
+   else
+
+#define GET_BOOL(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gboolean bval = qof_util_bool_to_int (str);            \
+      FN (SELF, bval);                                       \
+   }                                                         \
+   else
+
+#define GET_NUMERIC(SELF,FN,TOK)                             \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      gnc_numeric nval;                                      \
+      string_to_gnc_numeric (str, &nval);                    \
+      FN (SELF, nval);                                       \
+   }                                                         \
+   else
+
+#define GET_GUID(SELF,FN,TOK)                                \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      GUID guid;                                             \
+      string_to_guid (str, &guid);                           \
+      FN (SELF, &guid);                                      \
+   }                                                         \
+   else
+
+#define GET_HOW(VAL,TOK,A,B,C,D,E,F)                         \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_COMPARE_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_COMPARE_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_COMPARE_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_COMPARE_##C;    \
+      else if (!strcmp (#D, str)) ival = QOF_COMPARE_##D;    \
+      else if (!strcmp (#E, str)) ival = QOF_COMPARE_##E;    \
+      else if (!strcmp (#F, str)) ival = QOF_COMPARE_##F;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH2(VAL,TOK,PFX,A,B)                          \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH3(VAL,TOK,PFX,A,B,C)                        \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+#define GET_MATCH5(VAL,TOK,PFX,A,B,C,D,E)                    \
+   if (0 == strcmp (TOK, node->name))                        \
+   {                                                         \
+      const char *str = GET_TEXT (node);                     \
+      int ival = QOF_##PFX##_##A;                            \
+      if (!strcmp (#A, str)) ival = QOF_##PFX##_##A;         \
+      else if (!strcmp (#B, str)) ival = QOF_##PFX##_##B;    \
+      else if (!strcmp (#C, str)) ival = QOF_##PFX##_##C;    \
+      else if (!strcmp (#D, str)) ival = QOF_##PFX##_##D;    \
+      else if (!strcmp (#E, str)) ival = QOF_##PFX##_##E;    \
+      VAL = ival;                                            \
+   }                                                         \
+   else
+
+/* =============================================================== */
+/* Autogen the code for the simple, repetitive predicates */
+
+#define SIMPLE_PRED_HANDLER(SUBRNAME,CTYPE,GETTER,XMLTYPE,PRED) \
+static QofQueryPredData *                                       \
+SUBRNAME (xmlNodePtr root)                                      \
+{                                                               \
+	xmlNodePtr xp = root->xmlChildrenNode;                       \
+	xmlNodePtr node;                                             \
+                                                                \
+	QofQueryCompare how = QOF_COMPARE_EQUAL;                     \
+	CTYPE val = 0;                                               \
+                                                                \
+	for (node=xp; node; node = node->next)                       \
+	{                                                            \
+		if (node->type != XML_ELEMENT_NODE) continue;             \
+                                                                \
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ); \
+		GETTER (0, val=, XMLTYPE);                                \
+		{}                                                        \
+	}                                                            \
+                                                                \
+	QofQueryPredData *pred;                                      \
+	pred = PRED (how, val);                                      \
+	return pred;                                                 \
+}
+
+SIMPLE_PRED_HANDLER (qof_query_pred_double_from_xml,
+                     double,
+                     GET_DBL,
+                     "qofquery:double",
+	                  qof_query_double_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_int64_from_xml,
+                     gint64,
+                     GET_INT64,
+                     "qofquery:int64",
+	                  qof_query_int64_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_int32_from_xml,
+                     gint32,
+                     GET_INT32,
+                     "qofquery:int32",
+	                  qof_query_int32_predicate);
+
+SIMPLE_PRED_HANDLER (qof_query_pred_boolean_from_xml,
+                     gboolean,
+                     GET_BOOL,
+                     "qofquery:boolean",
+	                  qof_query_boolean_predicate);
+
+/* =============================================================== */
+
+static void wrap_new_gint64(KvpValue **v, gint64 value) {
+	*v = kvp_value_new_gint64 (value); }
+static void wrap_new_double(KvpValue **v, double value) {
+	*v = kvp_value_new_double (value); }
+static void wrap_new_numeric(KvpValue **v, gnc_numeric value) {
+	*v = kvp_value_new_gnc_numeric (value); }
+static void wrap_new_string(KvpValue **v, const char * value) {
+	*v = kvp_value_new_string (value); }
+static void wrap_new_guid(KvpValue **v, const GUID * value) {
+	*v = kvp_value_new_guid (value); }
+static void wrap_new_timespec(KvpValue **v, Timespec value) {
+	*v = kvp_value_new_timespec (value); }
+
+
+static QofQueryPredData *
+qof_query_pred_kvp_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	GSList *path = NULL;
+	KvpValue *value = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		if (0 == strcmp ("qofquery:kvp-path", node->name))
+		{
+			const char *str = GET_TEXT (node);
+			path = g_slist_append (path, (gpointer) str);
+		}
+		else
+		GET_INT64(&value,   wrap_new_gint64,   "qofquery:int64");
+		GET_DBL(&value,     wrap_new_double,   "qofquery:double");
+		GET_NUMERIC(&value, wrap_new_numeric,  "qofquery:numeric");
+		GET_STR(&value,     wrap_new_string,   "qofquery:string");
+		GET_GUID(&value,    wrap_new_guid,     "qofquery:guid");
+		GET_DATE(&value,    wrap_new_timespec, "qofquery:date");
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_kvp_predicate (how, path, value);
+	g_slist_free (path);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_guid_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+   GList *guid_list = NULL;
+
+	QofGuidMatch sm = QOF_GUID_MATCH_ANY;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		/* char pred doesn't have GET_HOW */
+		GET_MATCH5 (sm, "qofquery:guid-match", 
+		            GUID_MATCH, ANY, NONE, NULL, ALL, LIST_ANY);
+
+		if (0 == strcmp ("qofquery:guid", node->name))
+		{
+			const char *str = GET_TEXT (node);
+			GUID *guid = guid_malloc ();
+			gboolean decode = string_to_guid (str, guid);
+			if (decode)
+			{
+				guid_list = g_list_append (guid_list, guid);
+			}
+			else
+			{
+				guid_free (guid);
+				// XXX error!  let someone know!
+			}
+		}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_guid_predicate (sm, guid_list);
+
+	/* The predicate made a copy of everything, so free our stuff */
+   GList *n;
+	for (n=guid_list; n; n=n->next)
+	{
+		guid_free (n->data);
+	}
+   g_list_free (guid_list);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_char_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofCharMatch sm = QOF_CHAR_MATCH_ANY;
+   const char * char_list = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		/* char pred doesn't have GET_HOW */
+		GET_MATCH2 (sm, "qofquery:char-match", 
+		            CHAR_MATCH, ANY, NONE);
+		GET_STR (0, char_list=, "qofquery:char-list");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_char_predicate (sm, char_list);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_numeric_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofNumericMatch sm = QOF_NUMERIC_MATCH_ANY;
+   gnc_numeric num;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_MATCH3 (sm, "qofquery:numeric-match", 
+		            NUMERIC_MATCH, DEBIT, CREDIT, ANY);
+		GET_NUMERIC (0, num=, "qofquery:numeric");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_numeric_predicate (how, sm, num);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_date_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofDateMatch sm = QOF_DATE_MATCH_ROUNDED;
+	Timespec date = {0,0};
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_MATCH2 (sm, "qofquery:date-match", 
+		            DATE_MATCH, NORMAL, ROUNDED);
+		GET_DATE (0, date=, "qofquery:date");
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_date_predicate (how, sm, date);
+	return pred;
+}
+
+/* =============================================================== */
+
+static QofQueryPredData *
+qof_query_pred_string_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr xp = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	QofQueryCompare how = QOF_COMPARE_EQUAL;
+	QofStringMatch sm = QOF_STRING_MATCH_CASEINSENSITIVE;
+	gboolean is_regex = FALSE;
+	const char *pstr = NULL;
+
+	for (node=xp; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_HOW (how, "qofquery:compare", LT, LTE, EQUAL, GT, GTE, NEQ);
+		GET_BOOL (0, is_regex=, "qofquery:is-regex");
+		GET_STR (0, pstr=, "qofquery:string");
+		GET_MATCH2 (sm, "qofquery:string-match", 
+		            STRING_MATCH, NORMAL, CASEINSENSITIVE);
+		{}
+	}
+
+	QofQueryPredData *pred;
+	pred = qof_query_string_predicate (how, pstr, sm , is_regex);
+	return pred;
+}
+
+/* =============================================================== */
+
+static GSList * 
+qof_query_param_path_from_xml (xmlNodePtr root)
+{
+	xmlNodePtr pterms = root->xmlChildrenNode;
+	GSList *plist = NULL;
+	xmlNodePtr node;
+	for (node=pterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:param"))
+		{
+			const char *str = GET_TEXT (node);
+			plist = g_slist_append (plist, CACHE_INSERT(str));
+		}
+	}
+	return plist;
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_term_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr node;
+	xmlNodePtr term = root->xmlChildrenNode;
+	QofQueryPredData *pred = NULL;
+	GSList *path = NULL;
+
+	for (node=term; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+		if (0 == strcmp (node->name, "qofquery:invert"))
+		{
+			QofQuery *qt = qof_query_create();
+			qof_query_term_from_xml (qt, node);
+			QofQuery *qinv = qof_query_invert (qt);
+			qof_query_merge_in_place (q, qinv, QOF_QUERY_AND);
+			qof_query_destroy (qinv);
+			qof_query_destroy (qt);
+			return;
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:param-path"))
+		{
+			path = qof_query_param_path_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-string"))
+		{
+			pred = qof_query_pred_string_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-date"))
+		{
+			pred = qof_query_pred_date_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-numeric"))
+		{
+			pred = qof_query_pred_numeric_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-int32"))
+		{
+			pred = qof_query_pred_int32_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-int64"))
+		{
+			pred = qof_query_pred_int64_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-double"))
+		{
+			pred = qof_query_pred_double_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-boolean"))
+		{
+			pred = qof_query_pred_boolean_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-char"))
+		{
+			pred = qof_query_pred_char_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-guid"))
+		{
+			pred = qof_query_pred_guid_from_xml (node);
+		}
+		else
+		if (0 == strcmp (node->name, "qofquery:pred-kvp"))
+		{
+			pred = qof_query_pred_kvp_from_xml (node);
+		}
+		else
+		{
+			// warning unhandled predicate type
+		}
+	}
+
+	/* At this level, the terms should always be anded */
+	qof_query_add_term (q, path, pred, QOF_QUERY_AND);
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_and_terms_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr andterms = root->xmlChildrenNode;
+	xmlNodePtr node;
+	for (node=andterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:term"))
+		{
+			qof_query_term_from_xml (q, node);
+		}
+	}
+}
+
+/* =============================================================== */
+
+static void 
+qof_query_or_terms_from_xml (QofQuery *q, xmlNodePtr root)
+{
+	xmlNodePtr andterms = root->xmlChildrenNode;
+	xmlNodePtr node;
+
+	for (node=andterms; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		if (0 == strcmp (node->name, "qofquery:and-terms"))
+		{
+			QofQuery *qand = qof_query_create ();
+			qof_query_and_terms_from_xml (qand, node);
+			qof_query_merge_in_place (q, qand, QOF_QUERY_OR);
+			qof_query_destroy (qand);
+		}
+	}
+}
+
+/* =============================================================== */
+
+QofQuery *
+qof_query_from_xml (xmlNodePtr root)
+{
+	QofQuery *q;
+
+	if (!root) return NULL;
+
+	xmlChar * version = xmlGetProp(root, "version");
+   if (!root->name || strcmp ("qof:qofquery", root->name))
+   {
+		// XXX something is wrong. warn ... 
+      return NULL;
+   }
+
+	q = qof_query_create ();
+
+	xmlNodePtr qpart = root->xmlChildrenNode;
+	xmlNodePtr node;
+	for (node=qpart; node; node = node->next)
+	{
+		if (node->type != XML_ELEMENT_NODE) continue;
+
+		GET_STR   (q, qof_query_search_for,      "qofquery:search-for");
+		GET_INT32 (q, qof_query_set_max_results, "qofquery:max-results");
+		if (0 == strcmp (node->name, "qofquery:or-terms"))
+		{
+			qof_query_or_terms_from_xml (q, node);
+		}
+		else 
+		if (0 == strcmp (node->name, "qofquery:sort-list"))
+		{
+// XXX unfinished  I'm bored
+		}
+		else 
+		{
+			// XXX unknown node type tell someone about it
+		}
+	}
+
+	return q;
+}
+
+/* =============================================================== */
+
+#ifdef UNIT_TEST
+
+#include <stdio.h>
+#include <qof/qofsql.h>
+
+int main (int argc, char * argv[])
+{
+	QofQuery *q, *qnew;
+	QofSqlQuery *sq;
+
+	guid_init();
+	qof_query_init();
+	qof_object_initialize ();
+
+	static QofParam params[] = {
+		{ "adate", QOF_TYPE_DATE, NULL, NULL},
+		{ "aint", QOF_TYPE_INT32, NULL, NULL},
+		{ "aint64", QOF_TYPE_INT64, NULL, NULL},
+		{ "aflt", QOF_TYPE_DOUBLE, NULL, NULL},
+		{ "abool", QOF_TYPE_BOOLEAN, NULL, NULL},
+		{ "astr", QOF_TYPE_STRING, NULL, NULL},
+		{ "adate", QOF_TYPE_DATE, NULL, NULL},
+		{ "anum", QOF_TYPE_NUMERIC, NULL, NULL},
+		{ "achar", QOF_TYPE_CHAR, NULL, NULL},
+		{ "aguid", QOF_TYPE_GUID, NULL, NULL},
+		{ "akvp", QOF_TYPE_KVP, NULL, NULL},
+		{ NULL },
+   };
+
+	qof_class_register ("GncABC", NULL, params);
+	sq = qof_sql_query_new();
+
+	qof_sql_query_parse (sq, 
+	    "SELECT * from GncABC WHERE aint = 123 " 
+	    "and not aint64 = 6123123456789 "
+	    "or abool = TRUE "
+	    "and not aflt >= \'3.14159265358979\' "
+	    "and not astr=\'asdf\' "
+	    "and adate<\'01-01-01\' "
+	    "or anum<\'12301/100\' "
+	    "or achar != asdf "
+	    "and aguid != abcdef01234567890fedcba987654321 "
+	    "and akvp != \'/some/path:abcdef01234567890fedcba987654321\' "
+	    "and not akvp != \'/some/path/glop:1234\' "
+	    "and akvp = \'/arf/arf/arf:10.234\' "
+	    "and akvp != \'/some/other/path:qwerty1234uiop\' "
+	    "and not akvp = \'/some/final/path:123401/100\' "
+	    );
+	// qof_sql_query_parse (sq, "SELECT * from GncABC;");
+	q = qof_sql_query_get_query (sq);
+
+	qof_query_print (q);
+
+   xmlNodePtr topnode = qof_query_to_xml (q);
+
+	qnew = qof_query_from_xml (topnode);
+	printf ("  ------------------------------------------------------- \n");
+	qof_query_print (qnew);
+
+   /* If the before and after trees are the same, the test pases. */
+	gboolean eq = qof_query_equal (q, qnew);
+	printf ("Are the two equal? answer=%d\n", eq);
+
+#define DOPRINT 1
+#ifdef DOPRINT
+   xmlDocPtr doc = doc = xmlNewDoc("1.0");
+	xmlDocSetRootElement(doc,topnode);
+
+	xmlChar *xbuf;
+	int bufsz;
+	xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
+
+	printf ("%s\n", xbuf);
+	xmlFree (xbuf);
+	xmlFreeDoc(doc);
+#endif
+
+	return 0;
+}
+
+#endif /* UNIT_TEST */
+
+/* ======================== END OF FILE =================== */
Index: gnc-engine-util.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-engine-util.h,v
retrieving revision 1.41
retrieving revision 1.42
diff -Lsrc/engine/gnc-engine-util.h -Lsrc/engine/gnc-engine-util.h -u -r1.41 -r1.42
--- src/engine/gnc-engine-util.h
+++ src/engine/gnc-engine-util.h
@@ -25,7 +25,7 @@
     @brief GnuCash engine utility functions 
     @author Copyright (C) 1997 Robin D. Clark <rclark at cs.hmc.edu>
     @author Copyright (C) 2000 Bill Gribble <grib at billgribble.com>
-    @author Copyright (C) 1997-2002 Linas Vepstas <linas at linas.org>
+    @author Copyright (C) 1997-2002,2004 Linas Vepstas <linas at linas.org>
 */
 
 #ifndef QOF_UTIL_H
@@ -67,42 +67,50 @@
 
 /** Prototypes *************************************************/
 
-/* The safe_strcmp compares strings a and b the same way that strcmp()
+/** The safe_strcmp compares strings a and b the same way that strcmp()
  * does, except that either may be null.  This routine assumes that
  * a non-null string is always greater than a null string.
  */
 int safe_strcmp (const char * da, const char * db);
 int safe_strcasecmp (const char * da, const char * db);
 
-/* The null_strcmp compares strings a and b the same way that strcmp()
+/** The null_strcmp compares strings a and b the same way that strcmp()
  * does, except that either may be null.  This routine assumes that
  * a null string is equal to the empty string.
  */
 int null_strcmp (const char * da, const char * db);
 
-/* Search for str2 in first nchar chars of str1, ignore case. Return
+/** Search for str2 in first nchar chars of str1, ignore case. Return
  * pointer to first match, or null. These are just like that strnstr
  * and the strstr functions, except that they ignore the case. */
 extern char *strncasestr(const char *str1, const char *str2, size_t len);
 extern char *strcasestr(const char *str1, const char *str2);
 
-/* The ultostr() subroutine is the inverse of strtoul(). It accepts a
+/** The ultostr() subroutine is the inverse of strtoul(). It accepts a
  * number and prints it in the indicated base.  The returned string
  * should be g_freed when done.  */
 char * ultostr (unsigned long val, int base);
 
-/* Returns true if string s is a number, possibly surrounded by
+/** Returns true if string s is a number, possibly surrounded by
  * whitespace. */
 gboolean gnc_strisnum(const char *s);
 
-/* Define a gnucash stpcpy */
+/** Local copy of stpcpy, used wtih libc's that don't have one. */
 char * gnc_stpcpy (char *dest, const char *src);
 
 #ifndef HAVE_STPCPY
 #define stpcpy gnc_stpcpy
 #endif
 
+/** Return NULL if the field is whitespace (blank, tab, formfeed etc.)  
+ *  Else return pointer to first non-whitespace character. 
+ */
+const char * qof_util_whitespace_filter (const char * val);
 
+/** Return integer 1 if the string starts with 't' or 'T' or 
+ *  contains the word 'true' or 'TRUE'; if string is a number, 
+ *  return that number. (Leading whitespace is ignored). */
+int qof_util_bool_to_int (const char * val);
 
 /** Many strings used throughout the engine are likely to be duplicated.
  * So we provide a reference counted cache system for the strings, which
Index: gnc-engine-util.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-engine-util.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -Lsrc/engine/gnc-engine-util.c -Lsrc/engine/gnc-engine-util.c -u -r1.33 -r1.34
--- src/engine/gnc-engine-util.c
+++ src/engine/gnc-engine-util.c
@@ -1,7 +1,7 @@
 /********************************************************************\
  * gnc-engine-util.c -- GnuCash engine utility functions            *
  * Copyright (C) 1997 Robin D. Clark                                *
- * Copyright (C) 1997-2001 Linas Vepstas <linas at linas.org>          *
+ * Copyright (C) 1997-2001,2004 Linas Vepstas <linas at linas.org>     *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
  * modify it under the terms of the GNU General Public License as   *
@@ -177,6 +177,41 @@
 {
   strcpy (dest, src);
   return (dest + strlen (src));
+}
+
+/* =================================================================== */
+/* Return NULL if the field is whitespace (blank, tab, formfeed etc.)  
+ * Else return pointer to first non-whitespace character. */
+
+const char *
+qof_util_whitespace_filter (const char * val)
+{
+	size_t len;
+	if (!val) return NULL;
+
+	len = strspn (val, "\a\b\t\n\v\f\r ");
+	if (0 == val[len]) return NULL;
+	return val+len;
+}
+
+/* =================================================================== */
+/* Return integer 1 if the string starts with 't' or 'T' or contains the 
+ * word 'true' or 'TRUE'; if string is a number, return that number. */
+
+int
+qof_util_bool_to_int (const char * val)
+{
+	const char * p = qof_util_whitespace_filter (val);
+	if (!p) return 0;
+	if ('t' == p[0]) return 1;
+	if ('T' == p[0]) return 1;
+	if ('y' == p[0]) return 1;
+	if ('Y' == p[0]) return 1;
+	if (strstr (p, "true")) return 1;
+	if (strstr (p, "TRUE")) return 1;
+	if (strstr (p, "yes")) return 1;
+	if (strstr (p, "YES")) return 1;
+	return atoi (val);
 }
 
 /********************************************************************\
Index: qofgobj.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofgobj.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -Lsrc/engine/qofgobj.c -Lsrc/engine/qofgobj.c -u -r1.1 -r1.2
--- src/engine/qofgobj.c
+++ src/engine/qofgobj.c
@@ -49,9 +49,9 @@
                                                                                 
   // gobjectClassTable = g_hash_table_new (g_str_hash, g_str_equal);
 
-	/* Init the other subsystems that we need */
-	qof_object_initialize();
-	qof_query_init ();
+  /* Init the other subsystems that we need */
+  qof_object_initialize();
+  qof_query_init ();
 }
 
 void 
@@ -60,12 +60,12 @@
   if (!initialized) return;
   initialized = FALSE;
                                                                                 
-	GSList *n;
-	for (n=paramList; n; n=n->next) g_free(n->data);
-	g_slist_free (paramList);
+  GSList *n;
+  for (n=paramList; n; n=n->next) g_free(n->data);
+  g_slist_free (paramList);
 
-	for (n=classList; n; n=n->next) g_free(n->data);
-	g_slist_free (classList);
+  for (n=classList; n; n=n->next) g_free(n->data);
+  g_slist_free (classList);
 
 #if 0
   // XXX also need to walk over books, and collection and delete
@@ -83,13 +83,13 @@
 void 
 qof_gobject_register_instance (QofBook *book, QofType type, GObject *gob)
 {
-	if (!book || !type) return;
+  if (!book || !type) return;
 
-	QofCollection *coll = qof_book_get_collection (book, type);
+  QofCollection *coll = qof_book_get_collection (book, type);
 
-	GSList * instance_list = qof_collection_get_data (coll);
-	instance_list = g_slist_prepend (instance_list, gob);
-   qof_collection_set_data (coll, instance_list);
+  GSList * instance_list = qof_collection_get_data (coll);
+  instance_list = g_slist_prepend (instance_list, gob);
+  qof_collection_set_data (coll, instance_list);
 }
 
 /* =================================================================== */
@@ -97,91 +97,91 @@
 static gpointer
 qof_gobject_getter (gpointer data, QofParam *getter)
 {
-	GObject *gob = data;
+  GObject *gob = data;
 
-	GParamSpec *gps = getter->param_userdata;
+  GParamSpec *gps = getter->param_userdata;
 
-	/* Note that the return type must actually be of type
-	 * getter->param_type but we just follow the hard-coded 
-	 * mapping below ... */
-	if (G_IS_PARAM_SPEC_STRING(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_STRING);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      const char * str = g_value_get_string (&gval);
-      return (gpointer) str;
-   }
-	else
-	if (G_IS_PARAM_SPEC_INT(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_INT);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      int ival = g_value_get_int (&gval);
-      return (gpointer) ival;
-   }
-	else
-	if (G_IS_PARAM_SPEC_UINT(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_UINT);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      int ival = g_value_get_uint (&gval);
-      return (gpointer) ival;
-   }
-	else
-	if (G_IS_PARAM_SPEC_BOOLEAN(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_BOOLEAN);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      int ival = g_value_get_boolean (&gval);
-      return (gpointer) ival;
-   }
-
-	PWARN ("unhandled parameter type %s for paramter %s", 
-	        G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
-	return NULL;
+  /* Note that the return type must actually be of type
+   * getter->param_type but we just follow the hard-coded 
+   * mapping below ... */
+  if (G_IS_PARAM_SPEC_STRING(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_STRING);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    const char * str = g_value_get_string (&gval);
+    return (gpointer) str;
+  }
+  else
+  if (G_IS_PARAM_SPEC_INT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_INT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_int (&gval);
+    return (gpointer) ival;
+  }
+  else
+  if (G_IS_PARAM_SPEC_UINT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_UINT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_uint (&gval);
+    return (gpointer) ival;
+  }
+  else
+  if (G_IS_PARAM_SPEC_BOOLEAN(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_BOOLEAN);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    int ival = g_value_get_boolean (&gval);
+    return (gpointer) ival;
+  }
+
+  PWARN ("unhandled parameter type %s for paramter %s", 
+          G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
+  return NULL;
 }
 
 static double
 qof_gobject_double_getter (gpointer data, QofParam *getter)
 {
-	GObject *gob = data;
+  GObject *gob = data;
 
-	GParamSpec *gps = getter->param_userdata;
+  GParamSpec *gps = getter->param_userdata;
 
-	/* Note that the return type must actually be of type
-	 * getter->param_type but we just follow the hard-coded 
-	 * mapping below ... */
-	if (G_IS_PARAM_SPEC_FLOAT(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_FLOAT);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      double fval = g_value_get_float (&gval);
-      return fval;
-   }
-	else
-	if (G_IS_PARAM_SPEC_DOUBLE(gps))
-	{
-		GValue gval = {G_TYPE_INVALID};
-		g_value_init (&gval, G_TYPE_DOUBLE);
-		g_object_get_property (gob, getter->param_name, &gval);
-
-      double fval = g_value_get_double (&gval);
-      return fval;
-   }
-
-	PWARN ("unhandled parameter type %s for paramter %s", 
-	        G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
-	return 0.0;
+  /* Note that the return type must actually be of type
+   * getter->param_type but we just follow the hard-coded 
+   * mapping below ... */
+  if (G_IS_PARAM_SPEC_FLOAT(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_FLOAT);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    double fval = g_value_get_float (&gval);
+    return fval;
+  }
+  else
+  if (G_IS_PARAM_SPEC_DOUBLE(gps))
+  {
+    GValue gval = {G_TYPE_INVALID};
+    g_value_init (&gval, G_TYPE_DOUBLE);
+    g_object_get_property (gob, getter->param_name, &gval);
+
+    double fval = g_value_get_double (&gval);
+    return fval;
+  } 
+
+  PWARN ("unhandled parameter type %s for paramter %s", 
+          G_PARAM_SPEC_TYPE_NAME(gps), getter->param_name);
+  return 0.0;
 }
 
 /* =================================================================== */
@@ -191,12 +191,12 @@
 static void
 qof_gobject_foreach (QofCollection *coll, QofEntityForeachCB cb, gpointer ud)
 {
-   GSList *n;
-	n = qof_collection_get_data (coll);
-   for (; n; n=n->next)
-   {
-      cb (n->data, ud);
-   }
+  GSList *n;
+  n = qof_collection_get_data (coll);
+  for (; n; n=n->next)
+  {
+    cb (n->data, ud);
+  }
 }
                                                                                 
 /* =================================================================== */
@@ -205,105 +205,105 @@
 qof_gobject_register (QofType e_type, GObjectClass *obclass)
 {
 
-	/* Get the GObject properties, convert to QOF properties */
-	GParamSpec **prop_list;
-	int n_props;
-	prop_list = g_object_class_list_properties (obclass, &n_props);
-
-	QofParam * qof_param_list = g_new0 (QofParam, n_props);
-	paramList = g_slist_prepend (paramList, qof_param_list);
-
-	PINFO ("object %s has %d props", e_type, n_props);
-	int i, j=0;
-	for (i=0; i<n_props; i++)
-	{
-		GParamSpec *gparam = prop_list[i];
-		QofParam *qpar = &qof_param_list[j];
-
-		PINFO ("param %d %s is type %s", 
-		      i, gparam->name, G_PARAM_SPEC_TYPE_NAME(gparam));
-
-		qpar->param_name = g_param_spec_get_name (gparam);
-		qpar->param_getfcn = qof_gobject_getter;
-		qpar->param_setfcn = NULL;
-		qpar->param_userdata = gparam;
-		if ((G_IS_PARAM_SPEC_INT(gparam))  ||
-		    (G_IS_PARAM_SPEC_UINT(gparam)) ||
-		    (G_IS_PARAM_SPEC_ENUM(gparam)) ||
-		    (G_IS_PARAM_SPEC_FLAGS(gparam)))
-		{
-			qpar->param_type = QOF_TYPE_INT32;
-			j++;
-		} 
-		else
-		if ((G_IS_PARAM_SPEC_INT64(gparam)) ||
-		    (G_IS_PARAM_SPEC_UINT64(gparam)))
-		{
-			qpar->param_type = QOF_TYPE_INT64;
-			j++;
-		} 
-		else
-		if (G_IS_PARAM_SPEC_BOOLEAN(gparam))
-		{
-			qpar->param_type = QOF_TYPE_BOOLEAN;
-			j++;
-		} 
-		else
-		if (G_IS_PARAM_SPEC_STRING(gparam))
-		{
-			qpar->param_type = QOF_TYPE_STRING;
-			j++;
-		} 
-		else
-		if ((G_IS_PARAM_SPEC_POINTER(gparam)) ||
-		    (G_IS_PARAM_SPEC_OBJECT(gparam)))
-		{
-			/* No-op, silently ignore.  Someday we should handle this ...  */
-		} 
-		else
-		if ((G_IS_PARAM_SPEC_FLOAT(gparam)) ||
-		    (G_IS_PARAM_SPEC_DOUBLE(gparam)))
-		{
-			qpar->param_getfcn = (QofAccessFunc) qof_gobject_double_getter;
-			qpar->param_type = QOF_TYPE_DOUBLE;
-			j++;
-		} 
-		else
-		if (G_IS_PARAM_SPEC_CHAR(gparam))
-		{
-			qpar->param_type = QOF_TYPE_CHAR;
-			j++;
-		} 
-		else
-		{
-			PWARN ("Unknown/unhandled parameter type %s on %s:%s\n", 
-			    G_PARAM_SPEC_TYPE_NAME(gparam), e_type, qpar->param_name);
-		}
-	}
+  /* Get the GObject properties, convert to QOF properties */
+  GParamSpec **prop_list;
+  int n_props;
+  prop_list = g_object_class_list_properties (obclass, &n_props);
+
+  QofParam * qof_param_list = g_new0 (QofParam, n_props);
+  paramList = g_slist_prepend (paramList, qof_param_list);
+
+  PINFO ("object %s has %d props", e_type, n_props);
+  int i, j=0;
+  for (i=0; i<n_props; i++)
+  {
+    GParamSpec *gparam = prop_list[i];
+    QofParam *qpar = &qof_param_list[j];
+
+    PINFO ("param %d %s is type %s", 
+          i, gparam->name, G_PARAM_SPEC_TYPE_NAME(gparam));
+
+    qpar->param_name = g_param_spec_get_name (gparam);
+    qpar->param_getfcn = qof_gobject_getter;
+    qpar->param_setfcn = NULL;
+    qpar->param_userdata = gparam;
+    if ((G_IS_PARAM_SPEC_INT(gparam))  ||
+        (G_IS_PARAM_SPEC_UINT(gparam)) ||
+        (G_IS_PARAM_SPEC_ENUM(gparam)) ||
+        (G_IS_PARAM_SPEC_FLAGS(gparam)))
+    {
+      qpar->param_type = QOF_TYPE_INT32;
+      j++;
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_INT64(gparam)) ||
+        (G_IS_PARAM_SPEC_UINT64(gparam)))
+    {
+      qpar->param_type = QOF_TYPE_INT64;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_BOOLEAN(gparam))
+    {
+      qpar->param_type = QOF_TYPE_BOOLEAN;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_STRING(gparam))
+    {
+      qpar->param_type = QOF_TYPE_STRING;
+      j++;
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_POINTER(gparam)) ||
+        (G_IS_PARAM_SPEC_OBJECT(gparam)))
+    {
+      /* No-op, silently ignore.  Someday we should handle this ...  */
+    } 
+    else
+    if ((G_IS_PARAM_SPEC_FLOAT(gparam)) ||
+        (G_IS_PARAM_SPEC_DOUBLE(gparam)))
+    {
+      qpar->param_getfcn = (QofAccessFunc) qof_gobject_double_getter;
+      qpar->param_type = QOF_TYPE_DOUBLE;
+      j++;
+    } 
+    else
+    if (G_IS_PARAM_SPEC_CHAR(gparam))
+    {
+      qpar->param_type = QOF_TYPE_CHAR;
+      j++;
+    } 
+    else
+    {
+      PWARN ("Unknown/unhandled parameter type %s on %s:%s\n", 
+      G_PARAM_SPEC_TYPE_NAME(gparam), e_type, qpar->param_name);
+    }
+  }
 
-	/* NULL-terminated list! */
-	qof_param_list[j].param_type = NULL;
+  /* NULL-terminated list! */
+  qof_param_list[j].param_type = NULL;
 
-   qof_class_register (e_type, NULL, qof_param_list);
+  qof_class_register (e_type, NULL, qof_param_list);
 
-	/* ------------------------------------------------------ */
+  /* ------------------------------------------------------ */
    /* Now do the class itself */
-	QofObject *class_def = g_new0 (QofObject, 1);
-	classList = g_slist_prepend (classList, class_def);
+  QofObject *class_def = g_new0 (QofObject, 1);
+  classList = g_slist_prepend (classList, class_def);
 
-	class_def->interface_version = QOF_OBJECT_VERSION;
-	class_def->e_type = e_type;
-	/* We could let the user specify a "nick" here, but
-	 * the actual class name seems reasonable, e.g. for debugging. */
-	class_def->type_label = G_OBJECT_CLASS_NAME (obclass);
-	class_def->book_begin = NULL;
-	class_def->book_end = NULL;
-	class_def->is_dirty = NULL;
-	class_def->mark_clean = NULL;
-	class_def->foreach = qof_gobject_foreach;
-	class_def->printable = NULL;
+  class_def->interface_version = QOF_OBJECT_VERSION;
+  class_def->e_type = e_type;
+  /* We could let the user specify a "nick" here, but
+   * the actual class name seems reasonable, e.g. for debugging. */
+  class_def->type_label = G_OBJECT_CLASS_NAME (obclass);
+  class_def->book_begin = NULL;
+  class_def->book_end = NULL;
+  class_def->is_dirty = NULL;
+  class_def->mark_clean = NULL;
+  class_def->foreach = qof_gobject_foreach;
+  class_def->printable = NULL;
  
-   qof_object_register (class_def);
+  qof_object_register (class_def);
 }
 
 /* ======================= END OF FILE ================================ */
Index: qofclass.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofclass.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -Lsrc/engine/qofclass.c -Lsrc/engine/qofclass.c -u -r1.2 -r1.3
--- src/engine/qofclass.c
+++ src/engine/qofclass.c
@@ -51,23 +51,31 @@
                     QofSortFunc default_sort_function,
                     const QofParam *params)
 {
+  GHashTable *ht;
   int i;
 
   if (!obj_name) return;
 
   if (default_sort_function)
+  {
     g_hash_table_insert (sortTable, (char *)obj_name, default_sort_function);
+  }
 
-  if (params) {
-    GHashTable *ht = g_hash_table_lookup (paramTable, obj_name);
+  ht = g_hash_table_lookup (paramTable, obj_name);
 
-    /* If it doesn't already exist, create a new table for this object */
-    if (!ht) {
-      ht = g_hash_table_new (g_str_hash, g_str_equal);
-      g_hash_table_insert (paramTable, (char *)obj_name, ht);
-    }
+  /* If it doesn't already exist, create a new table for this object */
+  if (!ht) 
+  {
+    ht = g_hash_table_new (g_str_hash, g_str_equal);
+    g_hash_table_insert (paramTable, (char *)obj_name, ht);
+  }
 
-    /* Now insert all the parameters */
+  /* At least right now, we allow dummy, paramterless objects, 
+   * for testing purposes.  Although I suppose that should be 
+   * an error..  */
+  /* Now insert all the parameters */
+  if (params) 
+  {
     for (i = 0; params[i].param_name; i++)
       g_hash_table_insert (ht,
                (char *)params[i].param_name,
@@ -96,6 +104,15 @@
   g_hash_table_destroy (sortTable);
 }
 
+gboolean
+qof_class_is_registered (QofIdTypeConst obj_name)
+{
+  if (!obj_name) return FALSE;
+
+  if (g_hash_table_lookup (paramTable, obj_name)) return TRUE;
+
+  return FALSE;
+}
 
 const QofParam * 
 qof_class_get_parameter (QofIdTypeConst obj_name,
Index: kvp_frame.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/kvp_frame.h,v
retrieving revision 1.33
retrieving revision 1.34
diff -Lsrc/engine/kvp_frame.h -Lsrc/engine/kvp_frame.h -u -r1.33 -r1.34
--- src/engine/kvp_frame.h
+++ src/engine/kvp_frame.h
@@ -74,9 +74,16 @@
 /** Enum to enumerate possible types in the union KvpValue 
  *  XXX FIXME TODO: People have asked for boolean values, 
  *  e.g. in xaccAccountSetAutoInterestXfer
+ *
+ * XXX In the long run, this should be synchronized with the 
+ * core QOF types, which in turn should be synced to the g_types
+ * in GLib.  Unfortuantely, this requies writing a pile of code
+ * to handle all of the different cases.
+ * An alternative might be to make kvp values inherit from the 
+ * core g_types (i.e. add new core g_types) ??
  */
 typedef enum {
-  KVP_TYPE_GINT64,
+  KVP_TYPE_GINT64=1,
   KVP_TYPE_DOUBLE,
   KVP_TYPE_NUMERIC,
   KVP_TYPE_STRING,
--- /dev/null
+++ src/engine/qofquery-deserial.h
@@ -0,0 +1,42 @@
+/********************************************************************\
+ * qofquery-deserial.h -- Convert Qof-Query XML to QofQuery         *
+ * Copyright (C) 2004 Linas Vepstas <linas at linas.org>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+/** @file qofquery-deserial.h
+    @breif Convert Qof-Query XML to QofQuery 
+
+    Qof Queries can be convrted to and from XML so that they
+    can be sent from here to there. This file implements the
+    routine needed to convert the XML back into a C struct.
+
+    @author Copyright (C) 2004 Linas Vepstas <linas at linas.org>
+*/
+
+#ifndef QOF_QUERY_DESERIAL_H
+#define QOF_QUERY_DESERIAL_H
+
+#include <qof/qofquery.h>
+#include <libxml/tree.h>
+
+/** Given an XML tree, reconstruct and return the equivalent query. */
+QofQuery *qof_query_from_xml (xmlNodePtr);
+
+#endif /* QOF_QUERY_DESERIAL_H */
--- /dev/null
+++ src/engine/qofquery-serialize.h
@@ -0,0 +1,41 @@
+/********************************************************************\
+ * qofquery-serialize.h -- Convert QofQuery to XML                  *
+ * Copyright (C) 2004 Linas Vepstas <linas at linas.org>               *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+/** @file qofquery-serialize.h
+    @breif Convert QofQuery to XML
+    @author Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>
+ */
+
+#ifndef QOF_QUERY_SERIALIZE_H
+#define QOF_QUERY_SERIALIZE_H
+
+#include <qof/qofquery.h>
+#include <libxml/tree.h>
+
+/** Take the query passed as input, and serialize it into XML.
+ *  The DTD used will be a very qofquery specific DTD
+ *  This is NOT the XQuery XML.
+ */
+xmlNodePtr qof_query_to_xml (QofQuery *q);
+
+#endif /* QOF_QUERY_SERIALIZE_H */
Index: qofsession.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofsession.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -Lsrc/engine/qofsession.c -Lsrc/engine/qofsession.c -u -r1.8 -r1.9
--- src/engine/qofsession.c
+++ src/engine/qofsession.c
@@ -1100,6 +1100,7 @@
 void
 gnc_run_rpc_server (void)
 {
+#ifdef GNUCASH
   const char * dll_err;
   void * dll_handle;
   int (*rpc_run)(short);
@@ -1132,6 +1133,7 @@
   ret = (*rpc_run)(0);
 
   /* XXX How do we force an exit? */
+#endif /* GNUCASH */
 }
 
 /* =================== END OF FILE ====================================== */
--- /dev/null
+++ src/engine/qofquery-serialize.c
@@ -0,0 +1,555 @@
+/********************************************************************\
+ * qofquery-serialize.c -- Convert QofQuery to XML                  *
+ * Copyright (C) 2001,2002,2004 Linas Vepstas <linas at linas.org>     *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
+ * Boston, MA  02111-1307,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#include "config.h"
+
+#include "qofquery-serialize.h"
+#include "qofquery-p.h"
+#include "qofquerycore-p.h"
+#include "kvp_frame.h"
+
+/* ======================================================= */
+
+#define PUT_STR(TOK,VAL) {                           \
+   xmlNodePtr node;                                  \
+   const char * str = (VAL);                         \
+   if (str && 0 != str[0])                           \
+   {                                                 \
+      node = xmlNewNode (NULL, TOK);                 \
+      xmlNodeAddContent(node, str);                  \
+      xmlAddChild (topnode, node);                   \
+   }                                                 \
+}
+
+#define PUT_INT32(TOK,VAL) {                         \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%d", (VAL));     \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_INT64(TOK,VAL) {                         \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%lld", (VAL));   \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_DBL(TOK,VAL) {                           \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   g_snprintf (buff, sizeof(buff), "%.18g", (VAL));  \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_GUID(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   guid_to_string_buff ((VAL), buff);                \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_DATE(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   char buff[80];                                    \
+   gnc_timespec_to_iso8601_buff ((VAL), buff);       \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, buff);                    \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_NUMERIC(TOK,VAL) {                       \
+   xmlNodePtr node;                                  \
+   char *str;                                        \
+   str = gnc_numeric_to_string (VAL);                \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   g_free (str);                                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_BOOL(TOK,VAL) {                          \
+   xmlNodePtr node;                                  \
+   gboolean boll = (VAL);                            \
+   node = xmlNewNode (NULL, TOK);                    \
+   if (boll) {                                       \
+      xmlNodeAddContent(node, "T");                  \
+   } else {                                          \
+      xmlNodeAddContent(node, "F");                  \
+   }                                                 \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_HOW(TOK,VAL,A,B,C,D,E,F) {               \
+   xmlNodePtr node;                                  \
+   const char * str = "EQUAL";                       \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_COMPARE_##A: str = #A; break;         \
+      case QOF_COMPARE_##B: str = #B; break;         \
+      case QOF_COMPARE_##C: str = #C; break;         \
+      case QOF_COMPARE_##D: str = #D; break;         \
+      case QOF_COMPARE_##E: str = #E; break;         \
+      case QOF_COMPARE_##F: str = #F; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH2(TOK,VAL,PFX,A,B) {                \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH3(TOK,VAL,PFX,A,B,C) {              \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+      case QOF_##PFX##_##C: str = #C; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+#define PUT_MATCH5(TOK,VAL,PFX,A,B,C,D,E) {          \
+   xmlNodePtr node;                                  \
+   const char * str = #A;                            \
+   switch (VAL)                                      \
+   {                                                 \
+      case QOF_##PFX##_##A: str = #A; break;         \
+      case QOF_##PFX##_##B: str = #B; break;         \
+      case QOF_##PFX##_##C: str = #C; break;         \
+      case QOF_##PFX##_##D: str = #D; break;         \
+      case QOF_##PFX##_##E: str = #E; break;         \
+   }                                                 \
+   node = xmlNewNode (NULL, TOK);                    \
+   xmlNodeAddContent(node, str);                     \
+   xmlAddChild (topnode, node);                      \
+}
+
+/* ======================================================= */
+
+static void
+qof_kvp_value_to_xml (KvpValue *kval, xmlNodePtr topnode)
+{
+	KvpValueType kvt = kvp_value_get_type (kval);
+
+	switch (kvt)
+	{
+		case KVP_TYPE_GINT64:
+			PUT_INT64 ("qofquery:int64", kvp_value_get_gint64(kval));
+			break;
+		case KVP_TYPE_DOUBLE:
+			PUT_DBL ("qofquery:double", kvp_value_get_double(kval));
+			break;
+		case KVP_TYPE_NUMERIC:
+			PUT_NUMERIC ("qofquery:numeric", kvp_value_get_numeric(kval));
+			break;
+		case KVP_TYPE_GUID:
+			PUT_GUID ("qofquery:guid", kvp_value_get_guid(kval));
+			break;
+		case KVP_TYPE_STRING:
+			PUT_STR ("qofquery:string", kvp_value_get_string(kval));
+			break;
+		case KVP_TYPE_TIMESPEC:
+			PUT_DATE ("qofquery:date", kvp_value_get_timespec(kval));
+			break;
+		case KVP_TYPE_BINARY:
+		case KVP_TYPE_GLIST:
+		case KVP_TYPE_FRAME:
+			// XXX don't know how to support these.
+			break;
+	}
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_pred_data_to_xml (QofQueryPredData *pd)
+{
+
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_GUID))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-guid");
+		/* GUID Predicate doesn't do a PUT_HOW */
+
+		GList *n;
+		query_guid_t pdata = (query_guid_t) pd;
+		PUT_MATCH5("qofquery:guid-match", pdata->options, 
+		                GUID_MATCH, ANY, ALL, NONE, NULL, LIST_ANY);
+
+		for (n = pdata->guids; n; n = n->next)
+		{
+			PUT_GUID ("qofquery:guid", n->data);
+		}
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_STRING))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-string");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_string_t pdata = (query_string_t) pd;
+		PUT_MATCH2("qofquery:string-match", pdata->options,
+                       STRING_MATCH, NORMAL, CASEINSENSITIVE);
+		PUT_BOOL ("qofquery:is-regex", pdata->is_regex);
+		PUT_STR ("qofquery:string", pdata->matchstring);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_NUMERIC))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-numeric");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_numeric_t pdata = (query_numeric_t) pd;
+		PUT_MATCH3("qofquery:numeric-match", pdata->options,
+		                 NUMERIC_MATCH, DEBIT, CREDIT, ANY);
+		
+		PUT_NUMERIC ("qofquery:numeric", pdata->amount);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_KVP))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-kvp");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_kvp_t pdata = (query_kvp_t) pd;
+		
+		GSList *n;
+		for (n=pdata->path; n; n=n->next)
+		{
+			PUT_STR ("qofquery:kvp-path", n->data);
+		}
+		qof_kvp_value_to_xml (pdata->value, topnode);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_DATE))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-date");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_date_t pdata = (query_date_t) pd;
+		
+		PUT_MATCH2("qofquery:date-match", pdata->options,
+		                 DATE_MATCH, NORMAL, ROUNDED);
+
+		PUT_DATE ("qofquery:date", pdata->date);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_INT64))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-int64");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_int64_t pdata = (query_int64_t) pd;
+		PUT_INT64 ("qofquery:int64", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_INT32))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-int32");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_int32_t pdata = (query_int32_t) pd;
+		
+		PUT_INT32 ("qofquery:int32", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_DOUBLE))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-double");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_double_t pdata = (query_double_t) pd;
+		
+		PUT_DBL ("qofquery:double", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_BOOLEAN))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-boolean");
+		PUT_HOW ("qofquery:compare", pd->how, LT, LTE, EQUAL, GT, GTE, NEQ);
+
+		query_boolean_t pdata = (query_boolean_t) pd;
+		
+		PUT_BOOL ("qofquery:boolean", pdata->val);
+		return topnode;
+	}
+	if (!safe_strcmp (pd->type_name, QOF_TYPE_CHAR))
+	{
+		xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:pred-char");
+		/* There is no PUT_HOW for char-match */
+		query_char_t pdata = (query_char_t) pd;
+		
+		PUT_MATCH2("qofquery:char-match", pdata->options,
+		                 CHAR_MATCH, ANY, NONE);
+		
+		PUT_STR ("qofquery:char-list", pdata->char_list);
+		return topnode;
+	}
+	return NULL;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_param_path_to_xml (GSList *param_path)
+{
+	xmlNodePtr topnode = xmlNewNode (NULL, "qofquery:param-path");
+	GSList *n = param_path;
+	for ( ; n; n=n->next)
+	{
+		QofIdTypeConst path = n->data;
+		if (!path) continue;
+		PUT_STR ("qofquery:param", path);
+	}
+	return topnode;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_one_term_to_xml (QofQueryTerm *qt)
+{
+	xmlNodePtr node;
+	xmlNodePtr term = xmlNewNode (NULL, "qofquery:term");
+
+	gboolean invert = qof_query_term_is_inverted (qt);
+	GSList *path = qof_query_term_get_param_path (qt);
+	QofQueryPredData *pd = qof_query_term_get_pred_data (qt);
+
+	xmlNodePtr topnode = term;
+	if (invert)
+	{
+		/* inverter becomes new top mode */
+		topnode = xmlNewNode (NULL, "qofquery:invert");
+		xmlAddChild (term, topnode);
+	}
+
+	node = qof_query_param_path_to_xml (path);
+	if (node) xmlAddChild (topnode, node);
+
+	node = qof_query_pred_data_to_xml (pd);
+	if (node) xmlAddChild (topnode, node);
+
+	return term;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_and_terms_to_xml (GList *and_terms)
+{
+	xmlNodePtr terms = xmlNewNode (NULL, "qofquery:and-terms");
+	GList *n = and_terms;
+	for ( ; n; n=n->next)
+	{
+		QofQueryTerm *qt = n->data;
+		if (!qt) continue;
+
+		xmlNodePtr t = qof_query_one_term_to_xml (n->data);
+		if (t) xmlAddChild (terms, t);
+	}
+	return terms;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_terms_to_xml (QofQuery *q)
+{
+	xmlNodePtr terms = NULL;
+	GList *n = qof_query_get_terms (q);
+
+	if (!n) return NULL;
+	terms = xmlNewNode (NULL, "qofquery:or-terms");
+
+	for ( ; n; n=n->next)
+	{
+		xmlNodePtr andt = qof_query_and_terms_to_xml (n->data);
+		if (andt) xmlAddChild (terms, andt);
+	}
+	return terms;
+}
+
+/* ======================================================= */
+
+static xmlNodePtr
+qof_query_sorts_to_xml (QofQuery *q)
+{
+	QofQuerySort *s[3];
+	qof_query_get_sorts (q, &s[0], &s[1], &s[2]);
+
+	if (NULL == s[0]) return NULL;
+
+	xmlNodePtr sortlist = xmlNewNode (NULL, "qofquery:sort-list");
+	int i;
+	for (i=0; i<3; i++)
+	{
+		if (NULL == s[i]) continue;
+
+		GSList *plist = qof_query_sort_get_param_path (s[i]);
+		if (!plist) continue;
+
+		xmlNodePtr sort = xmlNewNode (NULL, "qofquery:sort");
+		xmlAddChild (sortlist, sort);
+
+		xmlNodePtr topnode = sort;
+
+		gboolean increasing = qof_query_sort_get_increasing (s[i]);
+		PUT_STR ("qofquery:order", increasing ? "DESCENDING" : "ASCENDING");
+
+		gint opt = qof_query_sort_get_sort_options (s[i]);
+		PUT_INT32 ("qofquery:options", opt);
+
+		xmlNodePtr pl = qof_query_param_path_to_xml (plist);
+		if (pl) xmlAddChild (sort, pl);
+	}
+
+	return sortlist;
+}
+
+/* ======================================================= */
+
+static void
+do_qof_query_to_xml (QofQuery *q, xmlNodePtr topnode)
+{
+	QofIdType search_for = qof_query_get_search_for (q);
+	PUT_STR ("qofquery:search-for", search_for);
+
+	xmlNodePtr terms = qof_query_terms_to_xml(q);
+	if (terms) xmlAddChild (topnode, terms);
+
+	xmlNodePtr sorts = qof_query_sorts_to_xml (q);
+	if (sorts) xmlAddChild (topnode, sorts);
+
+	gint max_results = qof_query_get_max_results (q);
+	PUT_INT32 ("qofquery:max-results", max_results);
+}
+
+/* ======================================================= */
+
+xmlNodePtr
+qof_query_to_xml (QofQuery *q)
+{
+	xmlNodePtr topnode;
+	xmlNodePtr node;
+	xmlNsPtr   ns;
+
+	topnode = xmlNewNode(NULL, "qof:qofquery");
+	xmlSetProp(topnode, "version", "1.0.1");
+
+	// XXX path to DTD is wrong
+	// ns = xmlNewNs (topnode, "file:" "/usr/share/lib" "/qofquery.dtd", "qof");
+
+	do_qof_query_to_xml (q, topnode);
+
+	return topnode;
+}
+
+/* =============================================================== */
+
+#ifdef UNIT_TEST
+
+#include <stdio.h>
+#include <qof/qofsql.h>
+
+int main (int argc, char * argv[])
+{
+	QofQuery *q;
+	QofSqlQuery *sq;
+
+	qof_query_init();
+	qof_object_initialize ();
+
+	static QofParam params[] = {
+		{ "adate",  QOF_TYPE_DATE, NULL, NULL},
+		{ "aint",   QOF_TYPE_INT32, NULL, NULL},
+		{ "aint64", QOF_TYPE_INT64, NULL, NULL},
+		{ "astr",   QOF_TYPE_STRING, NULL, NULL},
+		{ NULL },
+	};
+
+	qof_class_register ("GncABC", NULL, params);
+	sq = qof_sql_query_new();
+
+	qof_sql_query_parse (sq, 
+	    "SELECT * from GncABC WHERE aint = 123 "
+	    "or not astr=\'asdf\' "
+	    "and aint64 = 9876123456789;");
+	// qof_sql_query_parse (sq, "SELECT * from GncABC;");
+	q = qof_sql_query_get_query (sq);
+
+	qof_query_print (q);
+
+	xmlDocPtr doc = doc = xmlNewDoc("1.0");
+	xmlNodePtr topnode = qof_query_to_xml (q);
+	xmlDocSetRootElement(doc,topnode);
+
+	xmlChar *xbuf;
+	int bufsz;
+	xmlDocDumpFormatMemory (doc, &xbuf, &bufsz, 1);
+
+	printf ("%s\n", xbuf);
+	xmlFree (xbuf);
+	xmlFreeDoc(doc);
+
+#if 0
+printf ("duude\n");
+	// xmlOutputBufferPtr xbuf = xmlAllocOutputBuffer (enc);
+	xmlOutputBufferPtr xbuf = xmlOutputBufferCreateFile (stdout, NULL);
+printf ("duude\n");
+
+	xbuf = xmlOutputBufferCreateFd (1, NULL);
+printf ("duude\n");
+	xmlNodeDumpOutput (xbuf, NULL, topnode, 99, 99, "iso-8859-1");
+	// xmlElemDump (stdout, NULL, topnode);
+#endif
+
+	return 0;
+}
+
+#endif /* UNIT_TEST */
+
+/* ======================== END OF FILE =================== */
Index: guid.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/guid.h,v
retrieving revision 1.19
retrieving revision 1.20
diff -Lsrc/engine/guid.h -Lsrc/engine/guid.h -u -r1.19 -r1.20
--- src/engine/guid.h
+++ src/engine/guid.h
@@ -134,6 +134,9 @@
  *  'a' through 'f'. The encoding will always be GUID_ENCODING_LENGTH 
  *  characters long. 
  *
+ *  XXX This routine is not thread safe and is deprecated. Please
+ *  use the routine guid_to_string_buff() instead.
+ *
  *  @param guid The guid to print.
  *
  *  @return A pointer to the starting character of the string.  The
Index: qofclass.h
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/qofclass.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -Lsrc/engine/qofclass.h -Lsrc/engine/qofclass.h -u -r1.4 -r1.5
--- src/engine/qofclass.h
+++ src/engine/qofclass.h
@@ -145,6 +145,11 @@
  * qof_class_register ("myObjectName", myObjectCompare, &myParams);
  */
 
+/** Return true if the the indicated type is registered, 
+ *  else return FALSE.
+ */
+gboolean qof_class_is_registered (QofIdTypeConst obj_name);
+
 /** Return the core datatype of the specified object's parameter */
 QofType qof_class_get_parameter_type (QofIdTypeConst obj_name,
 					   const char *param_name);


More information about the Gnucash-changes mailing list