]> saetta.ns0.it Git - libgdaex/commitdiff
Added functions GdaExQueryEditor::load_tables_from_xml and
authorAndrea Zagli <a.zagli@comune.scandicci.fi.it>
Thu, 24 May 2012 13:33:39 +0000 (15:33 +0200)
committerAndrea Zagli <a.zagli@comune.scandicci.fi.it>
Thu, 24 May 2012 13:33:39 +0000 (15:33 +0200)
GdaExQueryEditor::add_relation_slist.

data/Makefile.am
data/gdaex_query_editor.dtd [new file with mode: 0644]
src/queryeditor.c
src/queryeditor.h
tests/query_editor.c
tests/query_editor.xml [new file with mode: 0644]

index b0dfb967c05dd80d5a823ca1e2c5b7b321a02af6..0e3b653bfdb8ff3eed1d59f13ea3b56c715236af 100644 (file)
@@ -1 +1,4 @@
 SUBDIRS = libgdaex
+
+EXTRA_DIST = \
+             gdaex_query_editor.dtd
diff --git a/data/gdaex_query_editor.dtd b/data/gdaex_query_editor.dtd
new file mode 100644 (file)
index 0000000..2156c6c
--- /dev/null
@@ -0,0 +1,45 @@
+<!ELEMENT gdaex_query_editor (tables, fields+, relations)>
+
+<!ELEMENT tables (table+)>
+
+<!ELEMENT table (name, name_visible?)>
+
+<!ELEMENT name (#PCDATA)>
+<!ELEMENT name_visible (#PCDATA)>
+
+<!ELEMENT fields (field+)>
+
+<!ATTLIST fields
+       table_name CDATA #REQUIRED
+>
+
+<!ELEMENT field (name, name_visible?, description?, alias?, type, for_show, always_showed, for_where, available_where_type, for_order, decode)>
+
+<!ELEMENT table_name (#PCDATA)>
+<!ELEMENT name_visible (#PCDATA)>
+<!ELEMENT description (#PCDATA)>
+<!ELEMENT alias (#PCDATA)>
+<!ELEMENT type (#PCDATA)>
+<!ELEMENT for_show (#PCDATA)>
+<!ELEMENT always_showed (#PCDATA)>
+<!ELEMENT for_where (#PCDATA)>
+<!ELEMENT available_where_type (#PCDATA)>
+<!ELEMENT for_order (#PCDATA)>
+
+<!ELEMENT decode (table_name, join_type, field_name_to_join, field_name_to_show, alias)>
+
+<!ELEMENT join_type (#PCDATA)>
+<!ELEMENT field_name_to_join (#PCDATA)>
+<!ELEMENT field_name_to_show (#PCDATA)>
+
+<!ELEMENT relations (relation+)>
+
+<!ELEMENT relation (table_left, table_right, join_type, fields_joined+)>
+
+<!ELEMENT table_left (#PCDATA)>
+<!ELEMENT table_right (#PCDATA)>
+
+<!ELEMENT fields_joined (field_left, field_right)>
+
+<!ELEMENT field_left (#PCDATA)>
+<!ELEMENT field_right (#PCDATA)>
\ No newline at end of file
index b79c4de90b7cd6bc140e189388e3facfcd3e54d3..1fc0ae52bd7d81fdeb1daa66f22abc404e91938a 100644 (file)
@@ -26,6 +26,7 @@
 #include <glib/gi18n-lib.h>
 
 #include <libxml/parser.h>
+#include <libxml/xpath.h>
 
 #include "queryeditor.h"
 #include "queryeditorentry.h"
@@ -77,6 +78,8 @@ static void gdaex_query_editor_get_property (GObject *object,
                                GValue *value,
                                GParamSpec *pspec);
 
+static void gdaex_query_editor_clean (GdaExQueryEditor *gdaex_query_editor);
+
 static gboolean _gdaex_query_editor_add_table (GdaExQueryEditor *qe,
                               const gchar *table_name,
                               const gchar *table_name_visibile,
@@ -294,10 +297,7 @@ gdaex_query_editor_init (GdaExQueryEditor *gdaex_query_editor)
 {
        GdaExQueryEditorPrivate *priv = GDAEX_QUERY_EDITOR_GET_PRIVATE (gdaex_query_editor);
 
-       priv->tables = g_hash_table_new (g_str_hash, g_str_equal);
-
-       priv->hpaned_main = NULL;
-       priv->relations = NULL;
+       gdaex_query_editor_clean (gdaex_query_editor);
 
        priv->lstore_link_type = gtk_list_store_new (2,
                                                     G_TYPE_UINT,
@@ -601,6 +601,7 @@ gdaex_query_editor_table_add_field (GdaExQueryEditor *qe,
        GdaExQueryEditorField *_field;
 
        g_return_val_if_fail (GDAEX_IS_QUERY_EDITOR (qe), FALSE);
+       g_return_val_if_fail (table_name != NULL, FALSE);
 
        priv = GDAEX_QUERY_EDITOR_GET_PRIVATE (qe);
 
@@ -732,16 +733,15 @@ gdaex_query_editor_table_add_field (GdaExQueryEditor *qe,
  * gdaex_query_editor_add_relation:
  * @table1: relation's left part.
  * @table2: relation's right part.
- * @...: couples of fields name, one from @table1 and one from @table2.; 
- *       must be terminated with a #NULL value.
+ * @fields_joined: couples of fields name, one from @table1 and one from @table2.
  *
  */
 gboolean
-gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
-                                 const gchar *table1,
-                                 const gchar *table2,
-                                 GdaExQueryEditorJoinType join_type,
-                                 ...)
+gdaex_query_editor_add_relation_slist (GdaExQueryEditor *qe,
+                                       const gchar *table1,
+                                       const gchar *table2,
+                                       GdaExQueryEditorJoinType join_type,
+                                       GSList *fields_joined)
 {
        GdaExQueryEditorPrivate *priv;
 
@@ -751,13 +751,10 @@ gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
 
        GdaExQueryEditorRelation *relation;
 
-       va_list fields;
-       gchar *field_name1;
-       gchar *field_name2;
-
        gboolean create_relation;
 
        g_return_val_if_fail (GDAEX_IS_QUERY_EDITOR (qe), FALSE);
+       g_return_val_if_fail (fields_joined != NULL, FALSE);
 
        priv = GDAEX_QUERY_EDITOR_GET_PRIVATE (qe);
 
@@ -782,19 +779,24 @@ gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
                }
        relation->table2 = table;
 
-       va_start (fields, join_type);
-
-       while ((field_name1 = va_arg (fields, gchar *)) != NULL
-              && (field_name2 = va_arg (fields, gchar *)) != NULL)
+       while (fields_joined != NULL)
                {
-                       field1 = g_hash_table_lookup (relation->table1->fields, field_name1);
+                       field1 = g_hash_table_lookup (relation->table1->fields, (gchar *)fields_joined->data);
                        if (field1 == NULL)
                                {
                                        continue;
                                }
 
-                       field2 = g_hash_table_lookup (relation->table2->fields, field_name2);
-                       if (field2 == NULL)
+                       fields_joined = g_slist_next (fields_joined);
+                       if (fields_joined != NULL)
+                               {
+                                       field2 = g_hash_table_lookup (relation->table2->fields, (gchar *)fields_joined->data);
+                                       if (field2 == NULL)
+                                               {
+                                                       continue;
+                                               }
+                               }
+                       else
                                {
                                        continue;
                                }
@@ -802,9 +804,9 @@ gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
                        relation->fields1 = g_slist_append (relation->fields1, field1);
                        relation->fields2 = g_slist_append (relation->fields2, field2);
                        create_relation = TRUE;
-               }
 
-       va_end (fields);
+                       fields_joined = g_slist_next (fields_joined);
+               }
 
        if (create_relation)
                {
@@ -821,6 +823,575 @@ gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
        return TRUE;
 }
 
+/**
+ * gdaex_query_editor_add_relation:
+ * @table1: relation's left part.
+ * @table2: relation's right part.
+ * @...: couples of fields name, one from @table1 and one from @table2;
+ *       must be terminated with a #NULL value.
+ *
+ */
+gboolean
+gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
+                                 const gchar *table1,
+                                 const gchar *table2,
+                                 GdaExQueryEditorJoinType join_type,
+                                 ...)
+{
+       GdaExQueryEditorPrivate *priv;
+
+       va_list fields;
+       gchar *field_name1;
+       gchar *field_name2;
+
+       GSList *fields_joined;
+
+       g_return_val_if_fail (GDAEX_IS_QUERY_EDITOR (qe), FALSE);
+
+       priv = GDAEX_QUERY_EDITOR_GET_PRIVATE (qe);
+
+       fields_joined = NULL;
+       va_start (fields, join_type);
+
+       while (TRUE)
+               {
+                       field_name1 = va_arg (fields, gchar *);
+                       if (field_name1 == NULL)
+                               {
+                                       break;
+                               }
+                       field_name2 = va_arg (fields, gchar *);
+                       if (field_name2 == NULL)
+                               {
+                                       break;
+                               }
+
+                       fields_joined = g_slist_append (fields_joined, field_name1);
+                       fields_joined = g_slist_append (fields_joined, field_name2);
+               }
+
+       va_end (fields);
+
+       return gdaex_query_editor_add_relation_slist (qe, table1, table2, join_type, fields_joined);
+}
+
+static GdaExQueryEditorFieldType
+gdaex_query_editor_str_to_field_type (gchar *str)
+{
+       GdaExQueryEditorFieldType ret;
+
+       g_return_val_if_fail (str != NULL, 0);
+
+       ret = 0;
+
+       if (g_strcmp0 (str, "text") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_TEXT;
+               }
+       else if (g_strcmp0 (str, "integer") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_INTEGER;
+               }
+       else if (g_strcmp0 (str, "double") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_DOUBLE;
+               }
+       else if (g_strcmp0 (str, "date") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_DATE;
+               }
+       else if (g_strcmp0 (str, "datetime") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_DATETIME;
+               }
+       else if (g_strcmp0 (str, "time") == 0)
+               {
+                       ret = GDAEX_QE_FIELD_TYPE_TIME;
+               }
+
+       return ret;
+}
+
+static gboolean
+gdaex_query_editor_str_to_boolean (gchar *str)
+{
+       gboolean ret;
+
+       g_return_val_if_fail (str != NULL, FALSE);
+
+       ret = FALSE;
+
+       if (g_strcmp0 (str, "f") == 0
+           || g_strcmp0 (str, "false") == 0
+           || g_strcmp0 (str, "no") == 0
+           || g_strcmp0 (str, "0") == 0)
+               {
+                       ret = FALSE;
+               }
+       else if (g_strcmp0 (str, "t") == 0
+           || g_strcmp0 (str, "true") == 0
+           || g_strcmp0 (str, "yes") == 0
+           || g_strcmp0 (str, "1") == 0)
+               {
+                       ret = TRUE;
+               }
+
+       return ret;
+}
+
+static guint
+gdaex_query_editor_str_to_where_type (gchar *str)
+{
+       guint ret;
+
+       gchar **types;
+
+       guint i;
+       guint l;
+
+       g_return_val_if_fail (str != NULL, 0);
+
+       ret = 0;
+
+       types = g_strsplit (str, "|", 0);
+
+       l = g_strv_length (types);
+       for (i = 0; i < l; i++)
+               {
+                       if (g_strcmp0 (types[i], "equal") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_EQUAL;
+                               }
+                       else if (g_strcmp0 (types[i], "starts") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_STARTS;
+                               }
+                       else if (g_strcmp0 (types[i], "contains") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_CONTAINS;
+                               }
+                       else if (g_strcmp0 (types[i], "ends") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_ENDS;
+                               }
+                       else if (g_strcmp0 (types[i], "istarts") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_ISTARTS;
+                               }
+                       else if (g_strcmp0 (types[i], "icontains") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_ICONTAINS;
+                               }
+                       else if (g_strcmp0 (types[i], "iends") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_IENDS;
+                               }
+                       else if (g_strcmp0 (types[i], "great") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_GREAT;
+                               }
+                       else if (g_strcmp0 (types[i], "great_equal") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_GREAT_EQUAL;
+                               }
+                       else if (g_strcmp0 (types[i], "less") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_LESS;
+                               }
+                       else if (g_strcmp0 (types[i], "less_equal") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_LESS_EQUAL;
+                               }
+                       else if (g_strcmp0 (types[i], "between") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_BETWEEN;
+                               }
+                       else if (g_strcmp0 (types[i], "is_null") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_IS_NULL;
+                               }
+                       else if (g_strcmp0 (types[i], "string") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_STRING;
+                               }
+                       else if (g_strcmp0 (types[i], "number") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_NUMBER;
+                               }
+                       else if (g_strcmp0 (types[i], "datetime") == 0)
+                               {
+                                       ret |= GDAEX_QE_WHERE_TYPE_DATETIME;
+                               }
+               }
+
+       g_strfreev (types);
+
+       return ret;
+}
+
+static GdaExQueryEditorJoinType
+gdaex_query_editor_str_to_join_type (gchar *str)
+{
+       GdaExQueryEditorJoinType ret;
+
+       g_return_val_if_fail (str != NULL, 0);
+
+       ret = 0;
+
+       if (g_strcmp0 (str, "inner") == 0)
+               {
+                       ret = GDAEX_QE_JOIN_TYPE_INNER;
+               }
+       else if (g_strcmp0 (str, "left") == 0)
+               {
+                       ret = GDAEX_QE_JOIN_TYPE_LEFT;
+               }
+
+       return ret;
+}
+
+void
+gdaex_query_editor_load_tables_from_xml (GdaExQueryEditor *qe,
+                                         xmlNode *root,
+                                         gboolean clean)
+{
+       xmlDoc *xdoc;
+       xmlXPathContextPtr xpcontext;
+       xmlXPathObjectPtr xpresult;
+       xmlNodeSetPtr xnodeset;
+
+       xmlNode *xnode;
+       xmlNode *xtable;
+       xmlNode *xfield;
+       xmlNode *xrelation;
+       xmlNode *xdecode;
+       xmlNode *xfields_joined;
+       xmlNode *cur;
+
+       guint i;
+
+       gchar *table_name;
+       gchar *name;
+       gchar *name_visible;
+
+       GdaExQueryEditorField *field;
+
+       gchar *table_left;
+       gchar *table_right;
+       GdaExQueryEditorJoinType join_type;
+       GSList *fields_joined;
+
+       g_return_if_fail (GDAEX_IS_QUERY_EDITOR (qe));
+       g_return_if_fail (root != NULL);
+       g_return_if_fail (xmlStrcmp (root->name, "gdaex_query_editor") == 0);
+
+       if (clean)
+               {
+                       gdaex_query_editor_clean (qe);
+                       gdaex_query_editor_clean_choices (qe);
+               }
+
+       table_name = NULL;
+       name = NULL;
+       name_visible = NULL;
+       join_type = 0;
+       fields_joined = NULL;
+
+       xdoc = xmlNewDoc ("");
+       xmlDocSetRootElement (xdoc, root);
+
+       /* search tables node */
+       xpcontext = xmlXPathNewContext (xdoc);
+       xpcontext->node = xmlDocGetRootElement (xdoc);
+       xpresult = xmlXPathEvalExpression ((const xmlChar *)"child::tables", xpcontext);
+       if (xpresult != NULL && !xmlXPathNodeSetIsEmpty (xpresult->nodesetval))
+               {
+                       xnodeset = xpresult->nodesetval;
+                       for (i = 0; i < xnodeset->nodeNr; i++)
+                               {
+                                       xtable = xnodeset->nodeTab[i]->children;
+                                       while (xtable != NULL)
+                                               {
+                                                       if (xmlStrcmp (xtable->name, "table") == 0)
+                                                               {
+                                                                       if (table_name != NULL)
+                                                                               {
+                                                                                       g_free (table_name);
+                                                                                       table_name = NULL;
+                                                                               }
+                                                                       if (name_visible != NULL)
+                                                                               {
+                                                                                       g_free (name_visible);
+                                                                                       name_visible = NULL;
+                                                                               }
+
+                                                                       cur = xtable->children;
+                                                                       while (cur != NULL)
+                                                                               {
+                                                                                       if (xmlStrcmp (cur->name, "name") == 0)
+                                                                                               {
+                                                                                                       table_name = xmlNodeGetContent (cur);
+                                                                                                       if (table_name != NULL)
+                                                                                                               {
+                                                                                                                       table_name = g_strstrip (g_strdup (table_name));
+                                                                                                               }
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "name_visible") == 0)
+                                                                                               {
+                                                                                                       name_visible = xmlNodeGetContent (cur);
+                                                                                                       if (name_visible != NULL)
+                                                                                                               {
+                                                                                                                       name_visible = g_strstrip (g_strdup (name_visible));
+                                                                                                               }
+                                                                                               }
+
+                                                                                       if (table_name != NULL)
+                                                                                               {
+                                                                                                       gdaex_query_editor_add_table (qe, table_name, name_visible);
+                                                                                               }
+
+                                                                                       cur = cur->next;
+                                                                               }
+                                                               }
+
+                                                       xtable = xtable->next;
+                                               }
+                               }
+               }
+       else
+               {
+                       g_warning (_("No table's definitions on xml file."));
+               }
+
+       /* search fields node */
+       if (table_name != NULL)
+               {
+                       g_free (table_name);
+                       table_name = NULL;
+               }
+
+       xpcontext = xmlXPathNewContext (xdoc);
+       xpcontext->node = xmlDocGetRootElement (xdoc);
+       xpresult = xmlXPathEvalExpression ((const xmlChar *)"child::fields", xpcontext);
+       if (xpresult != NULL && !xmlXPathNodeSetIsEmpty (xpresult->nodesetval))
+               {
+                       xnodeset = xpresult->nodesetval;
+                       for (i = 0; i < xnodeset->nodeNr; i++)
+                               {
+                                       table_name = xmlGetProp (xnodeset->nodeTab[i], "table");
+
+                                       xfield = xnodeset->nodeTab[i]->children;
+                                       while (xfield != NULL)
+                                               {
+                                                       if (xmlStrcmp (xfield->name, "field") == 0)
+                                                               {
+                                                                       field = g_new0 (GdaExQueryEditorField, 1);
+                                                                       field->for_show = TRUE;
+                                                                       field->for_where = TRUE;
+                                                                       field->for_order = TRUE;
+
+                                                                       cur = xfield->children;
+                                                                       while (cur != NULL)
+                                                                               {
+                                                                                       if (xmlStrcmp (cur->name, "name") == 0)
+                                                                                               {
+                                                                                                       field->name = xmlNodeGetContent (cur);
+                                                                                                       if (field->name != NULL)
+                                                                                                               {
+                                                                                                                       field->name = g_strstrip (g_strdup (field->name));
+                                                                                                               }
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "name_visible") == 0)
+                                                                                               {
+                                                                                                       field->name_visible = xmlNodeGetContent (cur);
+                                                                                                       if (field->name_visible != NULL)
+                                                                                                               {
+                                                                                                                       field->name_visible = g_strstrip (g_strdup (field->name_visible));
+                                                                                                               }
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "description") == 0)
+                                                                                               {
+                                                                                                       field->description = xmlNodeGetContent (cur);
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "alias") == 0)
+                                                                                               {
+                                                                                                       field->alias = xmlNodeGetContent (cur);
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "type") == 0)
+                                                                                               {
+                                                                                                       field->type = gdaex_query_editor_str_to_field_type (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "for_show") == 0)
+                                                                                               {
+                                                                                                       field->for_show = gdaex_query_editor_str_to_boolean (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "always_showed") == 0)
+                                                                                               {
+                                                                                                       field->always_showed = gdaex_query_editor_str_to_boolean (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "for_where") == 0)
+                                                                                               {
+                                                                                                       field->for_where = gdaex_query_editor_str_to_boolean (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "available_where_type") == 0)
+                                                                                               {
+                                                                                                       field->available_where_type = gdaex_query_editor_str_to_where_type (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "for_order") == 0)
+                                                                                               {
+                                                                                                       field->for_order = gdaex_query_editor_str_to_boolean (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "decode") == 0)
+                                                                                               {
+                                                                                                       xdecode = cur->children;
+                                                                                                       while (xdecode != NULL)
+                                                                                                               {
+                                                                                                                       if (xmlStrcmp (xdecode->name, "table_name"))
+                                                                                                                               {
+                                                                                                                                       field->decode_table2 = xmlNodeGetContent (xdecode);
+                                                                                                                               }
+                                                                                                                       else if (xmlStrcmp (xdecode->name, "join_type"))
+                                                                                                                               {
+                                                                                                                                       field->decode_join_type = gdaex_query_editor_str_to_join_type (xmlNodeGetContent (xdecode));
+                                                                                                                               }
+                                                                                                                       else if (xmlStrcmp (xdecode->name, "field_name_to_join"))
+                                                                                                                               {
+                                                                                                                                       field->decode_field2 = xmlNodeGetContent (xdecode);
+                                                                                                                               }
+                                                                                                                       else if (xmlStrcmp (xdecode->name, "field_name_to_show"))
+                                                                                                                               {
+                                                                                                                                       field->decode_field_to_show = xmlNodeGetContent (xdecode);
+                                                                                                                               }
+                                                                                                                       else if (xmlStrcmp (xdecode->name, "alias"))
+                                                                                                                               {
+                                                                                                                                       field->decode_field_alias = xmlNodeGetContent (xdecode);
+                                                                                                                               }
+
+                                                                                                                       xdecode = xdecode->next;
+                                                                                                               }
+                                                                                               }
+
+                                                                                       cur = cur->next;
+                                                                               }
+
+                                                                       gdaex_query_editor_table_add_field (qe, table_name, *field);
+                                                                       g_free (field);
+                                                               }
+
+                                                       xfield = xfield->next;
+                                               }
+
+                                       if (table_name != NULL)
+                                               {
+                                                       g_free (table_name);
+                                                       table_name = NULL;
+                                               }
+                               }
+               }
+       else
+               {
+                       g_warning (_("No field's definitions on xml file."));
+               }
+
+       /* search relations node */
+       xpcontext = xmlXPathNewContext (xdoc);
+       xpcontext->node = xmlDocGetRootElement (xdoc);
+       xpresult = xmlXPathEvalExpression ((const xmlChar *)"child::relations", xpcontext);
+       if (xpresult != NULL && !xmlXPathNodeSetIsEmpty (xpresult->nodesetval))
+               {
+                       xnodeset = xpresult->nodesetval;
+                       for (i = 0; i < xnodeset->nodeNr; i++)
+                               {
+                                       xrelation = xnodeset->nodeTab[i]->children;
+                                       while (xrelation != NULL)
+                                               {
+                                                       if (xmlStrcmp (xrelation->name, "relation") == 0)
+                                                               {
+                                                                       cur = xrelation->children;
+                                                                       while (cur != NULL)
+                                                                               {
+                                                                                       if (xmlStrcmp (cur->name, "table_left") == 0)
+                                                                                               {
+                                                                                                       table_left = xmlNodeGetContent (cur);
+                                                                                                       if (table_left != NULL)
+                                                                                                               {
+                                                                                                                       table_left = g_strstrip (g_strdup (table_left));
+                                                                                                               }
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "table_right") == 0)
+                                                                                               {
+                                                                                                       table_right = xmlNodeGetContent (cur);
+                                                                                                       if (table_right != NULL)
+                                                                                                               {
+                                                                                                                       table_right = g_strstrip (g_strdup (table_right));
+                                                                                                               }
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "join_type") == 0)
+                                                                                               {
+                                                                                                       join_type = gdaex_query_editor_str_to_join_type (xmlNodeGetContent (cur));
+                                                                                               }
+                                                                                       else if (xmlStrcmp (cur->name, "fields_joined") == 0)
+                                                                                               {
+                                                                                                       xfields_joined = cur->children;
+                                                                                                       while (xfields_joined != NULL)
+                                                                                                               {
+                                                                                                                       if (xmlStrcmp (xfields_joined->name, "field_left") == 0)
+                                                                                                                               {
+                                                                                                                                       name = xmlNodeGetContent (xfields_joined);
+                                                                                                                                       if (name != NULL)
+                                                                                                                                               {
+                                                                                                                                                       name = g_strstrip (g_strdup (name));
+                                                                                                                                                       fields_joined = g_slist_append (fields_joined, name);
+                                                                                                                                               }
+                                                                                                                               }
+                                                                                                                       else if (xmlStrcmp (xfields_joined->name, "field_right") == 0)
+                                                                                                                               {
+                                                                                                                                       name = xmlNodeGetContent (xfields_joined);
+                                                                                                                                       if (name != NULL)
+                                                                                                                                               {
+                                                                                                                                                       name = g_strstrip (g_strdup (name));
+                                                                                                                                                       fields_joined = g_slist_append (fields_joined, name);
+                                                                                                                                               }
+                                                                                                                               }
+
+                                                                                                                       xfields_joined = xfields_joined->next;
+                                                                                                               }
+                                                                                               }
+
+                                                                                       cur = cur->next;
+                                                                               }
+
+                                                                       if (table_left != NULL
+                                                                           && table_right != NULL
+                                                                           && join_type > 0
+                                                                           && fields_joined != NULL)
+                                                                               {
+                                                                                       gdaex_query_editor_add_relation_slist (qe, table_left, table_right, join_type, fields_joined);
+                                                                               }
+                                                                       if (table_left != NULL)
+                                                                               {
+                                                                                       g_free (table_left);
+                                                                                       table_left = NULL;
+                                                                               }
+                                                                       if (table_right != NULL)
+                                                                               {
+                                                                                       g_free (table_right);
+                                                                                       table_right = NULL;
+                                                                               }
+                                                                       if (fields_joined != NULL)
+                                                                               {
+                                                                                       g_slist_free (fields_joined);
+                                                                                       fields_joined = NULL;
+                                                                               }
+                                                                       join_type = 0;
+                                                               }
+
+                                                       xrelation = xrelation->next;
+                                               }
+                               }
+               }
+}
+
 void
 gdaex_query_editor_clean_choices (GdaExQueryEditor *qe)
 {
@@ -2126,6 +2697,30 @@ gdaex_query_editor_get_property (GObject *object,
                }
 }
 
+static void
+gdaex_query_editor_clean (GdaExQueryEditor *gdaex_query_editor)
+{
+       GdaExQueryEditorPrivate *priv = GDAEX_QUERY_EDITOR_GET_PRIVATE (gdaex_query_editor);
+
+       if (priv->tables != NULL)
+               {
+                       g_hash_table_destroy (priv->tables);
+               }
+       priv->tables = g_hash_table_new (g_str_hash, g_str_equal);
+
+       if (GTK_IS_PANED (priv->hpaned_main))
+               {
+                       gtk_widget_destroy (priv->hpaned_main);
+               }
+       priv->hpaned_main = NULL;
+
+       if (priv->relations != NULL)
+               {
+                       g_slist_free (priv->relations);
+               }
+       priv->relations = NULL;
+}
+
 static gboolean
 _gdaex_query_editor_add_table (GdaExQueryEditor *qe,
                               const gchar *table_name,
index bdf9ed530e38b7ed56bac9b5fa57f86162ff59e9..40b41020ce1e694a6478feecf5acb706cb8f50c4 100644 (file)
@@ -143,12 +143,21 @@ gboolean gdaex_query_editor_add_table (GdaExQueryEditor *qe,
 gboolean gdaex_query_editor_table_add_field (GdaExQueryEditor *qe,
                                              const gchar *table_name,
                                              GdaExQueryEditorField field);
+gboolean gdaex_query_editor_add_relation_slist (GdaExQueryEditor *qe,
+                                                const gchar *table1,
+                                                const gchar *table2,
+                                                GdaExQueryEditorJoinType join_type,
+                                                GSList *fields_joined);
 gboolean gdaex_query_editor_add_relation (GdaExQueryEditor *qe,
                                           const gchar *table1,
                                           const gchar *table2,
                                           GdaExQueryEditorJoinType join_type,
                                           ...);
 
+void gdaex_query_editor_load_tables_from_xml (GdaExQueryEditor *qe,
+                                              xmlNode *root,
+                                              gboolean clean);
+
 void gdaex_query_editor_clean_choices (GdaExQueryEditor *qe);
 
 GdaSqlBuilder *gdaex_query_editor_get_sql_as_gdasqlbuilder (GdaExQueryEditor *qe);
index 8b4106f60a51d43e6a2fc21057513216c2ee3277..ce3cc8250b19cc1bb9ec5d9b928c2c72c0c34a59 100644 (file)
@@ -202,6 +202,16 @@ main (int argc, char *argv[])
        GtkWidget *btn_load_xml;
        GtkWidget *btn_ok;
 
+       gchar *xmlfile;
+
+       xmlfile = NULL;
+
+       GOptionEntry entries[] =
+               {
+                       { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME, &xmlfile, "Xml file", NULL },
+                       { NULL }
+               };
+
        gtk_init (&argc, &argv);
 
        gdaex = gdaex_new_from_string (g_strdup_printf ("SQLite://DB_DIR=%s;DB_NAME=test_prefix.db", TESTSDIR));
@@ -213,6 +223,7 @@ main (int argc, char *argv[])
 
        error = NULL;
        context = g_option_context_new ("tests");
+       g_option_context_add_main_entries (context, entries, "tests");
        g_option_context_add_group (context, gdaex_get_option_group (gdaex));
        g_option_context_parse (context, &argc, &argv, &error);
        if (error != NULL)
@@ -222,137 +233,155 @@ main (int argc, char *argv[])
 
        qe = gdaex_query_editor_new (gdaex);
 
-       gdaex_query_editor_add_table (qe, "clients", "Clients");
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("id");
-       field->name_visible = g_strdup ("ID");
-       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
-       field->for_show = TRUE;
-       field->always_showed = TRUE;
-       field->for_where = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_EQUAL;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("name");
-       field->name_visible = g_strdup ("Name");
-       field->description = g_strdup ("The client's name");
-       field->type = GDAEX_QE_FIELD_TYPE_TEXT;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_STARTS
-                                     | GDAEX_QE_WHERE_TYPE_CONTAINS
-                                     | GDAEX_QE_WHERE_TYPE_ENDS
-                                     | GDAEX_QE_WHERE_TYPE_ISTARTS
-                                     | GDAEX_QE_WHERE_TYPE_ICONTAINS
-                                     | GDAEX_QE_WHERE_TYPE_IENDS;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("surname");
-       field->name_visible = g_strdup ("Surname");
-       field->description = g_strdup ("The client's surname");
-       field->type = GDAEX_QE_FIELD_TYPE_TEXT;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_STRING
-                                     | GDAEX_QE_WHERE_TYPE_IS_NULL;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("brithday");
-       field->name_visible = g_strdup ("Birthday");
-       field->description = g_strdup ("The client's birthday");
-       field->type = GDAEX_QE_FIELD_TYPE_DATE;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_DATETIME
-                                     | GDAEX_QE_WHERE_TYPE_IS_NULL;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("age");
-       field->name_visible = g_strdup ("Age");
-       field->description = g_strdup ("The client's age");
-       field->alias = g_strdup ("client_age");
-       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_NUMBER;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("datetime");
-       field->name_visible = g_strdup ("DateTime");
-       field->description = g_strdup ("???");
-       field->type = GDAEX_QE_FIELD_TYPE_DATETIME;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_DATETIME
+       if (xmlfile != NULL)
+               {
+                       xmlDoc *xdoc;
+                       xmlNode *xroot;
+
+                       xdoc = xmlParseFile (xmlfile);
+                       if (xdoc != NULL)
+                               {
+                                       xroot = xmlDocGetRootElement (xdoc);
+                                       if (xroot != NULL)
+                                               {
+                                                       gdaex_query_editor_load_tables_from_xml (qe, xroot, TRUE);
+                                               }
+                               }
+               }
+       else
+               {
+                       gdaex_query_editor_add_table (qe, "clients", "Clients");
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("id");
+                       field->name_visible = g_strdup ("ID");
+                       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
+                       field->for_show = TRUE;
+                       field->always_showed = TRUE;
+                       field->for_where = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_EQUAL;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("name");
+                       field->name_visible = g_strdup ("Name");
+                       field->description = g_strdup ("The client's name");
+                       field->type = GDAEX_QE_FIELD_TYPE_TEXT;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_STARTS
+                                                     | GDAEX_QE_WHERE_TYPE_CONTAINS
+                                                     | GDAEX_QE_WHERE_TYPE_ENDS
+                                                     | GDAEX_QE_WHERE_TYPE_ISTARTS
+                                                     | GDAEX_QE_WHERE_TYPE_ICONTAINS
+                                                     | GDAEX_QE_WHERE_TYPE_IENDS;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("surname");
+                       field->name_visible = g_strdup ("Surname");
+                       field->description = g_strdup ("The client's surname");
+                       field->type = GDAEX_QE_FIELD_TYPE_TEXT;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_STRING
+                                                     | GDAEX_QE_WHERE_TYPE_IS_NULL;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("brithday");
+                       field->name_visible = g_strdup ("Birthday");
+                       field->description = g_strdup ("The client's birthday");
+                       field->type = GDAEX_QE_FIELD_TYPE_DATE;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_DATETIME
+                                                     | GDAEX_QE_WHERE_TYPE_IS_NULL;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("age");
+                       field->name_visible = g_strdup ("Age");
+                       field->description = g_strdup ("The client's age");
+                       field->alias = g_strdup ("client_age");
+                       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_NUMBER;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("datetime");
+                       field->name_visible = g_strdup ("DateTime");
+                       field->description = g_strdup ("???");
+                       field->type = GDAEX_QE_FIELD_TYPE_DATETIME;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_DATETIME
                                      | GDAEX_QE_WHERE_TYPE_IS_NULL;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("id_cities");
-       field->name_visible = g_strdup ("City");
-       field->description = g_strdup ("The client's city");
-       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->decode_table2 = g_strdup ("cities");
-       field->decode_join_type = GDAEX_QE_JOIN_TYPE_LEFT;
-       /*field->decode_fields1 = g_slist_append (field->decode_fields1, "id_cities");
-       field->decode_fields2 = g_slist_append (field->decode_fields2, "id");*/
-       field->decode_field2 = g_strdup ("id");
-       field->decode_field_to_show = g_strdup ("name");
-       field->decode_field_alias = g_strdup ("city_name");
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_EQUAL;
-       gdaex_query_editor_table_add_field (qe, "clients", *field);
-       g_free (field);
-
-       gdaex_query_editor_add_table (qe, "orders", "Orders");
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("id");
-       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
-       gdaex_query_editor_table_add_field (qe, "orders", *field);
-       g_free (field);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("id_clients");
-       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
-       gdaex_query_editor_table_add_field (qe, "orders", *field);
-       g_free (field);
-
-       gdaex_query_editor_add_relation (qe,
-                                        "clients", "orders",
-                                        GDAEX_QE_JOIN_TYPE_LEFT,
-                                        "id", "id_clients",
-                                        NULL);
-
-       field = g_new0 (GdaExQueryEditorField, 1);
-       field->name = g_strdup ("amount");
-       field->name_visible = g_strdup ("Amount");
-       field->type = GDAEX_QE_FIELD_TYPE_DOUBLE;
-       field->for_show = TRUE;
-       field->for_where = TRUE;
-       field->for_order = TRUE;
-       field->available_where_type = GDAEX_QE_WHERE_TYPE_NUMBER;
-       gdaex_query_editor_table_add_field (qe, "orders", *field);
-       g_free (field);
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("id_cities");
+                       field->name_visible = g_strdup ("City");
+                       field->description = g_strdup ("The client's city");
+                       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->decode_table2 = g_strdup ("cities");
+                       field->decode_join_type = GDAEX_QE_JOIN_TYPE_LEFT;
+                       /*field->decode_fields1 = g_slist_append (field->decode_fields1, "id_cities");
+                       field->decode_fields2 = g_slist_append (field->decode_fields2, "id");*/
+                       field->decode_field2 = g_strdup ("id");
+                       field->decode_field_to_show = g_strdup ("name");
+                       field->decode_field_alias = g_strdup ("city_name");
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_EQUAL;
+                       gdaex_query_editor_table_add_field (qe, "clients", *field);
+                       g_free (field);
+
+                       gdaex_query_editor_add_table (qe, "orders", "Orders");
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("id");
+                       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
+                       gdaex_query_editor_table_add_field (qe, "orders", *field);
+                       g_free (field);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("id_clients");
+                       field->type = GDAEX_QE_FIELD_TYPE_INTEGER;
+                       gdaex_query_editor_table_add_field (qe, "orders", *field);
+                       g_free (field);
+
+                       gdaex_query_editor_add_relation (qe,
+                                                        "clients", "orders",
+                                                        GDAEX_QE_JOIN_TYPE_LEFT,
+                                                        "id", "id_clients",
+                                                        NULL);
+
+                       field = g_new0 (GdaExQueryEditorField, 1);
+                       field->name = g_strdup ("amount");
+                       field->name_visible = g_strdup ("Amount");
+                       field->type = GDAEX_QE_FIELD_TYPE_DOUBLE;
+                       field->for_show = TRUE;
+                       field->for_where = TRUE;
+                       field->for_order = TRUE;
+                       field->available_where_type = GDAEX_QE_WHERE_TYPE_NUMBER;
+                       gdaex_query_editor_table_add_field (qe, "orders", *field);
+                       g_free (field);
+               }
 
        w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_window_set_default_size (GTK_WINDOW (w), 610, 400);
diff --git a/tests/query_editor.xml b/tests/query_editor.xml
new file mode 100644 (file)
index 0000000..68a2948
--- /dev/null
@@ -0,0 +1,107 @@
+<gdaex_query_editor>
+
+       <tables>
+               <table>
+                       <name>clients</name>
+                       <name_visible>Clients</name_visible>
+               </table>
+               <table>
+                       <name>orders</name>
+                       <name_visible>Orders</name_visible>
+               </table>
+       </tables>
+
+       <fields table="clients">
+               <field>
+                       <name>id</name>
+                       <name_visible>ID</name_visible>
+                       <type>integer</type>
+                       <always_showed>true</always_showed>
+                       <available_where_type>equal</available_where_type>
+               </field>
+               <field>
+                       <name>name</name>
+                       <name_visible>Name</name_visible>
+                       <description>The client's name</description>
+                       <type>text</type>
+                       <available_where_type>starts|contains|ends|istarts|icontains|iends</available_where_type>
+               </field>
+               <field>
+                       <name>surname</name>
+                       <name_visible>Surname</name_visible>
+                       <description>The client's surname</description>
+                       <type>text</type>
+                       <available_where_type>string|is_null</available_where_type>
+               </field>
+               <field>
+                       <name>age</name>
+                       <name_visible>Age</name_visible>
+                       <description>The client's age</description>
+                       <type>integer</type>
+                       <available_where_type>number</available_where_type>
+               </field>
+               <field>
+                       <name>datetime</name>
+                       <name_visible>DateTime</name_visible>
+                       <description>???</description>
+                       <type>datetime</type>
+                       <available_where_type>datetime|is_null</available_where_type>
+               </field>
+               <field>
+                       <name>id_cities</name>
+                       <name_visible>City</name_visible>
+                       <description>The client's city</description>
+                       <type>integer</type>
+                       <available_where_type>equal</available_where_type>
+                       <decode>
+                               <table_name>cities</table_name>
+                               <join_type>left</join_type>
+                               <field_name_to_join>id</field_name_to_join>
+                               <field_name_to_show>name</field_name_to_show>
+                               <alias>city_name</alias>
+                       </decode>
+               </field>
+               <field>
+                       <name>from_xml</name>
+                       <name_visible>From Xml</name_visible>
+                       <description>The client's age</description>
+                       <type>integer</type>
+                       <available_where_type>number</available_where_type>
+               </field>
+       </fields>
+
+       <fields table="orders">
+               <field>
+                       <name>id</name>
+                       <type>integer</type>
+                       <for_show>false</for_show>
+                       <for_where>false</for_where>
+                       <for_order>false</for_order>
+               </field>
+               <field>
+                       <name>id_clients</name>
+                       <type>integer</type>
+                       <for_show>false</for_show>
+                       <for_where>false</for_where>
+                       <for_order>false</for_order>
+               </field>
+               <field>
+                       <name>amount</name>
+                       <name_visible>Amount</name_visible>
+                       <type>double</type>
+                       <available_where_type>number</available_where_type>
+               </field>
+       </fields>
+
+       <relations>
+               <relation>
+                       <table_left>clients</table_left>
+                       <table_right>orders</table_right>
+                       <join_type>left</join_type>
+                       <fields_joined>
+                               <field_left>id</field_left>
+                               <field_right>id_clients</field_right>
+                       </fields_joined>
+               </relation>
+       </relations>
+</gdaex_query_editor>
\ No newline at end of file