[libgda] GdaBrowser: use a combo box to select related data when inserting data
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] GdaBrowser: use a combo box to select related data when inserting data
- Date: Wed, 28 Jul 2010 15:48:19 +0000 (UTC)
commit 2f1609f8a32bd4b9ff07a7230c839227dff4e6bb
Author: Vivien Malerba <malerba gnome-db org>
Date: Wed Jul 28 17:37:44 2010 +0200
GdaBrowser: use a combo box to select related data when inserting data
tools/browser/help/C/figures/table-insert-data.png | Bin 0 -> 23450 bytes
tools/browser/help/C/table-insert-data.page | 44 +++
tools/browser/help/Makefile.am | 4 +-
.../schema-browser/schema-browser-perspective.c | 4 -
tools/browser/schema-browser/table-info.c | 288 ++++++++++++++++++--
5 files changed, 315 insertions(+), 25 deletions(-)
---
diff --git a/tools/browser/help/C/figures/table-insert-data.png b/tools/browser/help/C/figures/table-insert-data.png
new file mode 100644
index 0000000..fc91ff1
Binary files /dev/null and b/tools/browser/help/C/figures/table-insert-data.png differ
diff --git a/tools/browser/help/C/table-insert-data.page b/tools/browser/help/C/table-insert-data.page
new file mode 100644
index 0000000..abd588a
--- /dev/null
+++ b/tools/browser/help/C/table-insert-data.page
@@ -0,0 +1,44 @@
+<page xmlns="http://projectmallard.org/1.0/"
+ type="topic"
+ id="table-insert-data">
+ <info>
+ <title type="sort">1</title>
+ <link type="topic" xref="schema-browser-perspective"/>
+ </info>
+ <title>Inserting data in a table</title>
+ <p>
+ From the <link xref="schema-browser-perspective">schema browser perspective</link>, when a table's
+ properties is displayed in a tab, use the <guiseq><gui>Table</gui><gui>Insert data</gui></guiseq> menu
+ to have a popup appear containing a data entry for each column of the table to insert data into.
+ </p>
+ <p>
+ A popup similar to the following will appear:
+ </p>
+ <figure>
+ <title>Popup to insert data into table</title>
+ <desc>A popup to insert data in a table</desc>
+ <media type="image" mime="image/png" src="figures/table-insert-data.png"/>
+ </figure>
+ <p>
+ The noticeable points here are:
+ </p>
+ <list>
+ <item><p>There is one data entry per column in the table, with the exception (not shown in this
+ figure) of multiple foreign key fields for which a single drop down choice widget is shown;</p></item>
+ <item><p>Each data entry has a small button on the right giving information about the corresponding
+ data entry, and allowing you to reset the data entry's value to the value it had before any
+ modification done, to set it to NULL or to set it to a default value. The button's color has the
+ following signification:</p>
+ <list>
+ <item><p>Red: means the current value in the data entry is invalid</p></item>
+ <item><p>Blue: means the current value in the data entry is unused and the column's default value
+ will be used instead (usefull for example for auto incremented columns)</p></item>
+ <item><p>Green: means the current value in the data entry is NULL</p></item>
+ <item><p>Same color as the rest of the UI: means none of the above situations apply</p></item>
+ </list>
+ </item>
+ <item><p>Columns which are foreign keys to other tables are presented as a drop down combo box where
+ you can only choose among the values in the referenced table (hint: to have that values list
+ refresned you should close the popup dialog and reopen it);</p></item>
+ </list>
+</page>
diff --git a/tools/browser/help/Makefile.am b/tools/browser/help/Makefile.am
index 3f307d8..6ba449d 100644
--- a/tools/browser/help/Makefile.am
+++ b/tools/browser/help/Makefile.am
@@ -13,7 +13,8 @@ DOC_FIGURES = \
figures/query-exec-persp.png \
figures/trans-begin.png \
figures/trans-commit.png \
- figures/trans-rollback.png
+ figures/trans-rollback.png \
+ figures/table-insert-data.png
DOC_PAGES = \
data-manager-perspective.page \
@@ -26,6 +27,7 @@ DOC_PAGES = \
query-execution-perspective.page \
schema-browser-perspective.page \
transactions.page \
+ table-insert-data.page \
variables.page
DOC_LINGUAS = de es
diff --git a/tools/browser/schema-browser/schema-browser-perspective.c b/tools/browser/schema-browser/schema-browser-perspective.c
index f9570b8..d2c59ba 100644
--- a/tools/browser/schema-browser/schema-browser-perspective.c
+++ b/tools/browser/schema-browser/schema-browser-perspective.c
@@ -307,10 +307,6 @@ static const gchar *ui_actions_info =
" </menu>"
" </placeholder>"
" </menubar>"
- " <toolbar name='ToolBar'>"
- " <separator/>"
- " <toolitem action='NewDiagram'/>"
- " </toolbar>"
"</ui>";
static GtkActionGroup *
diff --git a/tools/browser/schema-browser/table-info.c b/tools/browser/schema-browser/table-info.c
index ddf6693..1a9416f 100644
--- a/tools/browser/schema-browser/table-info.c
+++ b/tools/browser/schema-browser/table-info.c
@@ -39,6 +39,7 @@
#include <libgda-ui/gdaui-enums.h>
#include <libgda-ui/gdaui-basic-form.h>
#include "../common/popup-container.h"
+#include <libgda/gda-data-model-extra.h>
struct _TableInfoPrivate {
BrowserConnection *bcnc;
@@ -53,6 +54,8 @@ struct _TableInfoPrivate {
GtkWidget *pages; /* notebook to store individual pages */
GtkWidget *insert_popup;
+ GHashTable *insert_columns_hash; /* key = column index as a pointer, value = GdaHolder in the
+ * params used in the INSERT statement */
};
static void table_info_class_init (TableInfoClass *klass);
@@ -127,6 +130,8 @@ table_info_dispose (GObject *object)
/* free memory */
if (tinfo->priv) {
+ if (tinfo->priv->insert_columns_hash)
+ g_hash_table_destroy (tinfo->priv->insert_columns_hash);
if (tinfo->priv->insert_popup)
gtk_widget_destroy (tinfo->priv->insert_popup);
g_free (tinfo->priv->schema);
@@ -266,6 +271,15 @@ meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableInfo *tin
GdaMetaDbObject *dbo;
GValue *schema_v = NULL, *name_v;
+ if (tinfo->priv->insert_columns_hash) {
+ g_hash_table_destroy (tinfo->priv->insert_columns_hash);
+ tinfo->priv->insert_columns_hash = NULL;
+ }
+ if (tinfo->priv->insert_popup) {
+ gtk_widget_destroy (tinfo->priv->insert_popup);
+ tinfo->priv->insert_popup = NULL;
+ }
+
g_value_set_string ((schema_v = gda_value_new (G_TYPE_STRING)), tinfo->priv->schema);
g_value_set_string ((name_v = gda_value_new (G_TYPE_STRING)), tinfo->priv->table_name);
dbo = gda_meta_struct_get_db_object (mstruct, NULL, schema_v, name_v);
@@ -540,8 +554,86 @@ insert_response_cb (GtkWidget *dialog, gint response_id, TableInfo *tinfo)
lerror && lerror->message ? lerror->message : _("No detail"));
g_clear_error (&lerror);
}
+ gtk_widget_hide (dialog);
+ }
+#ifdef HAVE_GDU
+ else if (response_id == GTK_RESPONSE_HELP) {
+ browser_show_help ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tinfo),
+ "table-insert-data");
+ }
+#endif
+ else
+ gtk_widget_hide (dialog);
+}
+
+typedef struct {
+ gint cols_nb;
+ gint *fk_cols_array;
+ GdaSet *insert_params;
+ GHashTable *chash;
+ GdaStatement *stmt;
+ GdaDataModel *model;
+ gboolean model_rerunning;
+} FKBindData;
+
+static void
+fk_bind_select_executed_cb (BrowserConnection *bcnc,
+ guint exec_id,
+ GObject *out_result,
+ GdaSet *out_last_inserted_row, GError *error,
+ FKBindData *fkdata)
+{
+ gint i;
+ GdaDataModel *model;
+ if (! out_result)
+ return;
+
+ if (fkdata->model)
+ g_object_unref (fkdata->model);
+
+ model = GDA_DATA_MODEL (out_result);
+ for (i = 0; i < fkdata->cols_nb; i++) {
+ GdaHolder *h;
+ GdaSetSource *source;
+ h = g_hash_table_lookup (fkdata->chash,
+ GINT_TO_POINTER (fkdata->fk_cols_array [i] - 1));
+ source = gda_set_get_source (fkdata->insert_params, h);
+ if (source && gda_holder_get_source_model (h, NULL)) {
+ gda_set_replace_source_model (fkdata->insert_params, source,
+ model);
+ /* break now as gda_set_replace_source_model() does the job of replacing
+ * the data model for all the holders which share the same data model */
+ break;
+ }
+ else {
+#ifdef GDA_DEBUG_NO
+ if (gda_holder_set_source_model (h, model, i, NULL))
+ g_print ("Bound holder [%s] to column %d for model %p\n", gda_holder_get_id (h), i, model);
+ else
+ g_print ("Could not bind holder [%s] to column %d\n", gda_holder_get_id (h), i);
+#else
+ gda_holder_set_source_model (h, model, i, NULL);
+#endif
+ }
+ }
+ fkdata->model = g_object_ref (out_result);
+ fkdata->model_rerunning = FALSE;
+}
+
+static void
+fkdata_list_free (GSList *fkdata_list)
+{
+ GSList *list;
+ for (list = fkdata_list; list; list = list->next) {
+ FKBindData *fkdata = (FKBindData*) list->data;
+ g_free (fkdata->fk_cols_array);
+ g_object_unref (fkdata->insert_params);
+ g_object_unref (fkdata->stmt);
+ if (fkdata->model)
+ g_object_unref (fkdata->model);
+ g_free (fkdata);
}
- gtk_widget_hide (dialog);
+ g_slist_free (fkdata_list);
}
static void
@@ -553,6 +645,21 @@ action_insert_cb (GtkAction *action, TableInfo *tinfo)
if (tinfo->priv->insert_popup) {
gtk_widget_show (tinfo->priv->insert_popup);
+ GSList *fkdata_list;
+
+ for (fkdata_list = g_object_get_data (G_OBJECT (tinfo->priv->insert_popup), "fkdata_list");
+ fkdata_list; fkdata_list = fkdata_list->next) {
+ FKBindData *fkdata = (FKBindData *) fkdata_list->data;
+ if (fkdata->model && !fkdata->model_rerunning) {
+ if (browser_connection_execute_statement_cb (tinfo->priv->bcnc,
+ fkdata->stmt, NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ FALSE,
+ (BrowserConnectionExecuteCallback) fk_bind_select_executed_cb,
+ fkdata, NULL) != 0)
+ fkdata->model_rerunning = TRUE;
+ }
+ }
return;
}
@@ -600,10 +707,12 @@ action_insert_cb (GtkAction *action, TableInfo *tinfo)
GdaStatement *stmt;
stmt = gda_sql_builder_get_statement (b, NULL);
g_object_unref (b);
+#ifdef GDA_DEBUG_NO
gchar *sql;
sql = gda_statement_to_sql (stmt, NULL, NULL);
g_print ("[%s]\n", sql);
g_free (sql);
+#endif
/* handle user preferences */
GdaSet *params;
@@ -617,47 +726,185 @@ action_insert_cb (GtkAction *action, TableInfo *tinfo)
g_object_unref (stmt);
return;
}
- for (list = mtable->columns; list; list = list->next) {
+ GSList *fkdata_list = NULL;
+ gint nthcol;
+ if (mtable->fk_list)
+ tinfo->priv->insert_columns_hash = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
+
+ for (nthcol = 0, list = mtable->columns; list; nthcol++, list = list->next) {
GdaMetaTableColumn *col = (GdaMetaTableColumn*) list->data;
gchar *plugin;
+ const GValue *autoinc;
+ GdaHolder *holder;
+
plugin = browser_connection_get_table_column_attribute (tinfo->priv->bcnc,
mtable, col,
BROWSER_CONNECTION_COLUMN_PLUGIN,
NULL);
- if (!plugin && !col->default_value)
+ holder = gda_set_get_holder (params, col->column_name);
+ if (!holder)
continue;
+
+ autoinc = gda_meta_table_column_get_attribute (col, GDA_ATTRIBUTE_AUTO_INCREMENT);
+ if (tinfo->priv->insert_columns_hash)
+ g_hash_table_insert (tinfo->priv->insert_columns_hash, GINT_TO_POINTER (nthcol), g_object_ref (holder));
- GdaHolder *holder;
- holder = gda_set_get_holder (params, col->column_name);
- if (holder) {
- if (plugin) {
- GValue *value;
- value = gda_value_new_from_string (plugin, G_TYPE_STRING);
- gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
- gda_value_free (value);
+ if (!plugin && !col->default_value && !autoinc)
+ continue;
+ if (plugin) {
+ GValue *value;
+ value = gda_value_new_from_string (plugin, G_TYPE_STRING);
+ gda_holder_set_attribute_static (holder, GDAUI_ATTRIBUTE_PLUGIN, value);
+ gda_value_free (value);
+ }
+
+ if (col->default_value) {
+ GValue *dv;
+ //g_value_set_string ((dv = gda_value_new (G_TYPE_STRING)), col->default_value);
+ dv = gda_value_new_null ();
+ gda_holder_set_default_value (holder, dv);
+ gda_value_free (dv);
+ gda_holder_set_value_to_default (holder);
+
+ gchar *tmp;
+ tmp = g_strdup_printf (_("Default value: '%s'"), col->default_value);
+ g_object_set (holder, "description", tmp, NULL);
+ g_free (tmp);
+ }
+ else if (autoinc) {
+ GValue *dv;
+ g_value_set_string ((dv = gda_value_new (G_TYPE_STRING)), "");
+ gda_holder_set_default_value (holder, dv);
+ gda_value_free (dv);
+ gda_holder_set_value_to_default (holder);
+ g_object_set (holder, "description", _("Default value: auto incremented value"), NULL);
+ }
+
+ g_free (plugin);
+ }
+
+ /* analyse FK list to propose values to choose from */
+ for (list = mtable->fk_list; list; list = list->next) {
+ GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+ if (fk->depend_on == dbo) /* don't link to itself */
+ continue;
+ else if (fk->depend_on->obj_type != GDA_META_DB_TABLE)
+ continue;
+ GdaMetaDbObject *rdbo = (GdaMetaDbObject*) fk->depend_on;
+ GdaDataModel *cmodel;
+ GValue *schema_v, *name_v, *catalog_v;
+ GError *lerror = NULL;
+
+ g_value_set_string ((catalog_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_catalog);
+ g_value_set_string ((schema_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_schema);
+ g_value_set_string ((name_v = gda_value_new (G_TYPE_STRING)), rdbo->obj_name);
+ dbo = gda_meta_struct_get_db_object (mstruct, NULL, schema_v, name_v);
+
+ cmodel = gda_meta_store_extract (browser_connection_get_meta_store (tinfo->priv->bcnc),
+ "SELECT tc.constraint_name, k.column_name FROM _key_column_usage k INNER JOIN _table_constraints tc ON (k.table_catalog=tc.table_catalog AND k.table_schema=tc.table_schema AND k.table_name=tc.table_name AND k.constraint_name=tc.constraint_name) WHERE tc.constraint_type='UNIQUE' AND k.table_catalog = ##catalog::string AND k.table_schema = ##schema::string AND k.table_name = ##tname::string ORDER by k.ordinal_position", &lerror,
+ "catalog", catalog_v,
+ "schema", schema_v,
+ "tname", name_v, NULL);
+
+ gda_value_free (catalog_v);
+ gda_value_free (schema_v);
+ gda_value_free (name_v);
+
+ GdaSqlBuilder *b = NULL;
+ if (cmodel) {
+ gint nrows, i;
+ nrows = gda_data_model_get_n_rows (cmodel);
+ b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+ gda_sql_builder_select_add_target_id (b,
+ gda_sql_builder_add_id (b, rdbo->obj_short_name),
+ NULL);
+ /* add REF PK fields */
+ for (i = 0; i < fk->cols_nb; i++) {
+ gda_sql_builder_select_add_field (b, fk->ref_pk_names_array [i], NULL, NULL);
}
- if (col->default_value) {
- GValue *dv;
- g_value_set_string ((dv = gda_value_new (G_TYPE_STRING)), col->default_value);
- gda_holder_set_default_value (holder, dv);
- gda_value_free (dv);
- gda_holder_set_value_to_default (holder);
+ /* add UNIQUE fields */
+ for (i = 0; i < nrows; i++) {
+ const GValue *cvalue;
+ cvalue = gda_data_model_get_value_at (cmodel, 1, i, NULL);
+ if (!cvalue || (G_VALUE_TYPE (cvalue) != G_TYPE_STRING))
+ break;
+ gda_sql_builder_select_add_field (b, g_value_get_string (cvalue), NULL, NULL);
+ gda_sql_builder_select_order_by (b,
+ gda_sql_builder_add_id (b, g_value_get_string (cvalue)),
+ TRUE, NULL);
}
+ if (i < nrows) {
+ /* error */
+ g_object_unref (b);
+ b = NULL;
+ }
+ /*gda_data_model_dump (cmodel, NULL);*/
+ g_object_unref (cmodel);
+ }
+ else {
+ g_warning ("Can't get list of unique constraints for table '%s': %s",
+ rdbo->obj_short_name,
+ lerror && lerror->message ? lerror->message : _("No detail"));
+ g_clear_error (&lerror);
}
- g_free (plugin);
+ if (b) {
+ GdaStatement *stmt;
+ stmt = gda_sql_builder_get_statement (b, NULL);
+ if (stmt) {
+#ifdef GDA_DEBUG_NO
+ gchar *sql;
+ sql = gda_statement_to_sql (stmt, NULL, NULL);
+ g_print ("UNIQUE SELECT [%s]\n", sql);
+ g_free (sql);
+#endif
+
+ FKBindData *fkdata;
+ guint eid;
+ fkdata = g_new0 (FKBindData, 1);
+ fkdata->cols_nb = fk->cols_nb;
+ fkdata->fk_cols_array = g_new (gint, fk->cols_nb);
+ memcpy (fkdata->fk_cols_array, fk->fk_cols_array, sizeof (gint) * fk->cols_nb);
+ fkdata->chash = tinfo->priv->insert_columns_hash;
+ fkdata->stmt = stmt;
+ eid = browser_connection_execute_statement_cb (tinfo->priv->bcnc, stmt, NULL,
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ FALSE,
+ (BrowserConnectionExecuteCallback) fk_bind_select_executed_cb,
+ fkdata, NULL);
+ if (! eid) {
+ g_free (fkdata->fk_cols_array);
+ g_object_unref (fkdata->stmt);
+ g_free (fkdata);
+ }
+ fkdata->insert_params = g_object_ref (params);
+
+ /* attach the kfdata to @popup to be able to re-run the SELECT
+ * everytime the window is shown */
+ fkdata_list = g_slist_prepend (fkdata_list, fkdata);
+ }
+
+ g_object_unref (b);
+ }
}
/* create popup */
GtkWidget *popup;
popup = gtk_dialog_new_with_buttons (_("Values to insert into table"), GTK_WINDOW (bwin),
- 0, GTK_STOCK_EXECUTE, GTK_RESPONSE_ACCEPT,
+ 0,
+#ifdef HAVE_GDU
+ GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+#endif
+ GTK_STOCK_EXECUTE, GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
NULL);
tinfo->priv->insert_popup = popup;
g_object_set_data_full (G_OBJECT (popup), "stmt", stmt, g_object_unref);
g_object_set_data_full (G_OBJECT (popup), "params", params, g_object_unref);
+ if (fkdata_list)
+ g_object_set_data_full (G_OBJECT (popup), "fkdata_list",
+ fkdata_list, (GDestroyNotify) fkdata_list_free);
g_signal_connect (popup, "close",
G_CALLBACK (gtk_widget_hide), NULL);
@@ -696,11 +943,12 @@ action_insert_cb (GtkAction *action, TableInfo *tinfo)
gtk_widget_show_all (popup);
}
+
static GtkActionEntry ui_actions[] = {
{ "Table", NULL, "_Table", NULL, "Table", NULL },
{ "AddToFav", STOCK_ADD_BOOKMARK, N_("Add to _Favorites"), NULL, N_("Add table to favorites"),
G_CALLBACK (action_add_to_fav_cb)},
- { "ViewContents", GTK_STOCK_EDIT, N_("_Contents"), NULL, N_("View contents"),
+ { "ViewContents", GTK_STOCK_EDIT, N_("_Contents"), NULL, N_("View table's contents"),
G_CALLBACK (action_view_contents_cb)},
{ "InsertData", GTK_STOCK_ADD, N_("_Insert data"), NULL, N_("Insert data into table"),
G_CALLBACK (action_insert_cb)},
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]