[libgda] GdaBrowser: handle multiple foreign keys between 2 tables in data manager perspective
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] GdaBrowser: handle multiple foreign keys between 2 tables in data manager perspective
- Date: Tue, 7 Sep 2010 19:23:39 +0000 (UTC)
commit 3a19be53ce58145c01f36271ffbe5201d36d23f5
Author: Vivien Malerba <malerba gnome-db org>
Date: Tue Sep 7 20:38:23 2010 +0200
GdaBrowser: handle multiple foreign keys between 2 tables in data manager perspective
tools/browser/data-manager/data-console.c | 14 +-
tools/browser/data-manager/data-source.c | 136 ++++++++++++++++++--
tools/browser/data-manager/data-source.h | 6 +-
tools/browser/data-manager/data-widget.c | 5 +
tools/browser/help/C/data-manager-perspective.page | 10 +-
tools/browser/help/C/data-manager-xml-syntax.page | 32 ++++-
6 files changed, 174 insertions(+), 29 deletions(-)
---
diff --git a/tools/browser/data-manager/data-console.c b/tools/browser/data-manager/data-console.c
index f45a6d4..4347fe2 100644
--- a/tools/browser/data-manager/data-console.c
+++ b/tools/browser/data-manager/data-console.c
@@ -42,8 +42,8 @@
#define MAIN_PAGE_EDITORS 0
#define MAIN_PAGE_DATA 1
-#define EDITOR_PAGE_XML 0
-#define EDITOR_PAGE_UI 1
+#define EDITOR_PAGE_XML 1
+#define EDITOR_PAGE_UI 0
typedef enum {
LAYOUT_HORIZ,
@@ -365,16 +365,16 @@ data_console_new (BrowserConnection *bcnc)
gtk_notebook_set_show_border (GTK_NOTEBOOK (nb), FALSE);
dconsole->priv->editors_notebook = nb;
- dconsole->priv->xml_sped = xml_spec_editor_new (dconsole->priv->mgr);
- gtk_widget_show (dconsole->priv->xml_sped);
- gtk_notebook_append_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
- dconsole->priv->xml_sped, NULL);
-
dconsole->priv->ui_sped = ui_spec_editor_new (dconsole->priv->mgr);
gtk_widget_show (dconsole->priv->ui_sped);
gtk_notebook_append_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
dconsole->priv->ui_sped, NULL);
+ dconsole->priv->xml_sped = xml_spec_editor_new (dconsole->priv->mgr);
+ gtk_widget_show (dconsole->priv->xml_sped);
+ gtk_notebook_append_page (GTK_NOTEBOOK (dconsole->priv->editors_notebook),
+ dconsole->priv->xml_sped, NULL);
+
/* buttons */
GtkWidget *bbox, *button;
bbox = gtk_vbutton_box_new ();
diff --git a/tools/browser/data-manager/data-source.c b/tools/browser/data-manager/data-source.c
index 1723777..d291bc9 100644
--- a/tools/browser/data-manager/data-source.c
+++ b/tools/browser/data-manager/data-source.c
@@ -30,6 +30,7 @@
#include "data-source.h"
#define DEFAULT_DATA_SOURCE_NAME "DataSource"
+#define DEPENDENCY_SEPARATOR "<|>"
/* signals */
enum {
@@ -60,6 +61,7 @@ static GObjectClass *parent_class = NULL;
typedef struct {
gchar *dep_id;
gchar *dep_table;
+ gchar *dep_columns; /* column names, separated by a |, sorted */
} Dependency;
static void
@@ -67,18 +69,61 @@ dependency_free (Dependency *dep)
{
g_free (dep->dep_id);
g_free (dep->dep_table);
+ g_free (dep->dep_columns);
g_free (dep);
}
+
+/*
+ * converts an array of column names into a single string in the format:
+ * <colname>[SEP<colname>...] where SEP is DEPENDENCY_SEPARATOR
+ *
+ * Returns: a new string, never %NULL
+ */
+static gchar *
+column_names_to_string (gint size, const gchar **colnames)
+{
+ if (!colnames)
+ return g_strdup ("");
+
+ GString *string = NULL;
+ GArray *colsarray;
+ gint i;
+ colsarray = g_array_new (FALSE, FALSE, sizeof (gchar*));
+ for (i = 0; i < size; i++)
+ g_array_append_val (colsarray, colnames[i]);
+ g_array_sort (colsarray, (GCompareFunc) g_strcmp0);
+ for (i = 0; i < size; i++) {
+ gchar *tmp;
+ tmp = g_array_index (colsarray, gchar *, i);
+ if (!string)
+ string = g_string_new (tmp);
+ else {
+ g_string_append (string, DEPENDENCY_SEPARATOR);
+ g_string_append (string, tmp);
+ }
+ }
+ g_array_free (colsarray, TRUE);
+ return g_string_free (string, FALSE);
+}
+
static Dependency *
-dependency_find (GSList *dep_list, const gchar *id, const gchar *table)
+dependency_find (GSList *dep_list, const gchar *id, const gchar *table, gint size, const gchar **colnames)
{
GSList *list;
+ gchar *colsstring = NULL;
for (list = dep_list; list; list = list->next) {
Dependency *dep = (Dependency*) list->data;
if (strcmp (dep->dep_id, id) || strcmp (dep->dep_table, table))
continue;
- return dep;
+
+ if (!colsstring)
+ colsstring = column_names_to_string (size, colnames);
+ if (!strcmp (colsstring, dep->dep_columns)) {
+ g_free (colsstring);
+ return dep;
+ }
}
+ g_free (colsstring);
return NULL;
}
@@ -446,17 +491,42 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
for (subnode = node->children; subnode; subnode = subnode->next) {
if (!strcmp ((gchar*)subnode->name, "depend")) {
xmlChar *fk_table, *id;
+ GArray *cols_array = NULL;
+ xmlNodePtr chnode;
fk_table = xmlGetProp (subnode, BAD_CAST "foreign_key_table");
id = xmlGetProp (subnode, BAD_CAST "id");
+ for (chnode = subnode->children; chnode; chnode = chnode->next) {
+ xmlChar *colname;
+ if (strcmp ((gchar*)chnode->name, "column"))
+ continue;
+ colname = xmlNodeGetContent (chnode);
+ if (colname) {
+ if (! cols_array)
+ cols_array = g_array_new (FALSE, FALSE, sizeof (gchar*));
+ g_array_append_val (cols_array, colname);
+ }
+ }
if (fk_table &&
- ! data_source_add_dependendency (source, (gchar *) fk_table, (gchar*) id, error))
+ ! data_source_add_dependency (source, (gchar *) fk_table, (gchar*) id,
+ cols_array ? cols_array->len : 0,
+ (const gchar **) (cols_array ? cols_array->data : NULL),
+ error))
retval = FALSE;
if (fk_table)
xmlFree (fk_table);
if (id)
xmlFree (id);
+ if (cols_array) {
+ gint i;
+ for (i = 0; i < cols_array->len; i++) {
+ xmlChar *colname;
+ colname = g_array_index (cols_array, xmlChar*, i);
+ xmlFree (colname);
+ }
+ g_array_free (cols_array, TRUE);
+ }
break;
}
}
@@ -465,23 +535,27 @@ init_from_table_node (DataSource *source, xmlNodePtr node, GError **error)
}
/**
- * data_source_add_dependendency
+ * data_source_add_dependency
* @source: a #DataSource
* @table: the name of the referenced table
* @id: the ID of the referenced data source, or %NULL if its ID is the same as the table name
+ * @col_name_size: the size of @col_names
+ * @col_names: names of the FK columns involved in the foreign key, or %NULL
+ * @error: a place to store errors, or %NULL
*
* Adds a dependency on the @table table, only for DATA_SOURCE_TABLE sources
*/
gboolean
-data_source_add_dependendency (DataSource *source, const gchar *table,
- const char *id, GError **error)
+data_source_add_dependency (DataSource *source, const gchar *table,
+ const char *id, gint col_name_size, const gchar **col_names,
+ GError **error)
{
g_return_val_if_fail (IS_DATA_SOURCE (source), FALSE);
g_return_val_if_fail (table && *table, FALSE);
g_return_val_if_fail (source->priv->source_type == DATA_SOURCE_TABLE, FALSE);
g_return_val_if_fail (source->priv->builder, FALSE);
- if (dependency_find (source->priv->dependencies, id ? id : table, table))
+ if (dependency_find (source->priv->dependencies, id ? id : table, table, col_name_size, col_names))
return TRUE;
GdaMetaTable *mtable, *mlinked;
@@ -500,7 +574,22 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
for (list = mtable->fk_list; list; list = list->next) {
if (GDA_META_TABLE_FOREIGN_KEY (list->data)->depend_on == GDA_META_DB_OBJECT (mlinked)) {
fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
- break;
+ if (col_names && (col_name_size == fk->cols_nb)) {
+ gint i;
+ for (i = 0; i < col_name_size; i++) {
+ gint j;
+ for (j = 0; j < col_name_size; j++) {
+ if (!strcmp (col_names [i], fk->fk_names_array [j]))
+ break;
+ }
+ if (j == col_name_size) {
+ fk = NULL; /* not this FK */
+ break;
+ }
+ }
+ }
+ if (fk)
+ break;
}
}
if (!fk) {
@@ -508,7 +597,23 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
if (GDA_META_TABLE_FOREIGN_KEY (list->data)->depend_on == GDA_META_DB_OBJECT (mtable)) {
fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
reverse = TRUE;
- break;
+ if (col_names && (col_name_size == fk->cols_nb)) {
+ gint i;
+ for (i = 0; i < col_name_size; i++) {
+ gint j;
+ for (j = 0; j < col_name_size; j++) {
+ if (!strcmp (col_names [i],
+ fk->fk_names_array [j]))
+ break;
+ }
+ if (j == col_name_size) {
+ fk = NULL; /* not this FK */
+ break;
+ }
+ }
+ }
+ if (fk)
+ break;
}
}
}
@@ -598,6 +703,7 @@ data_source_add_dependendency (DataSource *source, const gchar *table,
Dependency *dep = g_new0 (Dependency, 1);
dep->dep_id = g_strdup (id ? id : table);
dep->dep_table = g_strdup (table);
+ dep->dep_columns = column_names_to_string (col_name_size, col_names);
source->priv->dependencies = g_slist_append (source->priv->dependencies, dep);
compute_stmt_and_params (source);
@@ -631,9 +737,15 @@ data_source_to_xml_node (DataSource *source)
depnode = xmlNewChild (node, NULL, BAD_CAST "depend", NULL);
xmlSetProp (depnode, BAD_CAST "foreign_key_table",
BAD_CAST (dep->dep_table));
- if (strcmp (dep->dep_id, dep->dep_table))
- xmlSetProp (depnode, BAD_CAST "id",
- BAD_CAST (dep->dep_id));
+ xmlSetProp (depnode, BAD_CAST "id",
+ BAD_CAST (dep->dep_id));
+
+ gchar **array;
+ gint i;
+ array = g_strsplit (dep->dep_columns, DEPENDENCY_SEPARATOR, 0);
+ for (i = 0; array[i]; i++)
+ xmlNewChild (depnode, NULL, BAD_CAST "column", BAD_CAST (array[i]));
+ g_strfreev (array);
}
}
break;
diff --git a/tools/browser/data-manager/data-source.h b/tools/browser/data-manager/data-source.h
index 5968931..e596b99 100644
--- a/tools/browser/data-manager/data-source.h
+++ b/tools/browser/data-manager/data-source.h
@@ -71,8 +71,10 @@ const gchar *data_source_get_title (DataSource *source);
/* Data source as table API */
gboolean data_source_set_table (DataSource *source, const gchar *table, GError **error);
const gchar *data_source_get_table (DataSource *source);
-gboolean data_source_add_dependendency (DataSource *source, const gchar *table,
- const char *id, GError **error);
+gboolean data_source_add_dependency (DataSource *source, const gchar *table,
+ const char *id,
+ gint col_name_size, const gchar **col_names,
+ GError **error);
/* Data source as SQL query API */
void data_source_set_query (DataSource *source, const gchar *sql, GError **warning);
diff --git a/tools/browser/data-manager/data-widget.c b/tools/browser/data-manager/data-widget.c
index e1f237c..6185c02 100644
--- a/tools/browser/data-manager/data-widget.c
+++ b/tools/browser/data-manager/data-widget.c
@@ -547,6 +547,11 @@ compute_fk_dependency (GdaMetaTableForeignKey *fkey, GSList *selfields, gboolean
BAD_CAST GDA_META_DB_OBJECT (table)->obj_short_name);
xmlSetProp (snode, BAD_CAST "id", BAD_CAST data_source_get_id (part->source));
+
+ gint i;
+ for (i = 0; i < fkey->cols_nb; i++)
+ xmlNewChild (snode, NULL, BAD_CAST "column", BAD_CAST (fkey->fk_names_array[i]));
+
*out_sourcespec = node;
return g_string_free (string, FALSE);
}
diff --git a/tools/browser/help/C/data-manager-perspective.page b/tools/browser/help/C/data-manager-perspective.page
index c489f0b..e1e86cd 100644
--- a/tools/browser/help/C/data-manager-perspective.page
+++ b/tools/browser/help/C/data-manager-perspective.page
@@ -64,10 +64,14 @@
The commands available through the command buttons are:
</p>
<list>
- <item><p><gui>Clear</gui>: clears the XML editor</p></item>
+ <item><p><gui>Reset</gui>: resets the XML editor to a default XML template, only available when the XML
+ view is currently displayed</p></item>
+ <item><p><gui>Add data source</gui>: displays a popup menu with an entry per table to quickly add
+ the whole contents of a table as a data source, only available when the UI
+ editor is currently displayed</p></item>
<item><p><gui>Variables</gui>: shows/hide the <link xref="variables-syntax">variables</link> panel where you can give values to the variables
- present in the SQL code of any data source's definition. The panel is automatically shown when a variable is detected in
- the SQL code.</p></item>
+ present in the SQL code of any data source's definition. The panel is automatically shown when a variable is detected in
+ the SQL code.</p></item>
<item><p><gui>Execute</gui>: executes the defined data sources</p></item>
<item><p><gui>View XML</gui>: toggles between the XML editor and the UI editor</p></item>
<item><p><gui>Help</gui>: shows some help</p></item>
diff --git a/tools/browser/help/C/data-manager-xml-syntax.page b/tools/browser/help/C/data-manager-xml-syntax.page
index ea160c7..931c815 100644
--- a/tools/browser/help/C/data-manager-xml-syntax.page
+++ b/tools/browser/help/C/data-manager-xml-syntax.page
@@ -92,18 +92,38 @@
</p>
<list>
<item><p>requires the "name" attribute which represents the table name.</p></item>
- <item><p>can contain a <code><depend></code> node which defines a dependency on another
+ <item><p>can have a "id" attribute corresponding to the data source's ID. If not present,
+ and ID will be assigned automatically.</p></item>
+ <item><p>can contain a <code><depend></code> tag which defines a dependency on another
data source with
the "foreign_key_table" attribute defining the name of the table to which there are foreign keys
used to determine the dependency, and the "id" attribute can specify a data source ID if different than
the aforementioned table</p></item>
</list>
+ <p>
+ The <code><depend></code> tag, which, for a data source from a table, defines a dependency to
+ another data source from a table:
+ </p>
+ <list>
+ <item><p>requires the "foreign_key_table" attribute defining the name of the table to which there are
+ foreign keys used to determine the dependency</p></item>
+ <item><p>can have a "id" attribute corresponding to the ID of the referenced data source. If not
+ provided, then the dependency may fail if there is no data source which ID is the"foreign_key_table"
+ attribute.</p></item>
+ <item><p>can contain one or more <code><column></code> tag which contents define the columns
+ to identify the foreign key to use; this is necessary if there are multiple foreign keys,
+ and can be omitted if there is only one possible koreign key. The listed columns are the one
+ from the table where the foreign key exists.</p></item>
+ </list>
+
<code><![CDATA[
<data>
- <table name="customers"/>
+ <table id="the_cust" name="customers"/>
<table name="orders">
- <depend foreign_key_table="customers"/>
+ <depend id="the_cust" foreign_key_table="customers">
+ <column>customer_id</column>
+ </depend>
</table>
</data>]]>
</code>
@@ -114,8 +134,10 @@
<item><p>the <code>customers</code> data source which selects all the contents of the
<em>customers</em> table.</p></item>
<item><p>the <code>orders</code> data source which selects among contents of the <code>orders</code>
- table, the rows which correspond to a row in the <code>customers</code> table (assuming here
- that the <code>orders</code> references the <code>customers</code> table in some way).
+ table, the rows which correspond to a row in the <code>customers</code> table using the foreign key
+ on table orders which involves the "orders.customer_id" column and the primary key of the
+ customers table. The "id" attribute of the <code><depend></code> tag is necessary here to
+ identify referenced the data source.
</p></item>
</list>
<p>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]