QOF Query Usage Example

# QOF Queries Over a Set of Objects

- Home
- SourceForge
- Documentation
- QOF-Generator
- GObject queries
- Why QOF?
- Data Freedom
- Support

Generate your own examples using your own data and build a test application.

The example below shows how to perform an SQL query over a collection of generic, programmer-defined objects. The example comes in two parts: first, the definition of a generic object, and second, the actual 'application program' that queries over the set of objects.

## Defining a Queriable Object

QOF provides a loose run-time typed object system. To make an object queriable, you must use that system. However, it is our hope that the system is sufficiently loose that it doesn't impose any undesired, forced structure on your particular programming style. We are trying to provide mechanism, not policy. Note that this example is in C not C++.

QOF has only two dependencies, and those are GLib and libxml2.

GLib is a collection of basic algorithms and structures for C programmers, such as linked lists, hash tables, and more. Glib is a fairly small, fast and simple system, and should not present difficulties to the experienced programmer. In the example below, the list of results from the query are returned as a linked list of GLib objects. Please note that this example neither uses nor requires GObjects or Gtk+ in any way.

    /** @file my-object.c
     *   @brief Example definition of a queriable object.
     *   @author Copyright (c) 2003,2004 Linas Vepstas <linas@linas.org>
     *
     *  This example program shows how to configure an arbitrary
     *  programmer-defined oject "MyObj" so that the Query routines
     *  can be used on it.  It shows one part of the total:
     *  -- The object definition, showing how to hook into the query system.
     *
     * Please make note of how the 'book' is used.  A 'book' can serve
     * as a collection of collections.  In the example below, the book
     * will be used to hold all of the instances of 'my object' so that
     * these can be found later.  Note that the book doesn't need to
     * exclusively hold only instances of 'my object', it can hold
     * collections of other types as well.
     *
     * The above ability is one reason why 'book' holds such a central
     * place in the QOF system.  Note also: when the query is performed
     * over all instances of 'my object', its not really over *all*
     * instances; its only over those instances stored in the given book.
     * Thus, the book is a kind of dataset, and by using books, one
     * can have multiple disjoint datasets.
     */

    #include <glib.h>
    #include <qof/qof.h>
    #include "my-object.h"

    /* ===================================================== */

    MyObj *
    my_obj_new (QofBook *book)
    {
       MyObj *m = g_new0 (MyObj,1);

       /* Make sure we keep track of every object;
        * otherwise we won't be able to search over them.
        * Do this by storing them in a collection.
        *
        * Although we use a 'collection' in this example, there is no
        * requirement that this be done.  One could use a global variable
        * or something else.  The only requirement is that the 'foreach'
        * below is able to find every object.
        *
        * The collection 'data' pointer can be used to store any kind
        * of user-defined data.  Here, we use it to hold a linked
        * list (the GList), but it could have held anything.  Most
        * other objects (e.g. GnuCash objects) probably don't use the
        * data pointer in this way.
        */
       QofCollection *coll = qof_book_get_collection (book, MYOBJ_ID);
       GList *all_my_objs = qof_collection_get_data (coll);
       all_my_objs = g_list_prepend (all_my_objs, m);
       qof_collection_set_data (coll, all_my_objs);

       return m;
    }

    /* Generic object getters */
    int
    my_obj_get_a (MyObj *m)
    {
       return m->a;
    }

    int
    my_obj_get_b (MyObj *m)
    {
       return m->b;
    }

    const char *
    my_obj_get_memo (MyObj *m)
    {
       return m->memo;
    }

    /* Loop over every instance of MyObj, and apply the callback to it.
     * This routine must be defined for queries to be possible. */
    void
    my_obj_foreach (QofCollection *coll, QofEntityForeachCB cb, gpointer ud)
    {
       GList *n;
       GList *all_my_objs = qof_collection_get_data (coll);

       for (n=all_my_objs; n; n=n->next)
       {
          cb (n->data, ud);
       }
    }

    /* Provide a default mechanism to sort MyObj.
     * This is neeeded so that query results can be returned in
     * some 'reasonable' order.  If you don't want to sort,
     * just have this function always return 0.
     */
    int
    my_obj_order (MyObj *a, MyObj *b)
    {
       if ( (a) && !(b) ) return -1;
       if ( !(a) && (b) ) return +1;
       if ( !(a) && !(b) ) return 0;

       if ((a)->a > (b)->a) return +1;
       if ((a)->a == (b)->a) return 0;
       return -1;
    }

    /* ===================================================== */
    /* Provide infrastructure to register my object with QOF */

    static QofObject myobj_object_def =
    {
       interface_version: QOF_OBJECT_VERSION,
       e_type:            MYOBJ_ID,
       type_label:        "My Blinking Object",
       book_begin:        NULL,
       book_end:          NULL,
       is_dirty:          NULL,
       mark_clean:        NULL,
       foreach:           my_obj_foreach,
       printable:         NULL,
    };

    gboolean myObjRegister (void)
    {
       /* Associate an ASCII name to each getter, as well as the return type */
       static QofParam params[] = {
         { MYOBJ_A,     QOF_TYPE_INT32, (QofAccessFunc)my_obj_get_a, NULL },
         { MYOBJ_B,     QOF_TYPE_INT32, (QofAccessFunc)my_obj_get_b, NULL },
         { MYOBJ_MEMO,  QOF_TYPE_STRING, (QofAccessFunc)my_obj_get_memo, NULL },
         { NULL },
       };

       qof_class_register (MYOBJ_ID, (QofSortFunc)my_obj_order, params);
       return qof_object_register (&myobj_object_def);
    }

## The Main Application Program

The above defined a C object. Below follows an application program that shows how to perform SQL queries over a collection of these objects.

    /** @file sql-example.c
     *  @breif Example program showing SQL to perform query over objects.
     *  @author Copyright (c) 2003, 2004 Linas Vepstas <linas@linas.org>
     *
     *  This example program shows how to configure an arbitrary
     *  programmer-defined oject "MyObj" so that the Query routines
     *  can be used on it.  It consists of four basic peices:
     *  -- The object definition, showing how to hook into the query system.
     *     (this part is in the "my-object.c" file)
     *  -- Generic application intialization, including the creation of
     *     a number of instances of MyObj.
     *  -- QOF intialization, required before QOF can be used.
     *  -- The definition and running of a query, and a printout of the
     *     results.
     */

    #include <glib.h>
    #include <qof/qof.h>
    #include "my-object.h"

    /* ===================================================== */

    QofBook *
    my_app_init (void)
    {
       QofBook *book;

       /* Perform the application object registeration */
       myObjRegister ();

       /* Create a new top-level object container */
       book =  qof_book_new();

       return book;
    }

    void
    my_app_shutdown (QofBook *book)
    {
       /* Terminate our storage. This prevents the system from
        * being used any further. */
       qof_book_destroy (book);
    }

    void
    my_app_create_data (QofBook *book)
    {
       MyObj *m;

       /* Pretend our app has some objects; we will perform
        * the search over these objects */

       m = my_obj_new (book);
       m->a = 1;
       m->b = 1;
       m->memo = "Hiho Silver!";

       m = my_obj_new (book);
       m->a = 1;
       m->b = 42;
       m->memo = "The Answer to the Question";

       m = my_obj_new (book);
       m->a = 99;
       m->b = 1;
       m->memo = "M M M My Sharona";
    }

    /* ===================================================== */
    /* A routine that will build an actual query, run it, and
     * print the results.
     */

    void
    my_app_run_query (QofSqlQuery *q, char *sql_str)
    {
       GList *results, *n;

       /* Run the query */
       results = qof_sql_query_run (q, sql_str);

       printf ("------------------------------------------\n");
       printf ("Query string is: %s\n", sql_str);
       printf ("Query returned %d results:\n", g_list_length(results));
       for (n=results; n; n=n->next)
       {
          MyObj *m = n->data;
          printf ("Found a matching object, a=%d b=%d memo=\"%s\"\n",
              m->a, m->b, m->memo);
       }
       printf ("\n");
    }

    void
    my_app_do_some_queries (QofBook *book)
    {
       QofSqlQuery *q;
       GList *n;

       /* Print out the baseline: all of the instances in the system */
       printf ("\n");
       printf ("My Object collection contains the following objects:\n");
       QofCollection *coll = qof_book_get_collection (book, MYOBJ_ID);
       GList *all_my_objs = qof_collection_get_data (coll);
       for (n=all_my_objs; n; n=n->next)
       {
          MyObj *m = n->data;
          printf ("    a=%d b=%d memo=\"%s\"\n",
              m->a, m->b, m->memo);
       }
       printf ("\n");

       /* Create a new query */
       q =  qof_sql_query_new ();

       /* Set the book to be searched */
       qof_sql_query_set_book(q, book);

       /* Describe the query to be performed.
        * We want to find all objects whose "memo" field matches
        * a particular string, or all objects whose "b" field is 42.
        */
       char * str = "SELECT * FROM " MYOBJ_ID
               " WHERE (" MYOBJ_MEMO " = 'M M M My Sharona') OR "
       		  "       (" MYOBJ_B " = 42);";

       my_app_run_query (q, str);

       /* Do some more */
       my_app_run_query (q, "SELECT * FROM MyObj WHERE MyObj_a = 1;");
       my_app_run_query (q, "SELECT * FROM MyObj WHERE (MyObj_a = 1) AND (MyObj_b=42);");
       my_app_run_query (q, "SELECT * FROM MyObj WHERE (MyObj_a = 3);");
       my_app_run_query (q, "SELECT * FROM MyObj;");
       my_app_run_query (q, "SELECT * FROM MyObj ORDER BY MyObj_a DESC;");
       my_app_run_query (q, "SELECT * FROM MyObj ORDER BY MyObj_b DESC;");
       my_app_run_query (q, "SELECT * FROM MyObj WHERE MyObj_a = 1 ORDER BY MyObj_b DESC;");

       /* The query isn't needed any more; discard it */
       qof_sql_query_destroy (q);
    }

    /* ===================================================== */

    int
    main (int argc, char *argv[])
    {
       QofBook *book;

       /* Initialize the QOF framework */
       gnc_engine_get_string_cache();
       guid_init();
       qof_object_initialize ();
       qof_query_init ();
       qof_book_register ();

       /* Do application-specific things */
       book = my_app_init();
       my_app_create_data(book);
       my_app_do_some_queries (book);
       my_app_shutdown (book);

       /* Perform a clean shutdown */
       qof_query_shutdown ();
       qof_object_shutdown ();
       guid_shutdown ();
       gnc_engine_string_cache_destroy ();
    }

    /* =================== END OF FILE ===================== */