[libgda] Added the notion of declared foreign key in the database's meta data



commit f715acf4807c1f48e9f04919be930477fb664a0d
Author: Vivien Malerba <malerba gnome-db org>
Date:   Wed Feb 9 18:00:00 2011 +0100

    Added the notion of declared foreign key in the database's meta data

 doc/C/i_s_doc.xml                                  |    5 +
 doc/C/libgda-4.0-docs.sgml                         |   24 +
 doc/C/libgda-sections.txt                          |    4 +-
 doc/C/tmpl/gda-meta-store.sgml                     |   39 ++
 doc/C/tmpl/gda-meta-struct.sgml                    |    3 +-
 libgda/gda-meta-store.c                            |  419 ++++++++++++-
 libgda/gda-meta-store.h                            |   17 +-
 libgda/gda-meta-struct.c                           |  279 ++++++++-
 libgda/gda-meta-struct.h                           |   25 +-
 libgda/information_schema.xml                      |   35 +
 libgda/libgda.symbols                              |    2 +
 po/POTFILES.in                                     |    1 +
 tests/meta-store/common.c                          |    3 +-
 tools/Makefile.am                                  |    2 +
 tools/browser/Makefile.am                          |   12 +-
 tools/browser/browser-connection.c                 |   15 +
 tools/browser/browser-connection.h                 |    1 +
 tools/browser/canvas-example.c                     |   50 --
 tools/browser/canvas/browser-canvas-db-relations.c |   56 ++-
 tools/browser/canvas/browser-canvas-fkey.c         |   84 ++-
 tools/browser/canvas/browser-canvas.c              |    3 -
 tools/browser/common/Makefile.am                   |    4 +-
 tools/browser/common/fk-declare.c                  |  652 ++++++++++++++++++++
 tools/browser/common/fk-declare.h                  |   62 ++
 tools/browser/doc/gda-browser-sections.txt         |    1 +
 tools/browser/doc/tmpl/browser-connection.sgml     |    8 +
 tools/browser/help/C/declaredfk.page               |   82 +++
 tools/browser/help/C/diagram.page                  |    3 +-
 tools/browser/help/C/figures/declaredfk-dialog.png |  Bin 0 -> 72957 bytes
 tools/browser/help/C/figures/declaredfk.png        |  Bin 0 -> 23019 bytes
 tools/browser/help/C/figures/mainwin.png           |  Bin 47008 -> 67353 bytes
 .../help/C/figures/schema-browser-persp.png        |  Bin 0 -> 114471 bytes
 .../browser/help/C/schema-browser-perspective.page |    7 +
 tools/browser/help/Makefile.am                     |    6 +-
 tools/browser/schema-browser/table-columns.c       |  189 ++++--
 tools/browser/schema-browser/table-info.c          |   49 ++
 tools/browser/support.c                            |    4 +-
 tools/command-exec.c                               |  175 ++----
 tools/gda-sql.1.in                                 |    5 +
 tools/gda-sql.c                                    |  386 ++++++++++++
 tools/information-schema-types.c                   |   46 +-
 tools/tools-utils.c                                |   52 ++
 tools/tools-utils.h                                |   29 +
 43 files changed, 2515 insertions(+), 324 deletions(-)
---
diff --git a/doc/C/i_s_doc.xml b/doc/C/i_s_doc.xml
index 69fe4b4..26e13c1 100644
--- a/doc/C/i_s_doc.xml
+++ b/doc/C/i_s_doc.xml
@@ -231,6 +231,11 @@ do not modify-->
       </itemizedlist>
     </para>
   </sect3>
+  <sect3 id="is:__declared_fk">
+    <title>__declared_fk table</title>
+    <para>List of foreign key constraints, declared in Libgda and unknown to the database</para>
+    <para>The following table describes the columns:<informaltable frame="all"><tgroup cols="5" colsep="1" rowsep="1" align="justify"><thead><row><entry>Column name</entry><entry>Type</entry><entry>Key</entry><entry>Can be NULL</entry><entry>description</entry></row></thead><tbody><row><entry>constraint_name</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>table_catalog</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>table_schema</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>table_name</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>column_name</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>ref_table_catalog</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>ref_table_schema</entry><entry>stri
 ng</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>ref_table_name</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>ref_column_name</entry><entry>string</entry><entry>Yes</entry><entry>No</entry><entry></entry></row><row><entry>ts</entry><entry>timestamp</entry><entry></entry><entry>Yes</entry><entry></entry></row><row><entry>descr</entry><entry>string</entry><entry></entry><entry>Yes</entry><entry></entry></row></tbody></tgroup></informaltable></para>
+  </sect3>
   <sect3 id="is:_check_column_usage">
     <title>_check_column_usage table</title>
     <para>List of check constraints and the name of the tables' columns involved</para>
diff --git a/doc/C/libgda-4.0-docs.sgml b/doc/C/libgda-4.0-docs.sgml
index 0d23e73..55c17b6 100644
--- a/doc/C/libgda-4.0-docs.sgml
+++ b/doc/C/libgda-4.0-docs.sgml
@@ -1250,6 +1250,27 @@ g_object_unref (store);
 	  </para>
 	</sect2>
 
+	<sect2>
+	  <title>Declared foreign keys</title>
+	  <para>
+	    Foreign key constraints are used to constraint the contents of one or more columns of a table
+	    to the columns of another table. They are also use to help understand the database structure
+	    and withing UI programs to present relevant choices.
+	  </para>
+	  <para>
+	    &LIBGDA; reports existing foreign key constraints in its meta data.
+	  </para>
+	  <para>
+	    There are however some situations where the database developper does not whish to use
+	    foreign keys to actually implement the constraint, but the relation between the tables
+	    still exists. &LIBGDA; allows one to declare foreign keys, an operation which does not
+	    alter the database's schema, but add information to the database's meta data, specifically
+	    into the "__declared_fk" table. For more information, see the
+	    <link linkend="gda-meta-store-declare-foreign-key">gda_meta_store_declare_foreign_key()</link> and
+	    <link linkend="gda-meta-store-undeclare-foreign-key">gda_meta_store_undeclare_foreign_key</link> methods.
+	  </para>
+	</sect2>
+
 	<!-- tables and views documentation -->
 	&i-s-doc;
       </sect1>
@@ -1814,6 +1835,9 @@ g_object_unref (eng);
     <index id="index-4-2-3" role="4.2.3">
       <title>Index of new symbols in 4.2.3</title>
     </index>
+    <index id="index-4-2-4" role="4.2.4">
+      <title>Index of new symbols in 4.2.4</title>
+    </index>
     <index id="index-deprecated" role="deprecated">
       <title>Index of deprecated symbols</title>
     </index>
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index fbb0acf..ebef974 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1347,6 +1347,8 @@ gda_meta_store_get_attribute_value
 gda_meta_store_set_attribute_value
 gda_meta_store_schema_add_custom_object
 gda_meta_store_get_internal_connection
+gda_meta_store_declare_foreign_key
+gda_meta_store_undeclare_foreign_key
 <SUBSECTION Standard>
 GDA_IS_META_STORE
 GDA_META_STORE
@@ -1380,7 +1382,7 @@ GdaMetaTableForeignKey
 GDA_META_TABLE_FOREIGN_KEY
 GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY
 GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY
-GDA_META_TABLE_FOREIGN_KEY_IN_SCHEMA
+GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED
 GDA_META_STRUCT_ERROR
 gda_meta_struct_new
 gda_meta_struct_complement
diff --git a/doc/C/tmpl/gda-meta-store.sgml b/doc/C/tmpl/gda-meta-store.sgml
index ca0054a..13842fd 100644
--- a/doc/C/tmpl/gda-meta-store.sgml
+++ b/doc/C/tmpl/gda-meta-store.sgml
@@ -234,3 +234,42 @@ Dictionary object
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_meta_store_declare_foreign_key ##### -->
+<para>
+
+</para>
+
+ store: 
+ mstruct: 
+ fk_name: 
+ catalog: 
+ schema: 
+ table: 
+ ref_catalog: 
+ ref_schema: 
+ ref_table: 
+ nb_cols: 
+ colnames: 
+ ref_colnames: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_meta_store_undeclare_foreign_key ##### -->
+<para>
+
+</para>
+
+ store: 
+ mstruct: 
+ fk_name: 
+ catalog: 
+ schema: 
+ table: 
+ ref_catalog: 
+ ref_schema: 
+ ref_table: 
+ error: 
+ Returns: 
+
+
diff --git a/doc/C/tmpl/gda-meta-struct.sgml b/doc/C/tmpl/gda-meta-struct.sgml
index 90864c5..0a0322b 100644
--- a/doc/C/tmpl/gda-meta-struct.sgml
+++ b/doc/C/tmpl/gda-meta-struct.sgml
@@ -286,6 +286,7 @@ gda_meta_struct_free (mstruct);
 @fk_names_array: 
 @ref_pk_cols_array: 
 @ref_pk_names_array: 
+ fk_name: 
 
 <!-- ##### MACRO GDA_META_TABLE_FOREIGN_KEY ##### -->
 <para>
@@ -311,7 +312,7 @@ gda_meta_struct_free (mstruct);
 @fk: 
 
 
-<!-- ##### MACRO GDA_META_TABLE_FOREIGN_KEY_IN_SCHEMA ##### -->
+<!-- ##### MACRO GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED ##### -->
 <para>
 
 </para>
diff --git a/libgda/gda-meta-store.c b/libgda/gda-meta-store.c
index f240da6..de5f5fb 100644
--- a/libgda/gda-meta-store.c
+++ b/libgda/gda-meta-store.c
@@ -28,8 +28,9 @@
  *  - the version number is increased by 1:
  *      version 1 for libgda between 4.0.0 and 4.1.3
  *      version 2 for libgda >= 4.1.4: added the "_table_indexes" and "_index_column_usage" tables
+ *      version 3 for libgda >= 4.2.4: added the "__declared_fk" table
  */
-#define CURRENT_SCHEMA_VERSION "2"
+#define CURRENT_SCHEMA_VERSION "3"
 
 #include <string.h>
 #include <glib/gi18n-lib.h>
@@ -91,6 +92,8 @@ enum {
 	STMT_UPD_VERSION,
 	STMT_DEL_ATT_VALUE,
 	STMT_SET_ATT_VALUE,
+	STMT_ADD_DECLARE_FK,
+	STMT_DEL_DECLARE_FK,
 	STMT_LAST
 } PreStmtType;
 
@@ -465,6 +468,12 @@ gda_meta_store_class_init (GdaMetaStoreClass *klass)
 	klass->cpriv->prep_stmts[STMT_SET_ATT_VALUE] =
 		compute_prepared_stmt (klass->cpriv->parser, 
 				       "INSERT INTO _attributes VALUES (##name::string, ##value::string::null)");
+	klass->cpriv->prep_stmts[STMT_ADD_DECLARE_FK] =
+		compute_prepared_stmt (klass->cpriv->parser,
+				       "INSERT INTO __declared_fk (constraint_name, table_catalog, table_schema, table_name, column_name, ref_table_catalog, ref_table_schema, ref_table_name, ref_column_name) VALUES (##fkname::string, ##tcal::string, ##tschema::string, ##tname::string, ##colname::string, ##ref_tcal::string, ##ref_tschema::string, ##ref_tname::string, ##ref_colname::string)");
+	klass->cpriv->prep_stmts[STMT_DEL_DECLARE_FK] =
+		compute_prepared_stmt (klass->cpriv->parser,
+				       "DELETE FROM __declared_fk WHERE constraint_name = ##fkname::string AND table_catalog = ##tcal::string AND table_schema = ##tschema::string AND table_name = ##tname::string AND ref_table_catalog = ##ref_tcal::string AND ref_table_schema = ##ref_tschema::string AND ref_table_name = ##ref_tname::string");
 
 /*#define GDA_DEBUG_GRAPH*/
 #ifdef GDA_DEBUG_GRAPH
@@ -2116,12 +2125,14 @@ migrate_schema_from_v1_to_v2 (GdaMetaStore *store, GError **error)
 
 	/* create tables for this migration */
 	if (! create_a_dbobj (store, "_table_indexes", error))
-		return;
+		goto out;
 	if (! create_a_dbobj (store, "_index_column_usage", error))
-		return;
+		goto out;
 
 	/* set version info to CURRENT_SCHEMA_VERSION */
 	update_schema_version (store, "2", error);
+
+ out:
 	if (transaction_started) {
 		/* handle transaction started if necessary */
 		if (store->priv->version != 2)
@@ -2131,6 +2142,35 @@ migrate_schema_from_v1_to_v2 (GdaMetaStore *store, GError **error)
 	}
 }
 
+/* migrate schema from version 2 to 3 */
+static void
+migrate_schema_from_v2_to_v3 (GdaMetaStore *store, GError **error)
+{
+	g_return_if_fail (GDA_IS_CONNECTION (store->priv->cnc));
+	g_return_if_fail (gda_connection_is_opened (store->priv->cnc));
+
+	/* begin a transaction if possible */
+	gboolean transaction_started = FALSE;
+	if (! check_transaction_started (store->priv->cnc, &transaction_started))
+		return;
+
+	/* create tables for this migration */
+	if (! create_a_dbobj (store, "__declared_fk", error))
+		goto out;
+
+	/* set version info to CURRENT_SCHEMA_VERSION */
+	update_schema_version (store, "3", error);
+
+ out:
+	if (transaction_started) {
+		/* handle transaction started if necessary */
+		if (store->priv->version != 3)
+			gda_connection_rollback_transaction (store->priv->cnc, NULL, NULL);
+		else
+			gda_connection_commit_transaction (store->priv->cnc, NULL, NULL);
+	}
+}
+
 static gboolean
 handle_schema_version (GdaMetaStore *store, gboolean *schema_present, GError **error)
 {
@@ -2170,12 +2210,12 @@ handle_schema_version (GdaMetaStore *store, gboolean *schema_present, GError **e
 			case 1:
 				migrate_schema_from_v1_to_v2 (store, error);
 			case 2:
-				/* function call for migration from V2 will be here */
+				migrate_schema_from_v2_to_v3 (store, error);
+			case 3:
+				/* function call for migration from V3 will be here */
 				break;
 			default:
-				g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_INCORRECT_SCHEMA_ERROR,
-					     _ ("Unknown internal schema's version: '%s'"),
-					     g_value_get_string (version));
+				/* no downgrade to do */
 				break;
 			}
 			
@@ -4166,3 +4206,368 @@ _gda_meta_store_schema_get_downstream_contexts (GdaMetaStore *store, GdaMetaCont
 	gda_mutex_unlock (store->priv->mutex);
 	return g_slist_reverse (retlist);
 }
+
+/**
+ * gda_meta_store_declare_foreign_key:
+ * @store: a #GdaMetaStore
+ * @mstruct: (allow-none): a #GdaMetaStruct, or %NULL
+ * @fk_name: the name of the foreign key to declare
+ * @catalog: (allow-none): the catalog in which the table (for which the foreign key is for) is, or %NULL
+ * @schema: (allow-none): the schema in which the table (for which the foreign key is for) is, or %NULL
+ * @table: the name of the table (for which the foreign key is for)
+ * @ref_catalog: (allow-none): the catalog in which the referenced table is, or %NULL
+ * @ref_schema: (allow-none): the schema in which the referenced table is, or %NULL
+ * @ref_table: the name of the referenced table
+ * @nb_cols: the number of columns involved (>0)
+ * @colnames: (array length=nb_cols): an array of column names from the table for which the foreign key is for
+ * @ref_colnames: (array length=nb_cols): an array of column names from the referenced table
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Defines a new declared foreign key into @store. If another declared foreign key is already defined
+ * between the two tables and with the same name, then it is first removed.
+ *
+ * This method begins a transaction if possible (ie. none is already started), and if it can't,
+ * then if there is an error, the job may be partially done.
+ *
+ * A check is always performed to make sure all the database objects actually
+ * exist and returns an error if not. The check is performed using @mstruct if it's not %NULL, and in
+ * an internal #GdaMetaStruct if not. 
+ *
+ * The @catalog, @schema, @table, @ref_catalog, @ref_schema and @ref_table must follow the SQL
+ * identifiers naming convention, see the <link linkend="gen:sql_identifiers">SQL identifiers</link>
+ * section. The same convention needs to be respected for the strings in @conames and @ref_colnames.
+ *
+ * If @catalog is not %NULL, then @schema must also be not %NULL (the same restriction applies to
+ * @ref_catalog and @ref_schema).
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 4.2.4
+ */
+gboolean
+gda_meta_store_declare_foreign_key (GdaMetaStore *store, GdaMetaStruct *mstruct,
+				    const gchar *fk_name,
+				    const gchar *catalog, const gchar *schema, const gchar *table,
+				    const gchar *ref_catalog, const gchar *ref_schema, const gchar *ref_table,
+				    guint nb_cols,
+				    gchar **colnames, gchar **ref_colnames,
+				    GError **error)
+{
+ 	gboolean retval = FALSE;
+	GdaSet *params = NULL;
+	GdaMetaStoreClass *klass;
+	GdaMetaTable *mtable = NULL, *ref_mtable = NULL;
+	GdaMetaDbObject *dbo = NULL, *ref_dbo = NULL;
+
+	g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
+	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
+	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), FALSE);
+	g_return_val_if_fail (fk_name, FALSE);
+	g_return_val_if_fail (!catalog || (catalog && schema), FALSE);
+	g_return_val_if_fail (!ref_catalog || (ref_catalog && ref_schema), FALSE);
+	g_return_val_if_fail (table, FALSE);
+	g_return_val_if_fail (ref_table, FALSE);
+	g_return_val_if_fail (nb_cols > 0, FALSE);
+	g_return_val_if_fail (colnames, FALSE);
+	g_return_val_if_fail (ref_colnames, FALSE);
+	
+	if (mstruct)
+		g_object_ref (mstruct);
+	else
+		mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_NONE);
+
+	/* find database objects */
+	GValue *v1 = NULL, *v2 = NULL, *v3;
+	
+	if (catalog)
+		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), catalog);
+	if (schema)
+		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), schema);
+	g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), table);		
+	dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE, v1, v2, v3, error);
+	if (v1)
+		gda_value_free (v1);
+	if (v2)
+		gda_value_free (v2);
+	gda_value_free (v3);
+	if (! dbo)
+		goto out;
+	mtable = GDA_META_TABLE (dbo);
+	
+	v1 = NULL;
+	v2 = NULL;
+	if (ref_catalog)
+		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), ref_catalog);
+	if (ref_schema)
+		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), ref_schema);
+	g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), ref_table);		
+	ref_dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE, v1, v2, v3, error);
+	if (v1)
+		gda_value_free (v1);
+	if (v2)
+		gda_value_free (v2);
+	gda_value_free (v3);
+	if (! ref_dbo)
+		goto out;
+	ref_mtable = GDA_META_TABLE (ref_dbo);
+
+	/* set statement's variables */
+	if (! gda_statement_get_parameters (klass->cpriv->prep_stmts[STMT_ADD_DECLARE_FK],
+					    &params, error))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tcal", dbo->obj_catalog))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tschema", dbo->obj_schema))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tname",	dbo->obj_name))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tcal", ref_dbo->obj_catalog))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tschema", ref_dbo->obj_schema))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tname", ref_dbo->obj_name))
+		goto out;
+
+	if (! gda_set_set_holder_value (params, error, "fkname", fk_name))
+                goto out;
+
+	GdaConnection *store_cnc;
+	gboolean intrans;
+	store_cnc = gda_meta_store_get_internal_connection (store);
+	intrans = gda_connection_begin_transaction (store_cnc, NULL,
+						    GDA_TRANSACTION_ISOLATION_UNKNOWN,
+						    NULL);
+
+	/* remove existing declared FK, if any */
+	if (gda_connection_statement_execute_non_select (store_cnc,
+							 klass->cpriv->prep_stmts[STMT_DEL_DECLARE_FK],
+							 params,
+							 NULL, error) == -1) {
+		if (intrans)
+			gda_connection_rollback_transaction (store_cnc, NULL, NULL);
+		goto out;
+	}
+
+	/* declare FK */
+	guint l;
+	for (l = 0; l < nb_cols; l++) {
+		/* check that column name exists */
+		GSList *list;
+		for (list = mtable->columns; list; list = list->next) {
+			if (!strcmp (GDA_META_TABLE_COLUMN (list->data)->column_name,
+				     colnames[l]))
+				break;
+		}
+		if (!list) {
+			gchar *str;
+			if (catalog) {
+				if (schema)
+					str = g_strdup_printf ("%s.%s.%s", catalog, schema, table);
+				else
+					str = g_strdup (table);
+			}
+			else if (schema)
+				str = g_strdup_printf ("%s.%s", schema, table);
+			else
+				str = g_strdup (table);
+			g_set_error (error, GDA_META_STORE_ERROR,
+				     GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
+				     _("Could not find column '%s' in table '%s'"),
+				     colnames[l], str);
+			g_free (str);
+			goto out;
+		}
+		if (! gda_set_set_holder_value (params, error, "colname", colnames[l]))
+			goto out;
+
+		/* check that column name exists */
+		for (list = ref_mtable->columns; list; list = list->next) {
+			if (!strcmp (GDA_META_TABLE_COLUMN (list->data)->column_name,
+				     ref_colnames[l]))
+				break;
+		}
+		if (!list) {
+			gchar *str;
+			if (ref_catalog) {
+				if (ref_schema)
+					str = g_strdup_printf ("%s.%s.%s", ref_catalog,
+							       ref_schema, ref_table);
+				else
+					str = g_strdup (ref_table);
+			}
+			else if (ref_schema)
+				str = g_strdup_printf ("%s.%s", ref_schema, ref_table);
+			else
+				str = g_strdup (ref_table);
+			g_set_error (error, GDA_META_STORE_ERROR,
+				     GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
+				     _("Could not find column '%s' in table '%s'"),
+				     ref_colnames[l], str);
+			g_free (str);
+			goto out;
+		}
+		if (! gda_set_set_holder_value (params, error, "ref_colname", ref_colnames[l]))
+			goto out;
+		
+		if (gda_connection_statement_execute_non_select (store_cnc,
+								 klass->cpriv->prep_stmts[STMT_ADD_DECLARE_FK],
+								 params,
+								 NULL, error) == -1) {
+			if (intrans)
+				gda_connection_rollback_transaction (store_cnc, NULL,
+								     NULL);
+			goto out;
+		}
+	}
+	
+	if (intrans)
+		gda_connection_commit_transaction (store_cnc, NULL, NULL);
+	retval = TRUE;
+
+ out:		
+	g_object_unref (mstruct);
+	if (params)
+		g_object_unref (params);
+
+	return retval;
+}
+
+/**
+ * gda_meta_store_undeclare_foreign_key:
+ * @store: a #GdaMetaStore
+ * @mstruct: (allow-none): a #GdaMetaStruct, or %NULL
+ * @fk_name: the name of the foreign key to declare
+ * @catalog: (allow-none): the catalog in which the table (for which the foreign key is for) is, or %NULL
+ * @schema: (allow-none): the schema in which the table (for which the foreign key is for) is, or %NULL
+ * @table: the name of the table (for which the foreign key is for)
+ * @ref_catalog: (allow-none): the catalog in which the referenced table is, or %NULL
+ * @ref_schema: (allow-none): the schema in which the referenced table is, or %NULL
+ * @ref_table: the name of the referenced table
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Removes a declared foreign key from @store.
+ *
+ * This method begins a transaction if possible (ie. none is already started), and if it can't, then if there
+ * is an error, the job may be partially done.
+ *
+ * A check is always performed to make sure all the database objects actually
+ * exist and returns an error if not. The check is performed using @mstruct if it's not %NULL, and in
+ * an internal #GdaMetaStruct if not. 
+ *
+ * See gda_meta_store_declare_foreign_key() for more information anout the @catalog, @schema, @name,
+ * @ref_catalog, @ref_schema and @ref_name arguments.
+ *
+ * Returns: %TRUE if no error occurred
+ *
+ * Since: 4.2.4
+ */
+gboolean
+gda_meta_store_undeclare_foreign_key (GdaMetaStore *store, GdaMetaStruct *mstruct,
+				      const gchar *fk_name,
+				      const gchar *catalog, const gchar *schema, const gchar *table,
+				      const gchar *ref_catalog, const gchar *ref_schema, const gchar *ref_table,
+				      GError **error)
+{
+ 	gboolean retval = FALSE;
+	GdaSet *params = NULL;
+	GdaMetaStoreClass *klass;
+	GdaMetaTable *mtable = NULL, *ref_mtable = NULL;
+	GdaMetaDbObject *dbo = NULL, *ref_dbo = NULL;
+
+	g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
+	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
+	g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), FALSE);
+	g_return_val_if_fail (fk_name, FALSE);
+	g_return_val_if_fail (!catalog || (catalog && schema), FALSE);
+	g_return_val_if_fail (!ref_catalog || (ref_catalog && ref_schema), FALSE);
+	g_return_val_if_fail (table, FALSE);
+	g_return_val_if_fail (ref_table, FALSE);
+	
+	if (mstruct)
+		g_object_ref (mstruct);
+	else
+		mstruct = gda_meta_struct_new (store, GDA_META_STRUCT_FEATURE_NONE);
+
+	/* find database objects */
+	GValue *v1 = NULL, *v2 = NULL, *v3;
+	
+	if (catalog)
+		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), catalog);
+	if (schema)
+		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), schema);
+	g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), table);		
+	dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE, v1, v2, v3, error);
+	if (v1)
+		gda_value_free (v1);
+	if (v2)
+		gda_value_free (v2);
+	gda_value_free (v3);
+	if (! dbo)
+		goto out;
+	mtable = GDA_META_TABLE (dbo);
+	
+	v1 = NULL;
+	v2 = NULL;
+	if (ref_catalog)
+		g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), ref_catalog);
+	if (ref_schema)
+		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), ref_schema);
+	g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), ref_table);		
+	ref_dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE, v1, v2, v3, error);
+	if (v1)
+		gda_value_free (v1);
+	if (v2)
+		gda_value_free (v2);
+	gda_value_free (v3);
+	if (! ref_dbo)
+		goto out;
+	ref_mtable = GDA_META_TABLE (ref_dbo);
+
+	/* set statement's variables */
+	if (! gda_statement_get_parameters (klass->cpriv->prep_stmts[STMT_ADD_DECLARE_FK],
+					    &params, error))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tcal", dbo->obj_catalog))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tschema", dbo->obj_schema))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "tname",	dbo->obj_name))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tcal", ref_dbo->obj_catalog))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tschema", ref_dbo->obj_schema))
+		goto out;
+	if (! gda_set_set_holder_value (params, error, "ref_tname", ref_dbo->obj_name))
+		goto out;
+
+	if (! gda_set_set_holder_value (params, error, "fkname", fk_name))
+                goto out;
+
+	GdaConnection *store_cnc;
+	gboolean intrans;
+	store_cnc = gda_meta_store_get_internal_connection (store);
+	intrans = gda_connection_begin_transaction (store_cnc, NULL,
+						    GDA_TRANSACTION_ISOLATION_UNKNOWN,
+						    NULL);
+
+	/* remove existing declared FK, if any */
+	if (gda_connection_statement_execute_non_select (store_cnc,
+							 klass->cpriv->prep_stmts[STMT_DEL_DECLARE_FK],
+							 params,
+							 NULL, error) == -1) {
+		if (intrans)
+			gda_connection_rollback_transaction (store_cnc, NULL, NULL);
+		goto out;
+	}
+	
+	if (intrans)
+		gda_connection_commit_transaction (store_cnc, NULL, NULL);
+	retval = TRUE;
+
+ out:		
+	g_object_unref (mstruct);
+	if (params)
+		g_object_unref (params);
+
+	return retval;
+}
diff --git a/libgda/gda-meta-store.h b/libgda/gda-meta-store.h
index 68b5a18..8523e48 100644
--- a/libgda/gda-meta-store.h
+++ b/libgda/gda-meta-store.h
@@ -1,6 +1,6 @@
 /* gda-meta-store.h
  *
- * Copyright (C) 2008 - 2009 Vivien Malerba
+ * Copyright (C) 2008 - 2011 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -95,6 +95,7 @@ struct _GdaMetaStoreClass
 	GError  *(*suggest_update)(GdaMetaStore *store, GdaMetaContext *suggest);
 	void     (*meta_changed)  (GdaMetaStore *store, GSList *changes);
 
+	/*< private >*/
 	/* Padding for future expansion */
 	void (*_gda_reserved1) (void);
 	void (*_gda_reserved2) (void);
@@ -132,6 +133,20 @@ GSList           *gda_meta_store_schema_get_all_tables    (GdaMetaStore *store);
 GSList           *gda_meta_store_schema_get_depend_tables (GdaMetaStore *store, const gchar *table_name);
 GdaMetaStruct    *gda_meta_store_schema_get_structure     (GdaMetaStore *store, GError **error);
 
+gboolean          gda_meta_store_declare_foreign_key      (GdaMetaStore *store, GdaMetaStruct *mstruct,
+							   const gchar *fk_name,
+							   const gchar *catalog, const gchar *schema, const gchar *table,
+							   const gchar *ref_catalog, const gchar *ref_schema, const gchar *ref_table,
+							   guint nb_cols,
+							   gchar **colnames, gchar **ref_colnames,
+							   GError **error);
+
+gboolean          gda_meta_store_undeclare_foreign_key    (GdaMetaStore *store, GdaMetaStruct *mstruct,
+							   const gchar *fk_name,
+							   const gchar *catalog, const gchar *schema, const gchar *table,
+							   const gchar *ref_catalog, const gchar *ref_schema, const gchar *ref_table,
+							   GError **error);
+
 G_END_DECLS
 
 #endif
diff --git a/libgda/gda-meta-struct.c b/libgda/gda-meta-struct.c
index 1c50574..ea26aae 100644
--- a/libgda/gda-meta-struct.c
+++ b/libgda/gda-meta-struct.c
@@ -70,9 +70,6 @@ static void gda_meta_table_foreign_key_free (GdaMetaTableForeignKey *tfk);
 static GObjectClass  *parent_class = NULL;
 static GdaAttributesManager *att_mgr;
 
-#define ON_UPDATE_POLICY "upd_policy";
-#define ON_DELETE_POLICY "del_policy";
-
 /* properties */
 enum {
         PROP_0,
@@ -578,6 +575,239 @@ policy_string_to_value (const gchar *string)
 	return GDA_META_FOREIGN_KEY_UNKNOWN;
 }
 
+/*
+ * Find if there is already a declared foreign for @dbo to @ref_dbo using the columns
+ * listed in @columns
+ *
+ * @columns contains 4 columns, with no NULL value, and with nrows>0
+ *  - @dbo's column name (string)
+ *  - @dbo's column name ordinal position (int)
+ *  - @ref_dbo's column name (string)
+ *  - @ref_dbo's column name ordinal position (int)
+ */
+static GdaMetaTableForeignKey *
+find_real_foreign_key (GdaMetaTable *table, GdaMetaDbObject *ref_dbo, GdaDataModel *columns)
+{
+	GSList *list;
+	gint nrows = -1;
+	for (list = table->fk_list; list; list = list->next) {
+		GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data;
+		gint i;
+		gboolean *mapping;
+		if (fk->depend_on != ref_dbo)
+			continue;
+
+		if (nrows == -1)
+			nrows = gda_data_model_get_n_rows (columns);
+
+		if (nrows != fk->cols_nb)
+			continue;
+
+		mapping = g_new0 (gboolean, nrows);
+		for (i = 0; i < nrows; i++) {
+			const GValue *cvalues[4];
+			const gchar *fk_colname, *ref_colname;
+			gint fk_pos, ref_pos;
+			gint j;
+			for (j = 0; j < 4; j++) {
+				cvalues[j] = gda_data_model_get_value_at (columns, j, i, NULL);
+				if (!cvalues[j]) {
+					g_free (mapping);
+					goto out;
+				}
+			}
+			fk_colname = g_value_get_string (cvalues[0]);
+			fk_pos = g_value_get_int (cvalues[1]);
+			ref_colname = g_value_get_string (cvalues[2]);
+			ref_pos = g_value_get_int (cvalues[3]);
+			if (!fk_colname || !*fk_colname ||
+			    !ref_colname || !*ref_colname) {
+				g_free (mapping);
+				goto out;
+			}
+
+			for (j = 0; j < nrows; j++) {
+				if (mapping [j]) /* row already mapped? */
+					continue;
+				if ((fk_pos == fk->fk_cols_array[j]) &&
+				    (ref_pos == fk->ref_pk_cols_array[j]) &&
+				    !strcmp (fk_colname, fk->fk_names_array[j]) &&
+				    !strcmp (ref_colname, fk->ref_pk_names_array[j])) {
+					mapping [j] = TRUE;
+					break;
+				}
+			}
+			if (j == nrows)
+				/* i'th row has not been mapped */
+				break;
+		}
+		g_free (mapping);
+		if (i == nrows)
+			/* all the rows have been mapped */
+			return fk;
+		else
+			continue;
+	}
+ out:
+	return NULL;
+}
+
+static gboolean
+add_declared_foreign_keys (GdaMetaStruct *mstruct, GdaMetaTable *mt, GError **error)
+{
+	const gchar *sql1 = "SELECT DISTINCT constraint_name, ref_table_catalog, ref_table_schema, ref_table_name FROM __declared_fk WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string ORDER BY constraint_name, ref_table_catalog, ref_table_schema, ref_table_name";
+	const gchar *sql2 = "select de.column_name, c.ordinal_position, de.ref_column_name, rc.ordinal_position FROM __declared_fk de LEFT JOIN _columns c ON (de.table_catalog=c.table_catalog AND de.table_schema=c.table_schema AND de.table_name=c.table_name AND de.column_name=c.column_name) LEFT JOIN _columns rc ON (de.ref_table_catalog=rc.table_catalog AND de.ref_table_schema=rc.table_schema AND de.ref_table_name=rc.table_name AND de.ref_column_name=rc.column_name) WHERE de.constraint_name=##cname::string AND de.table_catalog=##tc::string AND de.table_schema=##ts::string AND de.table_name=##tname::string";
+	GdaDataModel *model, *cmodel = NULL;
+	gint i, nrows;
+	GValue *v1, *v2, *v3;
+	GdaMetaDbObject *dbo;
+
+	dbo = (GdaMetaDbObject*) mt;
+	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
+	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
+	g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+	model = gda_meta_store_extract (mstruct->priv->store, sql1, 
+					error, "tc", v1, "ts", v2, "tname", v3, NULL);
+	if (!model) 
+		goto onerror;
+
+	nrows = gda_data_model_get_n_rows (model);
+	for (i = 0; i < nrows; i++) {
+		GdaMetaDbObject *ref_dbo = NULL;
+		GdaMetaTableForeignKey *tfk = NULL;
+		const GValue *fk_catalog, *fk_schema, *fk_tname, *fk_name;
+		gboolean ignore = FALSE;
+
+		fk_name = gda_data_model_get_value_at (model, 0, i, error);
+		if (!fk_name) goto onerror;		
+		fk_catalog = gda_data_model_get_value_at (model, 1, i, error);
+		if (!fk_catalog) goto onerror;
+		fk_schema = gda_data_model_get_value_at (model, 2, i, error);
+		if (!fk_schema) goto onerror;
+		fk_tname = gda_data_model_get_value_at (model, 3, i, error);
+		if (!fk_tname) goto onerror;
+
+		/* get columns */
+		cmodel = gda_meta_store_extract (mstruct->priv->store, sql2, 
+						 error, "tc", v1, "ts", v2, "tname", v3,
+						 "cname", fk_name, NULL);
+		if (!cmodel) 
+			goto onerror;
+
+		/* create new FK structure */
+		tfk = g_new0 (GdaMetaTableForeignKey, 1);
+		tfk->meta_table = dbo;
+		tfk->declared = (gpointer) 0x01;
+		tfk->fk_name = g_value_dup_string (fk_name);
+		tfk->on_update_policy = GINT_TO_POINTER (GDA_META_FOREIGN_KEY_NONE);
+		tfk->on_delete_policy = GINT_TO_POINTER (GDA_META_FOREIGN_KEY_NONE);
+		
+		/* ignore if some columns are not found in the schema
+		 * (maybe wrong or outdated FK decl) */
+		gint j, cnrows;
+		cnrows = gda_data_model_get_n_rows (cmodel);
+		if (cnrows == 0)
+			ignore = TRUE;
+		else {
+			tfk->cols_nb = cnrows;
+			tfk->fk_cols_array = g_new0 (gint, cnrows);
+			tfk->fk_names_array = g_new0 (gchar *, cnrows);
+			tfk->ref_pk_cols_array = g_new0 (gint, cnrows);
+			tfk->ref_pk_names_array = g_new0 (gchar *, cnrows);
+		}
+		for (j = 0; j < cnrows; j++) {
+			const GValue *ov;
+			ov = gda_data_model_get_value_at (cmodel, 0, j, error);
+			if (!ov) goto onerror;
+			if (G_VALUE_TYPE (ov) != G_TYPE_STRING) {
+				ignore = TRUE;
+				break;
+			}
+			tfk->fk_names_array [j] = g_value_dup_string (ov);
+
+			ov = gda_data_model_get_value_at (cmodel, 1, j, error);
+			if (!ov) goto onerror;
+			if (G_VALUE_TYPE (ov) != G_TYPE_INT) {
+				ignore = TRUE;
+				break;
+			}
+			tfk->fk_cols_array [j] = g_value_get_int (ov);
+
+			ov = gda_data_model_get_value_at (cmodel, 2, j, error);
+			if (!ov) goto onerror;
+			if (G_VALUE_TYPE (ov) != G_TYPE_STRING) {
+				ignore = TRUE;
+				break;
+			}
+			tfk->ref_pk_names_array [j] = g_value_dup_string (ov);
+
+			ov = gda_data_model_get_value_at (cmodel, 3, j, error);
+			if (!ov) goto onerror;
+			if (G_VALUE_TYPE (ov) != G_TYPE_INT) {
+				ignore = TRUE;
+				break;
+			}
+			tfk->ref_pk_cols_array [j] = g_value_get_int (ov);
+		}
+
+		/* ignore if there is already an implemented FK */
+		if (! ignore) {
+			ref_dbo = _meta_struct_get_db_object (mstruct, fk_catalog,
+							      fk_schema, fk_tname);
+			if (ref_dbo && find_real_foreign_key (mt, ref_dbo, cmodel))
+				ignore = TRUE;
+		}
+		if (ignore) {
+			/* ignore this FK end move on to the next one */
+			if (tfk)
+				gda_meta_table_foreign_key_free (tfk);
+			continue;
+		}
+
+		tfk->depend_on = ref_dbo;
+		if (!tfk->depend_on) {
+			gchar *str;
+			tfk->depend_on = g_new0 (GdaMetaDbObject, 1);
+			tfk->depend_on->obj_type = GDA_META_DB_UNKNOWN;
+			tfk->depend_on->obj_catalog = g_strdup (g_value_get_string (fk_catalog));
+			tfk->depend_on->obj_schema = g_strdup (g_value_get_string (fk_schema));
+			tfk->depend_on->obj_name = g_strdup (g_value_get_string (fk_tname));
+			mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, tfk->depend_on);
+			str = g_strdup_printf ("%s.%s.%s", g_value_get_string (fk_catalog), 
+					       g_value_get_string (fk_schema), 
+					       g_value_get_string (fk_tname));
+			g_hash_table_insert (mstruct->priv->index, str, tfk->depend_on);
+		}
+		else if (tfk->depend_on->obj_type == GDA_META_DB_TABLE) {
+			GdaMetaTable *dot = GDA_META_TABLE (tfk->depend_on);
+			dot->reverse_fk_list = g_slist_prepend (dot->reverse_fk_list, tfk);
+		}
+
+		dbo->depend_list = g_slist_append (dbo->depend_list, tfk->depend_on);
+		mt->fk_list = g_slist_prepend (mt->fk_list, tfk);
+	}
+	g_object_unref (model);
+	if (cmodel)
+		g_object_unref (cmodel);
+
+	gda_value_free (v1);
+	gda_value_free (v2);
+	gda_value_free (v3);
+
+	return TRUE;
+
+ onerror:
+	if (model)
+		g_object_unref (model);
+	if (cmodel)
+		g_object_unref (cmodel);
+	gda_value_free (v1);
+	gda_value_free (v2);
+	gda_value_free (v3);
+
+	return FALSE;
+}
+
 static GdaMetaDbObject *
 _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 			 const GValue *icatalog, const GValue *ischema, const GValue *iname, 
@@ -856,7 +1086,7 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 
 		/* foreign keys */
 		if (mstruct->priv->features & GDA_META_STRUCT_FEATURE_FOREIGN_KEYS) { 
-			sql = "SELECT ref_table_catalog, ref_table_schema, ref_table_name, constraint_name, ref_constraint_name, update_rule, delete_rule FROM _referential_constraints WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string";
+			sql = "SELECT ref_table_catalog, ref_table_schema, ref_table_name, constraint_name, ref_constraint_name, update_rule, delete_rule, constraint_name FROM _referential_constraints WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string";
 			model = gda_meta_store_extract (mstruct->priv->store, sql, error, 
 							"tc", icatalog, 
 							"ts", ischema, 
@@ -867,34 +1097,37 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 			nrows = gda_data_model_get_n_rows (model);
 			for (i = 0; i < nrows; i++) {
 				GdaMetaTableForeignKey *tfk;
-				const GValue *fk_catalog, *fk_schema, *fk_name;
+				const GValue *fk_catalog, *fk_schema, *fk_tname, *fk_name;
 				const GValue *upd_policy, *del_policy;
 
 				fk_catalog = gda_data_model_get_value_at (model, 0, i, error);
 				if (!fk_catalog) goto onfkerror;
 				fk_schema = gda_data_model_get_value_at (model, 1, i, error);
 				if (!fk_schema) goto onfkerror;
-				fk_name = gda_data_model_get_value_at (model, 2, i, error);
-				if (!fk_name) goto onfkerror;
+				fk_tname = gda_data_model_get_value_at (model, 2, i, error);
+				if (!fk_tname) goto onfkerror;
 				upd_policy = gda_data_model_get_value_at (model, 5, i, error);
 				if (!upd_policy) goto onfkerror;
 				del_policy = gda_data_model_get_value_at (model, 6, i, error);
 				if (!del_policy) goto onfkerror;
+				fk_name = gda_data_model_get_value_at (model, 7, i, error);
+				if (!fk_name) goto onfkerror;
 
 				tfk = g_new0 (GdaMetaTableForeignKey, 1);
 				tfk->meta_table = dbo;
-				tfk->depend_on = _meta_struct_get_db_object (mstruct, fk_catalog, fk_schema, fk_name);
+				tfk->fk_name = g_value_dup_string (fk_name);
+				tfk->depend_on = _meta_struct_get_db_object (mstruct, fk_catalog, fk_schema, fk_tname);
 				if (!tfk->depend_on) {
 					gchar *str;
 					tfk->depend_on = g_new0 (GdaMetaDbObject, 1);
 					tfk->depend_on->obj_type = GDA_META_DB_UNKNOWN;
 					tfk->depend_on->obj_catalog = g_strdup (g_value_get_string (fk_catalog));
 					tfk->depend_on->obj_schema = g_strdup (g_value_get_string (fk_schema));
-					tfk->depend_on->obj_name = g_strdup (g_value_get_string (fk_name));
+					tfk->depend_on->obj_name = g_strdup (g_value_get_string (fk_tname));
 					mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, tfk->depend_on);
 					str = g_strdup_printf ("%s.%s.%s", g_value_get_string (fk_catalog), 
 							       g_value_get_string (fk_schema), 
-							       g_value_get_string (fk_name));
+							       g_value_get_string (fk_tname));
 					g_hash_table_insert (mstruct->priv->index, str, tfk->depend_on);
 				}
 				else if (tfk->depend_on->obj_type == GDA_META_DB_TABLE) {
@@ -903,21 +1136,17 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 				}
 				dbo->depend_list = g_slist_append (dbo->depend_list, tfk->depend_on);
 
-				tfk->on_update_policy = g_new (GdaMetaForeignKeyPolicy, 1);
 				if (G_VALUE_TYPE (upd_policy) == G_TYPE_STRING)
-					*tfk->on_update_policy = policy_string_to_value (g_value_get_string (upd_policy));
+					tfk->on_update_policy = GINT_TO_POINTER (policy_string_to_value (g_value_get_string (upd_policy)));
 				else
-					*tfk->on_update_policy = GDA_META_FOREIGN_KEY_UNKNOWN;
+					tfk->on_update_policy = GINT_TO_POINTER (GDA_META_FOREIGN_KEY_UNKNOWN);
 
-				tfk->on_delete_policy = g_new (GdaMetaForeignKeyPolicy, 1);
 				if (G_VALUE_TYPE (upd_policy) == G_TYPE_STRING)
-					*tfk->on_delete_policy = policy_string_to_value (g_value_get_string (del_policy));
+					tfk->on_delete_policy = GINT_TO_POINTER (policy_string_to_value (g_value_get_string (del_policy)));
 				else
-					*tfk->on_delete_policy = GDA_META_FOREIGN_KEY_UNKNOWN;
+					tfk->on_delete_policy = GINT_TO_POINTER (GDA_META_FOREIGN_KEY_UNKNOWN);
 
-				tfk->defined_in_schema = g_new (gboolean, 1);
-				*tfk->defined_in_schema = TRUE;
-					
+				tfk->declared = NULL;
 
 				/* FIXME: compute @cols_nb, and all the @*_array members (ref_pk_cols_array must be
 				 * initialized with -1 values everywhere */
@@ -939,9 +1168,9 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 				ref_pk_cols = gda_meta_store_extract (mstruct->priv->store, sql, error, 
 								      "tc", fk_catalog,
 								      "ts", fk_schema,
-								      "tname", fk_name,
+								      "tname", fk_tname,
 								      "cname", cvalue, NULL);
-				/*g_print ("tname=%s cvalue=%s\n", gda_value_stringify (fk_name),
+				/*g_print ("tname=%s cvalue=%s\n", gda_value_stringify (fk_tname),
 				  gda_value_stringify (cvalue));*/
 				
 				if (fk_cols && ref_pk_cols) {
@@ -998,7 +1227,7 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 						     g_value_get_string (iname),
 						     g_value_get_string (fk_catalog), 
 						     g_value_get_string (fk_schema), 
-						     g_value_get_string (fk_name));
+						     g_value_get_string (fk_tname));
 					goto onfkerror;
 				}
 				if (fk_cols)
@@ -1021,6 +1250,8 @@ _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type,
 			mt->fk_list = g_slist_reverse (mt->fk_list);
 			g_object_unref (model);
 			/* Note: mt->reverse_fk_list is not determined here */
+
+			add_declared_foreign_keys (mstruct, mt, NULL);
 		}
 		
 		break;
@@ -1891,9 +2122,7 @@ gda_meta_table_foreign_key_free (GdaMetaTableForeignKey *tfk)
 	g_free (tfk->fk_names_array);
 	g_free (tfk->ref_pk_cols_array);
 	g_free (tfk->ref_pk_names_array);
-	g_free (tfk->on_update_policy);
-	g_free (tfk->on_delete_policy);
-	g_free (tfk->defined_in_schema);
+	g_free (tfk->fk_name);
 	g_free (tfk);
 }
 
diff --git a/libgda/gda-meta-struct.h b/libgda/gda-meta-struct.h
index bd63e0c..1bdfec0 100644
--- a/libgda/gda-meta-struct.h
+++ b/libgda/gda-meta-struct.h
@@ -336,12 +336,17 @@ typedef struct {
 	gchar           **ref_pk_names_array; /* Ref PK fields names */
 
 	/*< private >*/
-	GdaMetaForeignKeyPolicy *on_update_policy;
-	GdaMetaForeignKeyPolicy *on_delete_policy;
-	gboolean                *defined_in_schema;
+	gpointer          on_update_policy; /* pointer containing the GdaMetaForeignKeyPolicy integer
+					     * to keep ABI from 4.0, use GINT_TO_POINTER and
+					     * GPOINTER_TO_INT */
+	gpointer          on_delete_policy; /* pointer containing the GdaMetaForeignKeyPolicy integer
+					     * to keep ABI from 4.0, use GINT_TO_POINTER and
+					     * GPOINTER_TO_INT */
+	gpointer          declared; /* pointer to a boolean to keep ABI from 4.0.
+				     * Any non NULL if FK has been declared in meta data */
 
-	/* Padding for future expansion */
-	gpointer _gda_reserved1;
+	/*< public >*/
+	gchar            *fk_name;
 } GdaMetaTableForeignKey;
 /**
  * GDA_META_TABLE_FOREIGN_KEY
@@ -361,7 +366,7 @@ typedef struct {
  *
  * Returns: the policy as a #GdaMetaForeignKeyPolicy
  */
-#define GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY(fk) (*(((GdaMetaTableForeignKey*)(fk))->on_update_policy))
+#define GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY(fk) ((GdaMetaForeignKeyPolicy) GPOINTER_TO_INT ((GdaMetaTableForeignKey*)(fk)->on_update_policy))
 
 /**
  * GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY:
@@ -371,18 +376,18 @@ typedef struct {
  *
  * Returns: the policy as a #GdaMetaForeignKeyPolicy
  */
-#define GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY(fk) (*(((GdaMetaTableForeignKey*)(fk))->on_delete_policy))
+#define GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY(fk) ((GdaMetaForeignKeyPolicy) GPOINTER_TO_INT ((GdaMetaTableForeignKey*)(fk)->on_delete_policy))
 
 /**
- * GDA_META_TABLE_FOREIGN_KEY_IN_SCHEMA
+ * GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED
  * @fk: a pointer to a #GdaMetaTableForeignKey
  *
  * Tells if @fk is an actual foreign key defined in the database's schema, or if it is an indication which
  * has been added to help Libgda understand the database schema.
  *
- * Returns: %TRUE if @fk is an actual foreign key defined in the database's schema
+ * Returns: %TRUE if @fk has been declared in the database's meta data and %FALSE if @fk is an actual foreign key defined in the database's schema
  */
-#define GDA_META_TABLE_FOREIGN_KEY_IN_SCHEMA(fk) (*(((GdaMetaTableForeignKey*)(fk))->defined_in_schema))
+#define GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED(fk) ((((GdaMetaTableForeignKey*)(fk))->declared) ? TRUE : FALSE)
 
 GType               gda_meta_struct_get_type           (void) G_GNUC_CONST;
 GdaMetaStruct      *gda_meta_struct_new                (GdaMetaStore *store, GdaMetaStructFeature features);
diff --git a/libgda/information_schema.xml b/libgda/information_schema.xml
index edcc051..f794faa 100644
--- a/libgda/information_schema.xml
+++ b/libgda/information_schema.xml
@@ -435,6 +435,41 @@
     </fkey>
   </table>
 
+  <!-- 
+     This table lists foreign keys declared but no present in the database. The constraint name is mandatory.
+    -->
+  <table name="__declared_fk" descr="List of foreign key constraints, declared in Libgda and unknown to the database">
+    <column name="constraint_name" pkey="TRUE"/>
+
+    <column name="table_catalog" pkey="TRUE" ident="TRUE"/>
+    <column name="table_schema" pkey="TRUE" ident="TRUE"/>
+    <column name="table_name" pkey="TRUE" ident="TRUE"/>
+    <column name="column_name" pkey="TRUE" ident="TRUE"/>
+
+    <column name="ref_table_catalog" pkey="TRUE" ident="TRUE"/>
+    <column name="ref_table_schema" pkey="TRUE" ident="TRUE"/>
+    <column name="ref_table_name" pkey="TRUE" ident="TRUE"/>
+    <column name="ref_column_name" pkey="TRUE" ident="TRUE"/>
+
+    <column name="ts" type="timestamp" nullok="TRUE"/>
+    <column name="descr" nullok="TRUE"/>
+
+    <!-- FK indication, not enforced
+    <fkey ref_table="_columns">
+      <part column="table_catalog" ref_column="table_catalog"/>
+      <part column="table_schema" ref_column="table_schema"/>
+      <part column="table_name" ref_column="table_name"/>
+      <part column="column_name" ref_column="column_name"/>
+    </fkey>
+    <fkey ref_table="_columns">
+      <part column="ref_table_catalog" ref_column="table_catalog"/>
+      <part column="ref_table_schema" ref_column="table_schema"/>
+      <part column="ref_table_name" ref_column="table_name"/>
+      <part column="ref_column_name" ref_column="column_name"/>
+    </fkey>
+    -->
+  </table>
+
   <table name="_check_column_usage" descr="List of check constraints and the name of the tables' columns involved">
     <column name="table_catalog" pkey="TRUE" ident="TRUE"/>
     <column name="table_schema" pkey="TRUE" ident="TRUE"/>
diff --git a/libgda/libgda.symbols b/libgda/libgda.symbols
index 35ca289..ba28244 100644
--- a/libgda/libgda.symbols
+++ b/libgda/libgda.symbols
@@ -417,6 +417,7 @@
 	gda_meta_sort_type_get_type
 	gda_meta_store_change_type_get_type
 	gda_meta_store_create_modify_data_model
+	gda_meta_store_declare_foreign_key
 	gda_meta_store_error_get_type
 	gda_meta_store_error_quark
 	gda_meta_store_extract
@@ -437,6 +438,7 @@
 	gda_meta_store_set_identifiers_style
 	gda_meta_store_set_reserved_keywords_func
 	gda_meta_store_sql_identifier_quote
+	gda_meta_store_undeclare_foreign_key
 	gda_meta_struct_add_db_object
 	gda_meta_struct_complement
 	gda_meta_struct_complement_all
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 136399f..ea76604 100755
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -262,6 +262,7 @@ tools/browser/canvas/browser-canvas-fkey.c
 tools/browser/canvas/browser-canvas-print.c
 tools/browser/canvas/browser-canvas-table.c
 tools/browser/canvas/browser-canvas.c
+tools/browser/common/fk-declare.c
 tools/browser/common/gdaui-data-import.c
 tools/browser/common/gdaui-entry-import.c
 tools/browser/common/objects-cloud.c
diff --git a/tests/meta-store/common.c b/tests/meta-store/common.c
index 1818bea..4796d53 100644
--- a/tests/meta-store/common.c
+++ b/tests/meta-store/common.c
@@ -305,7 +305,8 @@ common_drop_all_tables (GdaMetaStore *store)
 		"_parameters",
 		"_routine_columns",
 		"_table_indexes",
-		"_index_column_usage"
+		"_index_column_usage",
+		"__declared_fk"
 	};
 	gchar *view_names [] = {
 		"_all_types",
diff --git a/tools/Makefile.am b/tools/Makefile.am
index ee24934..652329b 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -31,6 +31,8 @@ gda_list_config_4_0_LDADD = \
 gda_sql_4_0_SOURCES = \
 	config-info.h \
 	config-info.c \
+	tools-utils.h \
+	tools-utils.c \
         gda-sql.c \
 	gda-sql.h \
 	gda-threader.h \
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index fa526ff..15bd70c 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -32,7 +32,10 @@ marshal.c: marshal.list $(GLIB_GENMARSHAL) marshal.h
 	$(GLIB_GENMARSHAL) $< --body --prefix=_marshal > $@
 
 libbrowser_la_SOURCES=\
+	../config-info.h \
 	../config-info.c \
+	../tools-utils.h \
+	../tools-utils.c \
 	cc-gray-bar.c \
 	cc-gray-bar.h \
 	marshal.c \
@@ -118,16 +121,23 @@ desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop)
 
 # canvas example
 canvas_example_DEPENDENCIES = \
-	canvas/libcanvas.la
+	canvas/libcanvas.la \
+	common/libcommon.la
+
 canvas_example_SOURCES = \
+	../tools-utils.h \
+	../tools-utils.c \
 	canvas-example.c \
 	dnd.c \
 	dnd.h
+
 canvas_example_CFLAGS = -DCANVAS_EXAMPLE
 canvas_example_LDFLAGS = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(top_builddir)/libgda-ui/libgda-ui-4.0.la \
 	$(top_builddir)/tools/browser/canvas/libcanvas.la \
+	libbrowser.la \
+	$(top_builddir)/libgda-ui/internal/libgda-ui-internal.la \
 	$(top_builddir)/tools/browser/common/libcommon.la
 
 # icons
diff --git a/tools/browser/browser-connection.c b/tools/browser/browser-connection.c
index f161732..ae1ccda 100644
--- a/tools/browser/browser-connection.c
+++ b/tools/browser/browser-connection.c
@@ -328,6 +328,7 @@ wrapper_meta_struct_sync (BrowserConnection *bcnc, GError **error)
 	return GINT_TO_POINTER (retval ? 2 : 1);
 }
 
+
 static void
 meta_changed_cb (G_GNUC_UNUSED GdaThreadWrapper *wrapper,
 		 G_GNUC_UNUSED GdaMetaStore *store,
@@ -359,6 +360,20 @@ meta_changed_cb (G_GNUC_UNUSED GdaThreadWrapper *wrapper,
 	}
 }
 
+/**
+ * browser_connection_meta_data_changed
+ * @bcnc: a #BrowserConnection
+ *
+ * Call this function if the meta data has been changed directly (ie. for example after
+ * declaring or undeclaring a foreign key). This call creates a new #GdaMetaStruct internally used.
+ */
+void
+browser_connection_meta_data_changed (BrowserConnection *bcnc)
+{
+	g_return_if_fail (BROWSER_IS_CONNECTION (bcnc));
+	meta_changed_cb (NULL, NULL, NULL, 0, NULL, NULL, bcnc);
+}
+
 static void
 browser_connection_set_property (GObject *object,
 				 guint param_id,
diff --git a/tools/browser/browser-connection.h b/tools/browser/browser-connection.h
index 603ec19..ad0dfa0 100644
--- a/tools/browser/browser-connection.h
+++ b/tools/browser/browser-connection.h
@@ -65,6 +65,7 @@ const GdaDsnInfo   *browser_connection_get_information        (BrowserConnection
 
 gboolean            browser_connection_is_busy                (BrowserConnection *bcnc, gchar **out_reason);
 void                browser_connection_update_meta_data       (BrowserConnection *bcnc);
+void                browser_connection_meta_data_changed      (BrowserConnection *bcnc);
 GdaMetaStruct      *browser_connection_get_meta_struct        (BrowserConnection *bcnc);
 GdaMetaStore       *browser_connection_get_meta_store         (BrowserConnection *bcnc);
 const gchar        *browser_connection_get_dictionary_file    (BrowserConnection *bcnc);
diff --git a/tools/browser/canvas-example.c b/tools/browser/canvas-example.c
index c197fee..d60bdd0 100644
--- a/tools/browser/canvas-example.c
+++ b/tools/browser/canvas-example.c
@@ -156,53 +156,3 @@ on_delete_event (G_GNUC_UNUSED GtkWidget *window, G_GNUC_UNUSED GdkEvent *event,
 {
 	exit (0);
 }
-
-/*
- * icons
- */
-typedef enum {
-        BROWSER_ICON_BOOKMARK,
-        BROWSER_ICON_SCHEMA,
-        BROWSER_ICON_TABLE,
-        BROWSER_ICON_COLUMN,
-        BROWSER_ICON_COLUMN_PK,
-        BROWSER_ICON_COLUMN_FK,
-        BROWSER_ICON_COLUMN_FK_NN,
-        BROWSER_ICON_COLUMN_NN,
-        BROWSER_ICON_REFERENCE,
-
-        BROWSER_ICON_LAST
-} BrowserIconType;
-
-GdkPixbuf *
-browser_get_pixbuf_icon (BrowserIconType type)
-{
-        static GdkPixbuf **array = NULL;
-        static const gchar* names[] = {
-                "gda-browser-bookmark.png",
-                "gda-browser-schema.png",
-                "gda-browser-table.png",
-                "gda-browser-column.png",
-                "gda-browser-column-pk.png",
-                "gda-browser-column-fk.png",
-                "gda-browser-column-fknn.png",
-                "gda-browser-column-nn.png",
-                "gda-browser-reference.png"
-        };
-
-        if (!array)
-                array = g_new0 (GdkPixbuf *, BROWSER_ICON_LAST);
-        if (!array [type]) {
-                gchar *path;
-                path = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "pixmaps", names[type], NULL);
-                array [type] = gdk_pixbuf_new_from_file (path, NULL);
-                g_free (path);
-
-                if (!array [type])
-                        array [type] = (GdkPixbuf*) 0x01;
-        }
-        if (array [type] == (GdkPixbuf*) 0x01)
-                return NULL;
-        else
-                return array [type];
-}
diff --git a/tools/browser/canvas/browser-canvas-db-relations.c b/tools/browser/canvas/browser-canvas-db-relations.c
index aca96d4..e320864 100644
--- a/tools/browser/canvas/browser-canvas-db-relations.c
+++ b/tools/browser/canvas/browser-canvas-db-relations.c
@@ -1,6 +1,6 @@
 /* browser-canvas-db-relations.c
  *
- * Copyright (C) 2002 - 2009 Vivien Malerba
+ * Copyright (C) 2002 - 2011 Vivien Malerba
  * Copyright (C) 2002 Fernando Martins
  *
  * This program is free software; you can redistribute it and/or
@@ -28,7 +28,10 @@
 #include "browser-canvas-column.h"
 #include "browser-canvas-fkey.h"
 #include "../common/objects-cloud.h"
+#include "../common/fk-declare.h"
 #include <libgda-ui/internal/popup-container.h>
+#include "../browser-window.h"
+#include "../support.h"
 
 static void browser_canvas_db_relations_class_init (BrowserCanvasDbRelationsClass *class);
 static void browser_canvas_db_relations_init       (BrowserCanvasDbRelations *canvas);
@@ -367,29 +370,74 @@ static GtkWidget *canvas_entity_popup_func (BrowserCanvasTable *ce);
 static void popup_func_delete_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
 static void popup_func_add_depend_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
 static void popup_func_add_ref_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
+static void popup_func_declare_fk_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce);
+
 static GtkWidget *
 canvas_entity_popup_func (BrowserCanvasTable *ce)
 {
 	GtkWidget *menu, *entry;
 
 	menu = gtk_menu_new ();
-	entry = gtk_menu_item_new_with_label (_("Remove"));
+	entry = gtk_menu_item_new_with_label (_("Remove from graph"));
 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_delete_cb), ce);
 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
 	gtk_widget_show (entry);
-	entry = gtk_menu_item_new_with_label (_("Add referenced tables"));
+	entry = gtk_menu_item_new_with_label (_("Add referenced tables to graph"));
 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_depend_cb), ce);
 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
 	gtk_widget_show (entry);
-	entry = gtk_menu_item_new_with_label (_("Add tables referencing this table"));
+	entry = gtk_menu_item_new_with_label (_("Add tables referencing this table to graph"));
 	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_add_ref_cb), ce);
 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
 	gtk_widget_show (entry);
 
+	entry = gtk_separator_menu_item_new ();
+	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+	gtk_widget_show (entry);
+
+	entry = gtk_menu_item_new_with_label (_("Declare foreign key for this table"));
+	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_func_declare_fk_cb), ce);
+	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+	gtk_widget_show (entry);
+
 	return menu;
 }
 
 static void
+popup_func_declare_fk_cb (GtkMenuItem *mitem, BrowserCanvasTable *ce)
+{
+	GtkWidget *dlg, *parent;
+	GdaMetaStruct *mstruct;
+	GdaMetaTable *mtable;
+	gint response;
+
+	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (ce)));
+	g_object_get (G_OBJECT (ce), "meta-struct", &mstruct, "table", &mtable, NULL);
+	dlg = fk_declare_new ((GtkWindow *) parent, mstruct, mtable);
+	response = gtk_dialog_run (GTK_DIALOG (dlg));
+	if (response == GTK_RESPONSE_ACCEPT) {
+		GError *error = NULL;
+		if (! fk_declare_write (FK_DECLARE (dlg),
+					BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+					&error)) {
+			browser_show_error ((GtkWindow *) parent, _("Failed to declare foreign key: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+		else if (BROWSER_IS_WINDOW (parent))
+			browser_window_show_notice (BROWSER_WINDOW (parent),
+						    GTK_MESSAGE_INFO, "fkdeclare",
+						    _("Successfully declared foreign key"));
+		else
+			browser_show_message ((GtkWindow *) parent, "%s",
+					      _("Successfully declared foreign key"));
+	}
+	
+	gtk_widget_destroy (dlg);
+	g_object_unref (mstruct);
+}
+
+static void
 popup_func_delete_cb (G_GNUC_UNUSED GtkMenuItem *mitem, BrowserCanvasTable *ce)
 {
 	GdaMetaTable *mtable;
diff --git a/tools/browser/canvas/browser-canvas-fkey.c b/tools/browser/canvas/browser-canvas-fkey.c
index ec64890..21c6688 100644
--- a/tools/browser/canvas/browser-canvas-fkey.c
+++ b/tools/browser/canvas/browser-canvas-fkey.c
@@ -1,6 +1,6 @@
 /* browser-canvas-fkey.c
  *
- * Copyright (C) 2004 - 2007 Vivien Malerba
+ * Copyright (C) 2004 - 2011 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -28,6 +28,10 @@
 #include "browser-canvas-text.h"
 #include "browser-canvas-utility.h"
 #include "browser-canvas-db-relations.h"
+#include "../../tools-utils.h"
+#include "../support.h"
+#include "../browser-window.h"
+#include "../common/fk-declare.h"
 
 static void browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class);
 static void browser_canvas_fkey_init       (BrowserCanvasFkey * cc);
@@ -68,6 +72,7 @@ struct _BrowserCanvasFkeyPrivate
 
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass *parent_class = NULL;
+static GooCanvasLineDash *dash = NULL, *no_dash = NULL;
 
 GType
 browser_canvas_fkey_get_type (void)
@@ -119,7 +124,9 @@ browser_canvas_fkey_class_init (BrowserCanvasFkeyClass * class)
 					 g_param_spec_pointer ("fk_constraint", "FK constraint", 
 							       NULL, 
 							       G_PARAM_WRITABLE));
-       
+
+	dash = goo_canvas_line_dash_new (2, 5., 1.5);
+	no_dash = goo_canvas_line_dash_new (0);
 }
 
 static void
@@ -341,6 +348,7 @@ create_items (BrowserCanvasFkey *cc)
 		gchar *color = "black";
 		g_object_set (G_OBJECT (item), 
 			      "stroke-color", color,
+			      "line-dash", GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ? dash : no_dash,
 			      NULL);
 		
 		if (G_OBJECT_TYPE (item) == GOO_TYPE_CANVAS_POLYLINE) {
@@ -385,7 +393,7 @@ update_items (BrowserCanvasFkey *cc)
  * item is for a single FK constraint
  */
 static gboolean
-single_item_enter_notify_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
+single_item_enter_notify_event_cb (GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
 				   G_GNUC_UNUSED GdkEventCrossing *event, BrowserCanvasFkey *cc)
 {
 	gint i;
@@ -407,6 +415,19 @@ single_item_enter_notify_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSE
 
 		column = browser_canvas_table_get_column_item (cc->priv->ref_pk_table_item, tcol);
 		browser_canvas_text_set_highlight (BROWSER_CANVAS_TEXT (column), TRUE);
+
+		gchar *str;
+		str = g_strdup_printf ("%s '%s'\n%s: %s\n%s: %s",
+				       GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (cc->priv->fk) ? 
+				       _("Declared foreign key") : _("Foreign key"),
+				       cc->priv->fk->fk_name,
+				       _("Policy on UPDATE"),
+				       tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY (cc->priv->fk)),
+				       _("Policy on DELETE"),
+				       tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY (cc->priv->fk)));
+		gtk_widget_set_tooltip_text (GTK_WIDGET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (ci))),
+					     str);
+		g_free (str);
 	}
 
 	return FALSE;
@@ -440,26 +461,49 @@ single_item_leave_notify_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSE
 	return FALSE;
 }
 
+static void
+delete_declared_fk_cb (GtkMenuItem *mitem, BrowserCanvasFkey *cc)
+{
+	GError *error = NULL;
+	GtkWidget *parent;
+	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (cc)));
+	if (! fk_declare_undeclare (cc->priv->mstruct,
+				    BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+				    cc->priv->fk, &error)) {
+		browser_show_error ((GtkWindow *) parent, _("Failed to undeclare foreign key: %s"),
+				    error && error->message ? error->message : _("No detail"));
+		g_clear_error (&error);
+	}
+	else if (BROWSER_IS_WINDOW (parent))
+		browser_window_show_notice (BROWSER_WINDOW (parent),
+					    GTK_MESSAGE_INFO, "fkdeclare",
+					    _("Successfully undeclared foreign key"));
+	else
+		browser_show_message ((GtkWindow *) parent, "%s",
+				      _("Successfully undeclared foreign key"));
+}
+
 static gboolean
 single_item_button_press_event_cb (G_GNUC_UNUSED GooCanvasItem *ci, G_GNUC_UNUSED GooCanvasItem *target_item,
-				   G_GNUC_UNUSED GdkEventButton *event, G_GNUC_UNUSED BrowserCanvasFkey *cc)
+				   G_GNUC_UNUSED GdkEventButton *event, BrowserCanvasFkey *cc)
 {
-	return FALSE;
-	/*
-	GdaMetaTableForeignKey *fkcons = g_object_get_data (G_OBJECT (ci), "fkcons");
-	GtkWidget *menu, *entry;
-
-	menu = gtk_menu_new ();
-	entry = gtk_menu_item_new_with_label (_("Remove"));
-	g_object_set_data (G_OBJECT (entry), "fkcons", fkcons);
-	g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (popup_delete_cb), cc);
-	gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
-	gtk_widget_show (entry);
-	gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-			NULL, NULL, ((GdkEventButton *)event)->button,
-			((GdkEventButton *)event)->time);
-	return TRUE;
-	*/
+	GdaMetaTableForeignKey *fk = g_object_get_data (G_OBJECT (ci), "fkcons");
+	if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk)) {
+		GtkWidget *menu, *entry;
+		
+		menu = gtk_menu_new ();
+		entry = gtk_menu_item_new_with_label (_("Remove this declared foreign key"));
+		g_object_set_data (G_OBJECT (entry), "fkcons", fk);
+		g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (delete_declared_fk_cb), cc);
+		gtk_menu_shell_append (GTK_MENU_SHELL (menu), entry);
+		gtk_widget_show (entry);
+		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+				NULL, NULL, ((GdkEventButton *)event)->button,
+				((GdkEventButton *)event)->time);
+		return TRUE;
+	}
+	else
+		return FALSE;
 }
 
 static void
diff --git a/tools/browser/canvas/browser-canvas.c b/tools/browser/canvas/browser-canvas.c
index fbd0353..5006407 100644
--- a/tools/browser/canvas/browser-canvas.c
+++ b/tools/browser/canvas/browser-canvas.c
@@ -25,9 +25,6 @@
 #include "browser-canvas-item.h"
 #include "browser-canvas-print.h"
 #include <libgda/libgda.h>
-#ifndef CANVAS_EXAMPLE
-#include "../support.h"
-#endif
 
 #define DEFAULT_SCALE .8
 #ifdef HAVE_GRAPHVIZ
diff --git a/tools/browser/common/Makefile.am b/tools/browser/common/Makefile.am
index e36d61c..147883e 100644
--- a/tools/browser/common/Makefile.am
+++ b/tools/browser/common/Makefile.am
@@ -24,7 +24,9 @@ libcommon_la_SOURCES = \
 	gdaui-entry-import.c \
 	gdaui-entry-import.h \
 	ui-formgrid.c \
-	ui-formgrid.h
+	ui-formgrid.h \
+	fk-declare.c \
+	fk-declare.h
 
 $(OBJECTS): marshal.c marshal.h
 
diff --git a/tools/browser/common/fk-declare.c b/tools/browser/common/fk-declare.c
new file mode 100644
index 0000000..917dea7
--- /dev/null
+++ b/tools/browser/common/fk-declare.c
@@ -0,0 +1,652 @@
+/* 
+ * Copyright (C) 2011 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <sql-parser/gda-sql-parser.h>
+#include "fk-declare.h"
+#include "../support.h"
+
+/* 
+ * Main static functions 
+ */
+static void fk_declare_class_init (FkDeclareClass * class);
+static void fk_declare_init (FkDeclare *declare);
+static void fk_declare_dispose (GObject *object);
+
+enum {
+	MODEL_COLUMNS_COLUMN_PIXBUF,
+	MODEL_COLUMNS_COLUMN_STRING,
+	MODEL_COLUMNS_COLUMN_META_COLUMN,
+	MODEL_COLUMNS_COLUMN_LAST
+};
+
+enum {
+	MODEL_TABLES_COLUMN_PIXBUF,
+	MODEL_TABLES_COLUMN_STRING,
+	MODEL_TABLES_COLUMN_META_TABLE,
+	MODEL_TABLES_COLUMN_LAST
+};
+static GtkTreeModel *create_tables_model (GdaMetaStruct *mstruct);
+static void update_reference_column_choices (FkDeclare *dec);
+static void update_dialog_response_sensitiveness (FkDeclare *dec);
+
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+typedef struct {
+	GtkWidget *checkbox; /* column name chackbox */
+	GtkComboBox *cbox; /* referenced column combo box */
+	GdaMetaTableColumn *column;
+} Assoc;
+
+struct _FkDeclarePrivate
+{
+	GdaMetaStruct *mstruct;
+	GdaMetaTable *mtable;
+	GtkWidget *fk_name;
+	GtkComboBox *ref_table_cbox;
+	gint n_cols; /* length (@mtable->columns) */
+	Assoc *associations; /* size is @n_cols */
+	
+	gboolean dialog_sensitive;
+};
+
+GType
+fk_declare_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (FkDeclareClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) fk_declare_class_init,
+			NULL,
+			NULL,
+			sizeof (FkDeclare),
+			0,
+			(GInstanceInitFunc) fk_declare_init,
+			0
+		};
+		
+		g_static_mutex_lock (&registering);
+		if (type == 0)
+			type = g_type_register_static (GTK_TYPE_DIALOG, "FkDeclare", &info, 0);
+		g_static_mutex_unlock (&registering);
+	}
+
+	return type;
+}
+
+
+static void
+fk_declare_class_init (FkDeclareClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	parent_class = g_type_class_peek_parent (class);
+
+	/* virtual functions */
+	object_class->dispose = fk_declare_dispose;
+}
+
+#ifdef HAVE_GDU
+static void
+help_clicked_cb (GtkButton *button, G_GNUC_UNUSED FkDeclare *declare)
+{
+	browser_show_help ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) button),
+			   "declared-fk");
+	g_signal_stop_emission_by_name (button, "clicked");
+}
+#endif
+
+static void
+fk_declare_init (FkDeclare *declare)
+{
+	declare->priv = g_new0 (FkDeclarePrivate, 1);
+	declare->priv->dialog_sensitive = FALSE;
+
+	gtk_dialog_add_buttons (GTK_DIALOG (declare),
+				GTK_STOCK_ADD,
+				GTK_RESPONSE_ACCEPT,
+				GTK_STOCK_CANCEL,
+				GTK_RESPONSE_REJECT,
+				NULL);
+#ifdef HAVE_GDU
+	GtkWidget *help_btn;
+	help_btn = gtk_button_new_from_stock (GTK_STOCK_HELP);
+	g_signal_connect (help_btn, "clicked",
+			  G_CALLBACK (help_clicked_cb), declare);
+	gtk_widget_show (help_btn);
+	gtk_dialog_add_action_widget (GTK_DIALOG (declare), help_btn, GTK_RESPONSE_HELP);
+#endif
+
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (declare), GTK_RESPONSE_ACCEPT, FALSE);
+}
+
+static void
+fk_declare_dispose (GObject *object)
+{
+	FkDeclare *declare;
+	declare = FK_DECLARE (object);
+	if (declare->priv) {
+		g_free (declare->priv->associations);
+		if (declare->priv->mstruct)
+			g_object_unref (declare->priv->mstruct);
+		
+		g_free (declare->priv);
+		declare->priv = NULL;
+	}
+
+	/* parent class */
+        parent_class->dispose (object);
+}
+
+static void is_node_sensitive (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model,
+			       GtkTreeIter *iter, gpointer data);
+static void fk_name_changed_cb (GtkWidget *entry, FkDeclare *decl);
+static void table_selection_changed_cb (GtkComboBox *cbox, FkDeclare *decl);
+static void column_toggled_cb (GtkToggleButton *toggle, FkDeclare *decl);
+static void column_selection_changed_cb (GtkComboBox *cbox, FkDeclare *decl);
+
+static void
+create_internal_layout (FkDeclare *decl)
+{
+	GtkWidget *label, *table, *cbox, *entry;
+	char *markup, *str;
+	GtkWidget *dcontents;
+	gint i, nrows;
+	GSList *list;
+
+#if GTK_CHECK_VERSION(2,18,0)
+	dcontents = gtk_dialog_get_content_area (GTK_DIALOG (decl));
+#else
+	dcontents = GTK_DIALOG (decl)->vbox;
+#endif
+	gtk_box_set_spacing (GTK_BOX (dcontents), 5);
+
+	/* label */
+	label = gtk_label_new ("");
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	str = g_strdup_printf (_("Declare a foreign key for table '%s'"),
+			       GDA_META_DB_OBJECT (decl->priv->mtable)->obj_short_name);
+	markup = g_markup_printf_escaped ("<big><b>%s:</b></big>\n%s", str,
+					  _("define which table is references, which columns are "
+					    "part of the foreign key, "
+					    "and which column each one references"));
+	g_free (str);
+					  
+	gtk_label_set_markup (GTK_LABEL (label), markup);
+	g_free (markup);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_box_pack_start (GTK_BOX (dcontents), label, FALSE, FALSE, 0);
+	gtk_widget_show_all (label);
+
+	/* GtkTable to hold contents */
+	nrows = g_slist_length (decl->priv->mtable->columns);
+	table = gtk_table_new (nrows + 3, 2, FALSE);
+	gtk_box_pack_start (GTK_BOX (dcontents), table, TRUE, TRUE, 0);
+	gtk_table_set_col_spacing (GTK_TABLE (table), 0, 5);
+	gtk_table_set_row_spacing (GTK_TABLE (table), 1, 5);
+
+	/* FK name */
+	gfloat yalign;
+	label = gtk_label_new (_("Foreign key name:"));
+	gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+	entry = gtk_entry_new ();
+	decl->priv->fk_name = entry;
+	gtk_table_attach_defaults (GTK_TABLE (table), entry, 1, 2, 0, 1);
+	g_signal_connect (entry, "changed",
+			  G_CALLBACK (fk_name_changed_cb), decl);
+
+	/* table to reference */
+	label = gtk_label_new (_("Referenced table:"));
+	gtk_misc_get_alignment (GTK_MISC (label), NULL, &yalign);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., yalign);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+
+	GtkTreeModel *model;
+	GtkCellRenderer *renderer;
+	model = create_tables_model (decl->priv->mstruct);
+	cbox = gtk_combo_box_new_with_model (model);
+	decl->priv->ref_table_cbox = GTK_COMBO_BOX (cbox);
+	g_signal_connect (cbox, "changed",
+			  G_CALLBACK (table_selection_changed_cb), decl);
+	g_object_unref (G_OBJECT (model));
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, FALSE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
+					"pixbuf", MODEL_TABLES_COLUMN_PIXBUF,
+					NULL);
+	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cbox),
+					    renderer,
+					    is_node_sensitive,
+					    NULL, NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, TRUE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
+					"text", MODEL_TABLES_COLUMN_STRING,
+					NULL);
+	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cbox),
+					    renderer,
+					    is_node_sensitive,
+					    NULL, NULL);
+	gtk_table_attach_defaults (GTK_TABLE (table), cbox, 1, 2, 1, 2);
+
+	/* more labels */
+	label = gtk_label_new ("");
+	markup = g_strdup_printf ("<b>%s:</b>", _("Columns"));
+	gtk_label_set_markup (GTK_LABEL (label), markup);
+	g_free (markup);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+
+	label = gtk_label_new ("");
+	markup = g_strdup_printf ("<b>%s:</b>", _("Referenced column"));
+	gtk_label_set_markup (GTK_LABEL (label), markup);
+	g_free (markup);
+	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
+	gtk_table_attach (GTK_TABLE (table), label, 1, 2, 2, 3, GTK_FILL, 0, 0, 0);
+
+	/* columns */
+	decl->priv->n_cols = g_slist_length (decl->priv->mtable->columns);
+	decl->priv->associations = g_new0 (Assoc, decl->priv->n_cols);
+	for (i = 3, list = decl->priv->mtable->columns; list; i++, list = list->next) {
+		GtkCellRenderer *renderer;
+		GdaMetaTableColumn *column = (GdaMetaTableColumn*) list->data;
+		Assoc *assoc = &(decl->priv->associations [i-3]);
+		assoc->column = column;
+
+		label = gtk_check_button_new_with_label (column->column_name);
+		gtk_table_attach (GTK_TABLE (table), label, 0, 1, i, i+1, GTK_FILL, 0, 0, 0);
+		assoc->checkbox = label;
+		g_signal_connect (label, "toggled",
+				  G_CALLBACK (column_toggled_cb), decl);
+
+		cbox = gtk_combo_box_new ();
+		g_object_set_data (G_OBJECT (label), "cbox", cbox);
+		renderer = gtk_cell_renderer_pixbuf_new ();
+		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, FALSE);
+		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
+						"pixbuf", MODEL_COLUMNS_COLUMN_PIXBUF,
+						NULL);		
+		renderer = gtk_cell_renderer_text_new ();
+		gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cbox), renderer, TRUE);
+		gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cbox), renderer,
+						"text", MODEL_COLUMNS_COLUMN_STRING,
+						NULL);
+		gtk_table_attach_defaults (GTK_TABLE (table), cbox, 1, 2, i, i+1);
+		assoc->cbox = GTK_COMBO_BOX (cbox);
+		g_signal_connect (cbox, "changed",
+				  G_CALLBACK (column_selection_changed_cb), decl);
+		gtk_widget_set_sensitive (cbox, FALSE);
+	}
+	gtk_widget_show_all (table);
+}
+
+static void
+is_node_sensitive (G_GNUC_UNUSED GtkCellLayout *cell_layout,
+		   GtkCellRenderer *cell, GtkTreeModel *tree_model,
+		   GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
+{
+	gboolean sensitive;
+	sensitive = !gtk_tree_model_iter_has_child (tree_model, iter);
+	g_object_set (cell, "sensitive", sensitive, NULL);
+}
+
+static void
+fk_name_changed_cb (G_GNUC_UNUSED GtkWidget *entry, FkDeclare *decl)
+{
+	update_dialog_response_sensitiveness (decl);
+}
+
+static void
+table_selection_changed_cb (G_GNUC_UNUSED GtkComboBox *cbox, FkDeclare *decl)
+{
+	update_reference_column_choices (decl);
+	update_dialog_response_sensitiveness (decl);
+}
+
+static void
+column_toggled_cb (GtkToggleButton *toggle, FkDeclare *decl)
+{
+	GtkWidget *cbox;
+	cbox = g_object_get_data (G_OBJECT (toggle), "cbox");
+	gtk_widget_set_sensitive (cbox, gtk_toggle_button_get_active (toggle));
+	update_dialog_response_sensitiveness (decl);
+}
+
+static void
+column_selection_changed_cb (G_GNUC_UNUSED GtkComboBox *cbox, FkDeclare *decl)
+{
+	update_dialog_response_sensitiveness (decl);
+}
+
+static void
+update_reference_column_choices (FkDeclare *decl)
+{
+	gint i;
+	GtkTreeIter iter;
+	GdaMetaTable *mtable = NULL;
+	
+	if (gtk_combo_box_get_active_iter (decl->priv->ref_table_cbox, &iter))
+		gtk_tree_model_get (gtk_combo_box_get_model (decl->priv->ref_table_cbox), &iter,
+				    MODEL_TABLES_COLUMN_META_TABLE, &mtable, -1);
+
+	for (i = 0; i < decl->priv->n_cols; i++) {
+		Assoc *assoc = &(decl->priv->associations [i]);
+		GtkListStore *lstore;
+		lstore = (GtkListStore*) gtk_combo_box_get_model (assoc->cbox);
+		if (! lstore) {
+			lstore = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
+			gtk_combo_box_set_model (assoc->cbox, GTK_TREE_MODEL (lstore));
+			g_object_unref (lstore);
+		}
+		else
+			gtk_list_store_clear (lstore);
+
+		if (!mtable)
+			continue;
+
+		/* add columns */
+		GSList *list;
+		for (list = mtable->columns; list; list = list->next) {
+			GdaMetaTableColumn *col = (GdaMetaTableColumn*) list->data;
+			GdkPixbuf *pix;
+			gtk_list_store_append (lstore, &iter);
+			pix = browser_get_pixbuf_icon (BROWSER_ICON_COLUMN);
+			gtk_list_store_set (lstore, &iter,
+					    MODEL_COLUMNS_COLUMN_PIXBUF, pix,
+					    MODEL_COLUMNS_COLUMN_STRING, col->column_name,
+					    MODEL_COLUMNS_COLUMN_META_COLUMN, col, -1);
+		}
+	}
+}
+
+/**
+ * fk_declare_new
+ */
+GtkWidget  *
+fk_declare_new (GtkWindow *parent, GdaMetaStruct *mstruct, GdaMetaTable *table)
+{
+	GtkWidget *wid;
+	FkDeclare *decl;
+	gchar *str;
+
+	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
+	g_return_val_if_fail (table, NULL);
+	g_return_val_if_fail (GDA_META_DB_OBJECT (table)->obj_type == GDA_META_DB_TABLE, NULL);
+	g_return_val_if_fail (table->columns, NULL);
+
+	str = g_strdup_printf (_("Declare a foreign key for table '%s'"),
+			       GDA_META_DB_OBJECT (table)->obj_short_name);
+	wid = (GtkWidget*) g_object_new (FK_DECLARE_TYPE, "title", str,
+					 "transient-for", parent,
+					 "border-width", 10, 
+					 "has-separator", FALSE, NULL);
+	g_free (str);
+
+	decl = FK_DECLARE (wid);
+	decl->priv->mstruct = g_object_ref ((GObject*) mstruct);
+	decl->priv->mtable = table;
+
+	create_internal_layout (decl);
+
+	return wid;
+}
+
+static GtkTreeModel *
+create_tables_model (GdaMetaStruct *mstruct)
+{
+	GtkTreeStore *tstore;
+	GSList *all_dbo, *list;
+	GHashTable *schemas = NULL; /* key = schema name, value = a #GtkTreeRowReference as parent */
+
+	tstore = gtk_tree_store_new (MODEL_TABLES_COLUMN_LAST, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
+	schemas = g_hash_table_new_full (g_str_hash, g_str_equal,
+					 NULL, (GDestroyNotify) gtk_tree_row_reference_free);
+	all_dbo = gda_meta_struct_get_all_db_objects (mstruct);
+	for (list = all_dbo; list; list = list->next) {
+		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
+		GtkTreeIter iter;
+		GdkPixbuf *pix;
+		if (dbo->obj_type != GDA_META_DB_TABLE)
+			continue;
+		
+		if (strcmp (dbo->obj_short_name, dbo->obj_full_name)) {
+			gtk_tree_store_prepend (tstore, &iter, NULL);
+			pix = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
+			gtk_tree_store_set (tstore, &iter,
+					    MODEL_TABLES_COLUMN_PIXBUF, pix,
+					    MODEL_TABLES_COLUMN_STRING, dbo->obj_short_name,
+					    MODEL_TABLES_COLUMN_META_TABLE, GDA_META_TABLE (dbo), -1);
+		}
+
+		GtkTreePath *path;
+		GtkTreeRowReference *rref;
+		GtkTreeIter parent;
+		rref = g_hash_table_lookup (schemas, dbo->obj_schema);
+		if (!rref) {
+			GtkTreeIter iter;
+			GtkTreePath *path;
+			GdkPixbuf *pix;
+			gtk_tree_store_append (tstore, &iter, NULL);
+			pix = browser_get_pixbuf_icon (BROWSER_ICON_SCHEMA);
+			gtk_tree_store_set (tstore, &iter,
+					    MODEL_TABLES_COLUMN_PIXBUF, pix,
+					    MODEL_TABLES_COLUMN_STRING, dbo->obj_schema,
+					    MODEL_TABLES_COLUMN_META_TABLE, NULL, -1);
+			path = gtk_tree_model_get_path ((GtkTreeModel*) tstore, &iter);
+			rref = gtk_tree_row_reference_new ((GtkTreeModel*) tstore, path);
+			gtk_tree_path_free (path);
+			g_hash_table_insert (schemas, dbo->obj_schema, rref);
+		}
+		
+		path = gtk_tree_row_reference_get_path (rref);
+		g_assert (gtk_tree_model_get_iter ((GtkTreeModel*) tstore, &parent, path));
+		gtk_tree_path_free (path);
+		gtk_tree_store_prepend (tstore, &iter, &parent);
+		pix = browser_get_pixbuf_icon (BROWSER_ICON_TABLE);
+		gtk_tree_store_set (tstore, &iter,
+				    MODEL_TABLES_COLUMN_PIXBUF, pix,
+				    MODEL_TABLES_COLUMN_STRING, dbo->obj_short_name,
+				    MODEL_TABLES_COLUMN_META_TABLE, GDA_META_TABLE (dbo), -1);
+	}
+	g_slist_free (all_dbo);
+	g_hash_table_destroy (schemas);
+
+	return GTK_TREE_MODEL (tstore);
+}
+
+/*
+ * Sets the dialog's sensitiveness for the GTK_RESPONSE_ACCEPT response
+ *
+ * It is sensitive if:
+ *  - the FK is named
+ *  - a reference table is selected
+ *  - at least one column is checked
+ *  - for each checked column, there is a selected reference column
+ */
+static void
+update_dialog_response_sensitiveness (FkDeclare *decl)
+{
+	gboolean sensitive = FALSE;
+	gint i;
+	gboolean onechecked = FALSE;
+	const gchar *fkname;
+
+	fkname = gtk_entry_get_text (GTK_ENTRY (decl->priv->fk_name));
+	if (!fkname || !*fkname)
+		goto out;
+
+	if (gtk_combo_box_get_active (decl->priv->ref_table_cbox) == -1)
+		goto out;
+
+	sensitive = TRUE;
+	for (i = 0; i < decl->priv->n_cols; i++) {
+		Assoc *assoc = &(decl->priv->associations [i]);
+		if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (assoc->checkbox)))
+			continue;
+
+		onechecked = TRUE;
+		if (gtk_combo_box_get_active (assoc->cbox) == -1)
+			sensitive = FALSE;
+	}
+
+	if (! onechecked) {
+		sensitive = FALSE;
+		goto out;
+	}
+
+ out:
+	decl->priv->dialog_sensitive = sensitive;
+	gtk_dialog_set_response_sensitive (GTK_DIALOG (decl), GTK_RESPONSE_ACCEPT, sensitive);
+}
+
+/**
+ * fk_declare_write
+ * @decl: a #FkDeclare widget
+ * @bwin: a #BrowserWindow, or %NULL
+ * @error: a place to store errors or %NULL
+ *
+ * Actually declares the new foreign key in the meta store
+ *
+ * Returns: %TRUE if no error occurred
+ */ 
+gboolean
+fk_declare_write (FkDeclare *decl, BrowserWindow *bwin, GError **error)
+{
+	gboolean retval = FALSE;
+
+	g_return_val_if_fail (IS_FK_DECLARE (decl), FALSE);
+	g_return_val_if_fail (!bwin || BROWSER_IS_WINDOW (bwin), FALSE);
+
+	if (! decl->priv->dialog_sensitive) {
+		g_set_error (error, 0, 0,
+			     _("Missing information to declare foreign key"));
+		return FALSE;
+	}
+
+	GdaMetaTable *mtable = NULL;
+	GtkTreeIter iter;
+	g_assert (gtk_combo_box_get_active_iter (decl->priv->ref_table_cbox, &iter));
+	gtk_tree_model_get (gtk_combo_box_get_model (decl->priv->ref_table_cbox), &iter,
+			    MODEL_TABLES_COLUMN_META_TABLE, &mtable, -1);
+
+	GdaMetaStore *mstore;
+	gchar **colnames, **ref_colnames;
+	colnames = g_new0 (gchar *, decl->priv->n_cols);
+	ref_colnames = g_new0 (gchar *, decl->priv->n_cols);
+	gint i, j;
+	for (i = 0, j = 0; i < decl->priv->n_cols; i++) {
+		Assoc *assoc = &(decl->priv->associations [i]);
+		if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (assoc->checkbox)))
+			continue;
+		colnames [j] = assoc->column->column_name;
+		g_assert (gtk_combo_box_get_active_iter (assoc->cbox, &iter));
+		GdaMetaTableColumn *ref_column;
+		gtk_tree_model_get (gtk_combo_box_get_model (assoc->cbox), &iter,
+				    MODEL_TABLES_COLUMN_META_TABLE, &ref_column, -1);
+		g_assert (ref_column);
+		ref_colnames [j] = ref_column->column_name;
+		j++;
+	}
+
+	g_object_get (G_OBJECT (decl->priv->mstruct), "meta-store", &mstore, NULL);
+	retval = gda_meta_store_declare_foreign_key (mstore, NULL,
+						     gtk_entry_get_text (GTK_ENTRY (decl->priv->fk_name)),
+						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_catalog,
+						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_schema,
+						     GDA_META_DB_OBJECT (decl->priv->mtable)->obj_name,
+						     GDA_META_DB_OBJECT (mtable)->obj_catalog,
+						     GDA_META_DB_OBJECT (mtable)->obj_schema,
+						     GDA_META_DB_OBJECT (mtable)->obj_name,
+						     j, colnames, ref_colnames, error);
+	g_free (colnames);
+	g_free (ref_colnames);
+
+	if (retval && bwin) {
+		BrowserConnection *bcnc;
+		bcnc = browser_window_get_connection (bwin);
+		browser_connection_meta_data_changed (bcnc);
+	}
+
+	g_object_unref (mstore);
+	return retval;
+}
+
+/**
+ * fk_declare_undeclare:
+ * @mstruct: the #GdaMEtaStruct to delete the FK from
+ * @bwin: a #BrowserWindow, or %NULL
+ * @decl_fk: the #GdaMetaTableForeignKey fk to delete
+ * @error: a place to store errors, or %NULL
+ *
+ * Deletes a declared FK.
+ *
+ * Returns: %TRUE if no error occurred
+ */
+gboolean
+fk_declare_undeclare (GdaMetaStruct *mstruct, BrowserWindow *bwin, GdaMetaTableForeignKey *decl_fk,
+		      GError **error)
+{
+	gboolean retval = FALSE;
+	GdaMetaStore *mstore;
+
+	g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE);
+	g_return_val_if_fail (!bwin || BROWSER_IS_WINDOW (bwin), FALSE);
+	g_return_val_if_fail (decl_fk, FALSE);
+	if (!decl_fk->meta_table ||
+	    !decl_fk->meta_table->obj_catalog ||
+	    !decl_fk->meta_table->obj_schema ||
+	    !decl_fk->meta_table->obj_name ||
+	    !decl_fk->depend_on ||
+	    !decl_fk->depend_on->obj_catalog ||
+	    !decl_fk->depend_on->obj_schema ||
+	    !decl_fk->depend_on->obj_name) {
+		g_set_error (error, 0, 0,
+			     _("Missing information to undeclare foreign key"));
+		return FALSE;
+	}
+
+	g_object_get (G_OBJECT (mstruct), "meta-store", &mstore, NULL);
+	retval = gda_meta_store_undeclare_foreign_key (mstore, NULL,
+						       decl_fk->fk_name,
+						       decl_fk->meta_table->obj_catalog,
+						       decl_fk->meta_table->obj_schema,
+						       decl_fk->meta_table->obj_name,
+						       decl_fk->depend_on->obj_catalog,
+						       decl_fk->depend_on->obj_schema,
+						       decl_fk->depend_on->obj_name,
+						       error);
+	if (retval && bwin) {
+		BrowserConnection *bcnc;
+		bcnc = browser_window_get_connection (bwin);
+		browser_connection_meta_data_changed (bcnc);
+	}
+
+	g_object_unref (mstore);
+	return retval;
+}
diff --git a/tools/browser/common/fk-declare.h b/tools/browser/common/fk-declare.h
new file mode 100644
index 0000000..ff60eb4
--- /dev/null
+++ b/tools/browser/common/fk-declare.h
@@ -0,0 +1,62 @@
+/* 
+ * Copyright (C) 2011 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __FK_DECLARE_H_
+#define __FK_DECLARE_H_
+
+#include <libgda-ui/libgda-ui.h>
+#include <gtk/gtk.h>
+#include "../browser-window.h"
+
+G_BEGIN_DECLS
+
+#define FK_DECLARE_TYPE          (fk_declare_get_type())
+#define FK_DECLARE(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, fk_declare_get_type(), FkDeclare)
+#define FK_DECLARE_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, fk_declare_get_type (), FkDeclareClass)
+#define IS_FK_DECLARE(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, fk_declare_get_type ())
+
+typedef struct _FkDeclare FkDeclare;
+typedef struct _FkDeclareClass FkDeclareClass;
+typedef struct _FkDeclarePrivate FkDeclarePrivate;
+
+/* struct for the object's data */
+struct _FkDeclare
+{
+	GtkDialog               object;
+	FkDeclarePrivate       *priv;
+};
+
+/* struct for the object's class */
+struct _FkDeclareClass
+{
+	GtkDialogClass          parent_class;
+};
+
+
+GType       fk_declare_get_type (void) G_GNUC_CONST;
+GtkWidget  *fk_declare_new      (GtkWindow *parent, GdaMetaStruct *mstruct, GdaMetaTable *table);
+gboolean    fk_declare_write    (FkDeclare *decl, BrowserWindow *bwin, GError **error);
+
+gboolean    fk_declare_undeclare (GdaMetaStruct *mstruct, BrowserWindow *bwin,
+				  GdaMetaTableForeignKey *decl_fk, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/doc/gda-browser-sections.txt b/tools/browser/doc/gda-browser-sections.txt
index 6eac4cb..9d0cc88 100644
--- a/tools/browser/doc/gda-browser-sections.txt
+++ b/tools/browser/doc/gda-browser-sections.txt
@@ -81,6 +81,7 @@ browser_connection_get_name
 browser_connection_get_information
 browser_connection_is_busy
 browser_connection_update_meta_data
+browser_connection_meta_data_changed
 browser_connection_get_meta_struct
 browser_connection_get_meta_store
 browser_connection_get_dictionary_file
diff --git a/tools/browser/doc/tmpl/browser-connection.sgml b/tools/browser/doc/tmpl/browser-connection.sgml
index 22495c0..c5c352a 100644
--- a/tools/browser/doc/tmpl/browser-connection.sgml
+++ b/tools/browser/doc/tmpl/browser-connection.sgml
@@ -151,6 +151,14 @@ An opened connection
 @bcnc: 
 
 
+<!-- ##### FUNCTION browser_connection_meta_data_changed ##### -->
+<para>
+
+</para>
+
+ bcnc: 
+
+
 <!-- ##### FUNCTION browser_connection_get_meta_struct ##### -->
 <para>
 
diff --git a/tools/browser/help/C/declaredfk.page b/tools/browser/help/C/declaredfk.page
new file mode 100644
index 0000000..fde32e2
--- /dev/null
+++ b/tools/browser/help/C/declaredfk.page
@@ -0,0 +1,82 @@
+<page xmlns="http://projectmallard.org/1.0/";
+      type="topic"
+      id="declared-fk">
+  <info>
+    <title type="sort">1</title>
+    <link type="topic" xref="schema-browser-perspective"/>
+  </info>
+  <title>Declared foreign keys</title>
+  <p>
+    All the foreign key constraints (where the contents of one or more columns in a table
+    is constrained to be among the values of one or more columns of another table) are
+    analysed and reported in the <link xref="schema-browser-perspective">Schema Browser perspective</link>.
+    Foreign key constraints help understand the database schema and is automatically used where appropriate
+    by the application.
+  </p>
+  <p>
+    However sometimes the designer of a database has forgotten or did not whish to use foreign
+    key constraints, and so it is possible to "declare foreign keys" which are only declaration to
+    the tool and not actually defined in the database.
+  </p>
+  <figure>
+    <title>Declared foreign keys in diagrams</title>
+    <desc>When displayed in diagrams, declared foreign keys are represented using a dashed line instead of a
+    solid line for actual foreign keys</desc>
+    <media type="image" mime="image/png" src="figures/declaredfk.png"/>
+  </figure>
+  <p>
+    Notes:
+  </p>
+  <list>
+    <item><p>the (foreign key name, table, referenced table) triplet uniquely identifies a declared
+    foreign key and thus declaring a new foreign key with the same triplet will remove any previously
+    declared one.</p></item>
+    <item><p>If the same constraint is actually represented by a real foreign key and also
+	by a declared foreign key, then the real foreign key definition will mask the
+	declared one</p></item>
+    <item><p>Declared foreign key don't have any policy to determine what action to perform on
+	UPDATE or DELETE situations, so the reported policy will always be "not enforced"</p></item>
+  </list>
+
+  <section>
+    <title>Declaring a new foreign key</title>
+    <p>
+      A new foreign key can be declared from the
+      <link xref="schema-browser-perspective">Schema Browser perspective</link> using the
+      <guiseq><gui>Table</gui><gui>Declare foreign key</gui></guiseq> menu which is present when
+      a table's properties are shown in the current tab.
+    </p>
+    <p>
+      Another way of declaring a foreign key is using the contextual menu on a table in a diagram, and
+      selecting the <guiseq><gui>Declare foreign key for this table</gui></guiseq> option.
+    </p>
+    <p>
+      In any way, to declare a new foreign key, the following information must be provided:
+    </p>
+    <list>
+      <item><p>A foreign key name</p></item>
+      <item><p>The referenced table</p></item>
+      <item><p>Columns and referenced columns for each column involved in the foreign key</p></item>
+    </list>
+    <figure>
+      <title>Information to declare a new foreign key</title>
+      <desc>UI dialog poping up to declare a new foreign key. Here the referenced table is 'warehouses', and
+      the involved columns are 'country' and 'city'.</desc>
+      <media type="image" mime="image/png" src="figures/declaredfk-dialog.png"/>
+    </figure>
+  </section>
+  
+  <section>
+    <title>Remove a declared foreign key</title>
+    <p>
+      A declared foreign key can be removed by clicking on the <guiseq><gui>(Remove)</gui></guiseq> link
+      displayed below each declared foreign key properties in the table's properties (in the
+      <link xref="schema-browser-perspective">Schema Browser perspective</link>). 
+    </p>
+    <p>
+      Another way to remobe a declared foreign key is in a <link xref="diagram">diagram</link> page through
+      the contextual menu associated with the dashed line representing the declared foreign key, selecting
+      the <guiseq><gui>Remove this declared foreign key</gui></guiseq> option.
+    </p>
+  </section>
+</page>
diff --git a/tools/browser/help/C/diagram.page b/tools/browser/help/C/diagram.page
index 02c9b59..ede9409 100644
--- a/tools/browser/help/C/diagram.page
+++ b/tools/browser/help/C/diagram.page
@@ -11,7 +11,8 @@
   </p>
   <figure>
     <title>Diagram</title>
-    <desc>Diagram showing some tables and their relations</desc>
+    <desc>Diagram showing some tables and their relations represented by foreign key constraints (also see 
+    <link xref="declared-fk">the declared foreign keys</link>)</desc>
     <media type="image" mime="image/png" src="figures/diagram.png"/>
   </figure>
   <p>
diff --git a/tools/browser/help/C/figures/declaredfk-dialog.png b/tools/browser/help/C/figures/declaredfk-dialog.png
new file mode 100644
index 0000000..3ed3414
Binary files /dev/null and b/tools/browser/help/C/figures/declaredfk-dialog.png differ
diff --git a/tools/browser/help/C/figures/declaredfk.png b/tools/browser/help/C/figures/declaredfk.png
new file mode 100644
index 0000000..5cde969
Binary files /dev/null and b/tools/browser/help/C/figures/declaredfk.png differ
diff --git a/tools/browser/help/C/figures/mainwin.png b/tools/browser/help/C/figures/mainwin.png
index 9328e64..1dd58cb 100644
Binary files a/tools/browser/help/C/figures/mainwin.png and b/tools/browser/help/C/figures/mainwin.png differ
diff --git a/tools/browser/help/C/figures/schema-browser-persp.png b/tools/browser/help/C/figures/schema-browser-persp.png
new file mode 100644
index 0000000..4b9d5ae
Binary files /dev/null and b/tools/browser/help/C/figures/schema-browser-persp.png differ
diff --git a/tools/browser/help/C/schema-browser-perspective.page b/tools/browser/help/C/schema-browser-perspective.page
index c77dab7..a9a97b2 100644
--- a/tools/browser/help/C/schema-browser-perspective.page
+++ b/tools/browser/help/C/schema-browser-perspective.page
@@ -40,4 +40,11 @@
       linked using their relations (as foreign key constraints)</p></item>
 </list>
 
+<figure>
+  <title>Tab showing information about a table</title>
+  <desc>Example of information displayed for each table, note the difference between foreign keys implemented in
+  the database and <link xref="declared-fk">declared foreign keys</link></desc>
+  <media type="image" mime="image/png" src="figures/schema-browser-persp.png"/>
+</figure>
+
 </page>
diff --git a/tools/browser/help/Makefile.am b/tools/browser/help/Makefile.am
index 143f266..647aa10 100644
--- a/tools/browser/help/Makefile.am
+++ b/tools/browser/help/Makefile.am
@@ -19,13 +19,17 @@ DOC_FIGURES = \
 	figures/trans-begin.png \
 	figures/trans-commit.png \
 	figures/trans-rollback.png \
-	figures/table-insert-data.png
+	figures/table-insert-data.png \
+	figures/declaredfk.png \
+	figures/declaredfk-dialog.png \
+	figures/schema-browser-persp.png
 
 DOC_PAGES = \
 	actions.page \
 	data-manager-perspective.page \
 	data-manager-xml-syntax.page \
 	data-manager-source-editor.page \
+	declaredfk.page \
 	diagram.page \
 	features.page \
 	general-ui.page \
diff --git a/tools/browser/schema-browser/table-columns.c b/tools/browser/schema-browser/table-columns.c
index 560bf62..60e98b5 100644
--- a/tools/browser/schema-browser/table-columns.c
+++ b/tools/browser/schema-browser/table-columns.c
@@ -28,10 +28,13 @@
 #include "table-info.h"
 #include "table-columns.h"
 #include <libgda-ui/gdaui-tree-store.h>
+#include "../tools-utils.h"
 #include "../support.h"
 #include "../cc-gray-bar.h"
 #include "mgr-columns.h"
 #include "schema-browser-perspective.h"
+#include "../browser-window.h"
+#include "../common/fk-declare.h"
 
 struct _TableColumnsPrivate {
 	BrowserConnection *bcnc;
@@ -146,7 +149,7 @@ static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, Table
 static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, TableColumns *tcolumns);
 static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, TableColumns *tcolumns);
 static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, TableColumns *tcolumns);
-static const gchar *fk_policy_to_string (GdaMetaForeignKeyPolicy policy);
+static GSList *build_reverse_depend_list (GdaMetaStruct *mstruct, GdaMetaTable *mtable);
 
 static void
 meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct, TableColumns *tcolumns)
@@ -209,6 +212,7 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 				GdaMetaTableForeignKey *fk = GDA_META_TABLE_FOREIGN_KEY (list->data);
 				GdaMetaDbObject *fdbo = fk->depend_on;
 				gint i;
+				gchar *str;
 				
 				if (fdbo->obj_type == GDA_META_DB_UNKNOWN) {
 					GValue *v1, *v2, *v3;
@@ -225,10 +229,16 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 					gda_value_free (v3);
 					continue;
 				}
+				if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk))
+					str = g_strdup_printf (_("Declared foreign key '%s' on "),
+							       fk->fk_name);
+				else
+					str = g_strdup_printf (_("Foreign key '%s' on "),
+							       fk->fk_name);
 				gtk_text_buffer_insert_with_tags_by_name (tbuffer,
-									  &current, 
-									  _("Foreign key on "), -1,
+									  &current, str, -1,
 									  "section", NULL);
+				g_free (str);
 				if (fdbo->obj_type == GDA_META_DB_TABLE) {
 					/* insert link to table name */
 					GtkTextTag *tag;
@@ -245,6 +255,20 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 								g_strdup (fdbo->obj_short_name), g_free);
 					gtk_text_buffer_insert_with_tags (tbuffer, &current,
 									  fdbo->obj_short_name, -1, tag, NULL);
+					if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk)) {
+						tag = gtk_text_buffer_create_tag (tbuffer, NULL, 
+										  "foreground", "blue", 
+										  "underline",
+										  PANGO_UNDERLINE_SINGLE, 
+										  NULL);
+						g_object_set_data_full (G_OBJECT (tag), "fk_name", 
+									g_strdup (fk->fk_name),
+									g_free);
+						gtk_text_buffer_insert (tbuffer, &current, "\n(", -1);
+						gtk_text_buffer_insert_with_tags (tbuffer, &current,
+										  _("Remove"), -1, tag, NULL);
+						gtk_text_buffer_insert (tbuffer, &current, ")", -1);
+					}					
 				}
 				else
 					gtk_text_buffer_insert_with_tags_by_name (tbuffer,
@@ -297,7 +321,8 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 					gtk_text_buffer_insert (tbuffer, &current, _("Policy on UPDATE"), -1);
 					gtk_text_buffer_insert (tbuffer, &current, ": ", -1);
 					gtk_text_buffer_insert (tbuffer, &current,
-								fk_policy_to_string (policy), -1);
+								tools_utils_fk_policy_to_string (policy),
+								-1);
 				}
 				policy = GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY (fk);
 				if (policy != GDA_META_FOREIGN_KEY_UNKNOWN) {
@@ -306,7 +331,8 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 					gtk_text_buffer_insert (tbuffer, &current, _("Policy on DELETE"), -1);
 					gtk_text_buffer_insert (tbuffer, &current, ": ", -1);
 					gtk_text_buffer_insert (tbuffer, &current,
-								fk_policy_to_string (policy), -1);
+								tools_utils_fk_policy_to_string (policy),
+								-1);
 				}
 				
 				gtk_text_buffer_insert (tbuffer, &current, "\n\n", -1);
@@ -362,38 +388,18 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 			}
 
 			/* reverse FK constraints */
-			g_value_set_string ((catalog_v = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
-			model = gda_meta_store_extract (browser_connection_get_meta_store (tcolumns->priv->bcnc),
-							"SELECT c.table_schema, c.table_name, t.table_short_name FROM _referential_constraints c NATURAL JOIN _tables t WHERE ref_table_catalog = ##catalog::string AND ref_table_schema=##schema::string AND ref_table_name=##tname::string", &error,
-							"catalog", catalog_v,
-							"schema", schema_v,
-							"tname", name_v, NULL);
-			if (model) {
-				gint nrows;
-				
-				/*gda_data_model_dump (model, NULL);*/
-				nrows = gda_data_model_get_n_rows (model);
-				if (nrows > 0) {
-					gtk_text_buffer_insert_with_tags_by_name (tbuffer,
-										  &current, 
-										  _("Tables referencing this one"), -1,
-										  "section", NULL);
-					gtk_text_buffer_insert (tbuffer, &current, "\n", -1);
-
-					gint i;
-					const GValue *cvalue[3];
-					for (i = 0; i < nrows; i++) {
-						if (i > 0)
-							gtk_text_buffer_insert (tbuffer, &current, "\n", -1);
-
-						gint j;
-						for (j = 0; j < 3; j++) {
-							cvalue[j] = gda_data_model_get_value_at (model, j, i, NULL);
-							if (! cvalue[j])
-								break;
-						}
-						if (j != 3)
-							break;
+			GSList *rev_list;
+			rev_list = build_reverse_depend_list (mstruct, mtable);
+			if (rev_list) {
+				gtk_text_buffer_insert_with_tags_by_name (tbuffer,
+									  &current, 
+									  _("Tables referencing this one"), -1,
+									  "section", NULL);
+				for (list = rev_list; list; list = list->next) {
+					GdaMetaDbObject *ddbo;
+					ddbo = GDA_META_DB_OBJECT (list->data);
+					if (ddbo->obj_type == GDA_META_DB_TABLE) {
+						gtk_text_buffer_insert (tbuffer, &current, "\n", -1);
 						GtkTextTag *tag;
 						tag = gtk_text_buffer_create_tag (tbuffer, NULL, 
 										  "foreground", "blue", 
@@ -401,16 +407,18 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 										  "underline", PANGO_UNDERLINE_SINGLE, 
 										  NULL);
 						g_object_set_data_full (G_OBJECT (tag), "table_schema", 
-									g_value_dup_string (cvalue[0]), g_free);
+									g_strdup (ddbo->obj_schema), g_free);
 						g_object_set_data_full (G_OBJECT (tag), "table_name", 
-									g_value_dup_string (cvalue[1]), g_free);
+									g_strdup (ddbo->obj_name), g_free);
 						g_object_set_data_full (G_OBJECT (tag), "table_short_name", 
-									g_value_dup_string (cvalue[2]), g_free);
+									g_strdup (ddbo->obj_short_name), g_free);
 						gtk_text_buffer_insert_with_tags (tbuffer, &current,
-										  g_value_get_string (cvalue[2]), -1, tag, NULL);
+										  ddbo->obj_short_name,
+										  -1, tag, NULL);
 					}
 				}
-				g_object_unref (model);
+				g_slist_free (rev_list);
+				gtk_text_buffer_insert (tbuffer, &current, "\n", -1);
 			}
 		}
 
@@ -422,27 +430,22 @@ meta_changed_cb (G_GNUC_UNUSED BrowserConnection *bcnc, GdaMetaStruct *mstruct,
 	}
 }
 
-static const gchar *
-fk_policy_to_string (GdaMetaForeignKeyPolicy policy)
+/*
+ * builds a list of #GdaMetaDbObject where dbo->depend_list includes @mtable in @mstruct
+ * Returns: (transfer container): a new list
+ */
+static GSList *
+build_reverse_depend_list (GdaMetaStruct *mstruct, GdaMetaTable *mtable)
 {
-	switch (policy) {
-	default:
-		g_assert_not_reached ();
-	case GDA_META_FOREIGN_KEY_UNKNOWN:
-		return _("Unknown");
-	case GDA_META_FOREIGN_KEY_NONE:
-		return _("not enforced");
-	case GDA_META_FOREIGN_KEY_NO_ACTION:
-		return _("stop with error");
-	case GDA_META_FOREIGN_KEY_RESTRICT:
-		return _("stop with error, not deferrable");
-	case GDA_META_FOREIGN_KEY_CASCADE:
-		return _("cascade changes");
-	case GDA_META_FOREIGN_KEY_SET_NULL:
-		return _("set to NULL");
-	case GDA_META_FOREIGN_KEY_SET_DEFAULT:
-		return _("set to default value");
+	GSList *retlist = NULL;
+	GSList *list, *alldbo;
+	alldbo = gda_meta_struct_get_all_db_objects (mstruct);
+	for (list = alldbo; list; list = list->next) {
+		if (g_slist_find (GDA_META_DB_OBJECT (list->data)->depend_list, mtable))
+			retlist = g_slist_prepend (retlist, list->data);
 	}
+	g_slist_free (alldbo);
+	return retlist;
 }
 
 /**
@@ -640,10 +643,9 @@ set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, TableColumns
 	tags = gtk_text_iter_get_tags (&iter);
 	for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
 		GtkTextTag *tag = tagp->data;
-		gchar *table_name;
-		
-		table_name = g_object_get_data (G_OBJECT (tag), "table_name");
-		if (table_name) {
+
+		if (g_object_get_data (G_OBJECT (tag), "table_name") ||
+		    g_object_get_data (G_OBJECT (tag), "fk_name")) {
 			hovering = TRUE;
 			break;
 		}
@@ -735,11 +737,13 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, TableColu
 		const gchar *table_name;
 		const gchar *table_schema;
 		const gchar *table_short_name;
+		const gchar *fk_name;
 		SchemaBrowserPerspective *bpers;
 		
 		table_schema = g_object_get_data (G_OBJECT (tag), "table_schema");
 		table_name = g_object_get_data (G_OBJECT (tag), "table_name");
 		table_short_name = g_object_get_data (G_OBJECT (tag), "table_short_name");
+		fk_name = g_object_get_data (G_OBJECT (tag), "fk_name");
 
 		bpers = SCHEMA_BROWSER_PERSPECTIVE (browser_find_parent_widget (GTK_WIDGET (tcolumns),
 									      TYPE_SCHEMA_BROWSER_PERSPECTIVE));
@@ -749,6 +753,61 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, TableColu
 								       table_name,
 								       table_short_name);
 		}
+		else if (fk_name) {
+			GdaMetaStruct *mstruct;
+			GdaMetaDbObject *dbo;
+			GValue *v1, *v2;
+			GtkWidget *parent;
+			table_schema = table_info_get_table_schema (tcolumns->priv->tinfo);
+			table_name = table_info_get_table_name (tcolumns->priv->tinfo);
+			g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), table_schema);
+			g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), table_name);
+			mstruct = browser_connection_get_meta_struct (tcolumns->priv->bcnc);
+			dbo = gda_meta_struct_get_db_object (mstruct, NULL, v1, v2);
+			gda_value_free (v1);
+			gda_value_free (v2);
+			parent = gtk_widget_get_toplevel (GTK_WIDGET (tcolumns));
+			if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE)) {
+				browser_show_error ((GtkWindow*) parent,
+						    _("Could not find table '%s.%s'"),
+						    table_schema, table_name);
+			}
+			else {
+				GdaMetaTableForeignKey *fk = NULL;
+				GdaMetaTable *mtable;
+				GSList *list;
+				mtable = GDA_META_TABLE (dbo);
+				for (list = mtable->fk_list; list; list = list->next) {
+					GdaMetaTableForeignKey *tfk;
+					tfk = (GdaMetaTableForeignKey*) list->data;
+					if (tfk->fk_name && !strcmp (tfk->fk_name, fk_name)) {
+						fk = tfk;
+						break;
+					}
+				}
+				if (fk) {
+					GError *error = NULL;
+					if (! fk_declare_undeclare (mstruct,
+								    BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+								    fk, &error)) {
+						browser_show_error ((GtkWindow *) parent, _("Failed to undeclare foreign key: %s"),
+								    error && error->message ? error->message : _("No detail"));
+						g_clear_error (&error);
+					}
+					else if (BROWSER_IS_WINDOW (parent))
+						browser_window_show_notice (BROWSER_WINDOW (parent),
+									    GTK_MESSAGE_INFO, "fkdeclare",
+									    _("Successfully undeclared foreign key"));
+					else
+						browser_show_message ((GtkWindow *) parent, "%s",
+								      _("Successfully undeclared foreign key"));
+				}
+				else
+					browser_show_error ((GtkWindow*) parent,
+							    _("Could not find declared foreign key '%s'"),
+							    fk_name);
+			}
+		}
         }
 
 	if (tags) 
diff --git a/tools/browser/schema-browser/table-info.c b/tools/browser/schema-browser/table-info.c
index c309578..d313708 100644
--- a/tools/browser/schema-browser/table-info.c
+++ b/tools/browser/schema-browser/table-info.c
@@ -40,6 +40,7 @@
 #include <libgda-ui/gdaui-basic-form.h>
 #include <libgda-ui/internal/popup-container.h>
 #include <libgda/gda-data-model-extra.h>
+#include "../common/fk-declare.h"
 
 struct _TableInfoPrivate {
 	BrowserConnection *bcnc;
@@ -955,6 +956,50 @@ action_insert_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
 	gtk_widget_show_all (popup);
 }
 
+static void
+action_declarefk_cb (G_GNUC_UNUSED GtkAction *action, TableInfo *tinfo)
+{
+	GtkWidget *dlg, *parent;
+	GdaMetaStruct *mstruct;
+	GdaMetaDbObject *dbo;
+	gint response;
+	GValue *v1, *v2;
+
+	parent = (GtkWidget*) gtk_widget_get_toplevel ((GtkWidget*) tinfo);
+	mstruct = browser_connection_get_meta_struct (tinfo->priv->bcnc);
+	g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), tinfo->priv->schema);
+	g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), tinfo->priv->table_name);
+	dbo = gda_meta_struct_get_db_object (mstruct, NULL, v1, v2);
+	gda_value_free (v1);
+	gda_value_free (v2);
+	if (!dbo || (dbo->obj_type != GDA_META_DB_TABLE)) {
+		browser_show_error ((GtkWindow *) parent, _("Can't find information about table '%s'"),
+				    tinfo->priv->table_short_name);
+		return;
+	}
+
+	dlg = fk_declare_new ((GtkWindow *) parent, mstruct, GDA_META_TABLE (dbo));
+	response = gtk_dialog_run (GTK_DIALOG (dlg));
+	if (response == GTK_RESPONSE_ACCEPT) {
+		GError *error = NULL;
+		if (! fk_declare_write (FK_DECLARE (dlg),
+					BROWSER_IS_WINDOW (parent) ? BROWSER_WINDOW (parent) : NULL,
+					&error)) {
+			browser_show_error ((GtkWindow *) parent, _("Failed to declare foreign key: %s"),
+					    error && error->message ? error->message : _("No detail"));
+			g_clear_error (&error);
+		}
+		else if (BROWSER_IS_WINDOW (parent))
+			browser_window_show_notice (BROWSER_WINDOW (parent),
+						    GTK_MESSAGE_INFO, "fkdeclare",
+						    _("Successfully declared foreign key"));
+		else
+			browser_show_message ((GtkWindow *) parent, "%s",
+					      _("Successfully declared foreign key"));
+	}
+	
+	gtk_widget_destroy (dlg);
+}
 
 static GtkActionEntry ui_actions[] = {
 	{ "Table", NULL, N_("_Table"), NULL, N_("Table"), NULL },
@@ -964,6 +1009,8 @@ static GtkActionEntry ui_actions[] = {
 	  G_CALLBACK (action_view_contents_cb)},
 	{ "InsertData", GTK_STOCK_ADD, N_("_Insert data"), NULL, N_("Insert data into table"),
 	  G_CALLBACK (action_insert_cb)},
+	{ "KfDeclare", NULL, N_("_Declare foreign key"), NULL, N_("Declare a foreign key for table"),
+	  G_CALLBACK (action_declarefk_cb)},
 };
 static const gchar *ui_actions_info =
 	"<ui>"
@@ -973,6 +1020,8 @@ static const gchar *ui_actions_info =
         "        <menuitem name='AddToFav' action= 'AddToFav'/>"
         "        <menuitem name='InsertData' action= 'InsertData'/>"
         "        <menuitem name='ViewContents' action= 'ViewContents'/>"
+        "        <separator/>"
+        "        <menuitem name='KfDeclare' action= 'KfDeclare'/>"
         "      </menu>"
         "    </placeholder>"
 	"  </menubar>"
diff --git a/tools/browser/support.c b/tools/browser/support.c
index f45b0e1..b1b885f 100644
--- a/tools/browser/support.c
+++ b/tools/browser/support.c
@@ -279,9 +279,11 @@ browser_show_help (GtkWindow *parent, const gchar *topic)
 		
 		g_error_free (error);
 	}
-	else
+	else if (BROWSER_IS_WINDOW (parent))
 		browser_window_show_notice (BROWSER_WINDOW (parent), GTK_MESSAGE_INFO,
 					    "show-help", _("Help is being loaded, please wait..."));
+	else
+		browser_show_message (parent, "%s", _("Help is being loaded, please wait..."));
 
 	g_free (uri);
 }
diff --git a/tools/command-exec.c b/tools/command-exec.c
index f138d23..ddcee88 100644
--- a/tools/command-exec.c
+++ b/tools/command-exec.c
@@ -28,6 +28,7 @@
 #include <readline/history.h>
 #endif
 #include <sql-parser/gda-statement-struct-util.h>
+#include "tools-utils.h"
 
 /*
  *  gda_internal_command_arg_remove_quotes
@@ -789,6 +790,7 @@ gda_internal_command_detail (G_GNUC_UNUSED SqlConsole *console, GdaConnection *c
 			const gchar *sql = "SELECT constraint_type, constraint_name  "
 				"FROM _table_constraints WHERE table_catalog = ##tc::string "
 				"AND table_schema = ##ts::string AND table_name = ##tname::string "
+				"AND constraint_type != 'FOREIGN KEY' "
 				"ORDER BY constraint_type DESC, constraint_name";
 
 			g_value_set_string ((catalog = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
@@ -853,122 +855,7 @@ gda_internal_command_detail (G_GNUC_UNUSED SqlConsole *console, GdaConnection *c
 					}
 				}
 				else if (*str == 'F') {
-					/* foreign key */
-					GdaDataModel *cols;
-					cvalue = gda_data_model_get_value_at (model, 1, i, error);
-					if (!cvalue) {
-						gda_internal_command_exec_result_free (res);
-						res = NULL;
-						goto out;
-					}
-					string = g_string_new (_("Foreign key"));
-					g_string_append_printf (string, " '%s'", g_value_get_string (cvalue));
-
-					str = "SELECT rc.ref_table_schema as ref_table_schema, "
-						"rc.ref_table_name as ref_table_name, "
-						"kc1.column_name as fk_column, "
-						"kc2.column_name as ref_column, "
-						"rc.update_rule, rc.delete_rule "
-						"FROM _referential_constraints rc "
-						"INNER JOIN _key_column_usage kc2 ON "
-						"(rc.ref_table_catalog=kc2.table_catalog AND rc.ref_table_schema=kc2.table_schema AND rc.ref_table_name=kc2.table_name AND rc.ref_constraint_name=kc2.constraint_name) "
-						"INNER JOIN _key_column_usage kc1 ON (rc.table_catalog=kc1.table_catalog AND rc.table_schema=kc1.table_schema AND rc.table_name=kc1.table_name AND rc.constraint_name=kc1.constraint_name) "
-						"WHERE kc1.ordinal_position = kc2.ordinal_position "
-						"AND rc.table_catalog = ##tc::string "
-						"AND rc.table_schema = ##ts::string AND rc.table_name = ##tname::string "
-						"AND rc.constraint_name = ##cname::string "
-						"ORDER BY rc.table_catalog, rc.table_schema, rc.table_name, kc1.ordinal_position";
-					
-					cols = gda_meta_store_extract (gda_connection_get_meta_store (cnc), str, NULL, 
-								       "tc", catalog, "ts", schema, "tname", name, "cname", cvalue, 
-								       NULL);
-					if (cols) {
-						gint j, cnrows;
-						cnrows = gda_data_model_get_n_rows (cols);
-						if (cnrows > 0) {
-							g_string_append (string, " (");
-							for (j = 0; j < cnrows; j++) {
-								if (j > 0)
-									g_string_append (string, ", ");
-								cvalue = gda_data_model_get_value_at (cols, 2, j, error);
-								if (!cvalue) {
-									gda_internal_command_exec_result_free (res);
-									res = NULL;
-									g_object_unref (cols);
-									g_string_free (string, TRUE);
-									goto out;
-								}
-								g_string_append (string, g_value_get_string (cvalue));
-							}
-							g_string_append (string, ") references");
-
-							const GValue *v1, *v2;
-							
-							v1 = gda_data_model_get_value_at (cols, 0, 0, error);
-							if (!v1) {
-								gda_internal_command_exec_result_free (res);
-								res = NULL;
-								g_object_unref (cols);
-								g_string_free (string, TRUE);
-								goto out;
-							}
-							v2 = gda_data_model_get_value_at (cols, 1, 0, error);
-							if (!v2) {
-								gda_internal_command_exec_result_free (res);
-								res = NULL;
-								g_object_unref (cols);
-								g_string_free (string, TRUE);
-								goto out;
-							}
-							g_string_append_printf (string, " %s.%s (", 
-										g_value_get_string (v1),
-										g_value_get_string (v2));
-							for (j = 0; j < cnrows; j++) {
-								if (j > 0)
-									g_string_append (string, ", ");
-								cvalue = gda_data_model_get_value_at (cols, 3, j, error);
-								if (!cvalue) {
-									gda_internal_command_exec_result_free (res);
-									res = NULL;
-									g_object_unref (cols);
-									g_string_free (string, TRUE);
-									goto out;
-								}
-								g_string_append (string, g_value_get_string (cvalue));
-							}
-							g_string_append_c (string, ')');
-
-							v1 = gda_data_model_get_value_at (cols, 4, 0, error);
-							if (!v1) {
-								gda_internal_command_exec_result_free (res);
-								res = NULL;
-								g_object_unref (cols);
-								g_string_free (string, TRUE);
-								goto out;
-							}
-							v2 = gda_data_model_get_value_at (cols, 5, 0, error);
-							if (!v2) {
-								gda_internal_command_exec_result_free (res);
-								res = NULL;
-								g_object_unref (cols);
-								g_string_free (string, TRUE);
-								goto out;
-							}
-							if (G_VALUE_TYPE (v1) == G_TYPE_STRING) {
-								g_string_append (string, ", ");
-								g_string_append (string, _("Policy on UPDATE"));
-								g_string_append (string, ": ");
-								g_string_append (string, g_value_get_string (v1));
-							}
-							if (G_VALUE_TYPE (v2) == G_TYPE_STRING) {
-								g_string_append (string, ", ");
-								g_string_append (string, _("Policy on DELETE"));
-								g_string_append (string, ": ");
-								g_string_append (string, g_value_get_string (v1));
-							}
-						}
-						g_object_unref (cols);
-					}
+					/* foreign key, not handled here */
 				}
 				else if (*str == 'U') {
 					/* Unique constraint */
@@ -1027,6 +914,62 @@ gda_internal_command_detail (G_GNUC_UNUSED SqlConsole *console, GdaConnection *c
 			gda_value_free (name);
 
 			g_object_unref (model);
+
+			/* foreign key constraints */
+			GSList *list;
+			for (list = mt->fk_list; list; list = list->next) {
+				GString *string;
+				GdaMetaTableForeignKey *fk;
+				gint i;
+				fk = (GdaMetaTableForeignKey*) list->data;
+				if (GDA_META_TABLE_FOREIGN_KEY_IS_DECLARED (fk))
+					string = g_string_new (_("Declared foreign key"));
+				else
+					string = g_string_new (_("Foreign key"));
+				g_string_append_printf (string, " '%s' (", fk->fk_name);
+				for (i = 0; i < fk->cols_nb; i++) {
+					if (i != 0)
+						g_string_append (string, ", ");
+					g_string_append (string, fk->fk_names_array [i]);
+				}
+				g_string_append (string, ") ");
+				if (fk->depend_on->obj_short_name)
+					/* To translators: the term "references" is the verb
+					 * "to reference" in the context of foreign keys where
+					 * "table A REFERENCES table B" */
+					g_string_append_printf (string, _("references %s"),
+								fk->depend_on->obj_short_name);
+				else
+					/* To translators: the term "references" is the verb
+					 * "to reference" in the context of foreign keys where
+					 * "table A REFERENCES table B" */
+					g_string_append_printf (string, _("references %s.%s"),
+								fk->depend_on->obj_schema,
+								fk->depend_on->obj_name);
+				g_string_append (string, " (");
+				for (i = 0; i < fk->cols_nb; i++) {
+					if (i != 0)
+						g_string_append (string, ", ");
+					g_string_append (string, fk->ref_pk_names_array [i]);
+				}
+				g_string_append (string, ")");
+
+				g_string_append (string, "\n  ");
+				g_string_append (string, _("Policy on UPDATE"));
+				g_string_append (string, ": ");
+				g_string_append (string, tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_UPDATE_POLICY (fk)));
+				g_string_append (string, "\n  ");
+				g_string_append (string, _("Policy on DELETE"));
+				g_string_append (string, ": ");
+				g_string_append (string, tools_utils_fk_policy_to_string (GDA_META_TABLE_FOREIGN_KEY_ON_DELETE_POLICY (fk)));
+
+				subres = g_new0 (GdaInternalCommandResult, 1);
+				subres->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
+				subres->u.txt = string;
+				res->u.multiple_results = g_slist_append (res->u.multiple_results,
+									  subres);
+			}
+
 			return res;
 		}
 	}
diff --git a/tools/gda-sql.1.in b/tools/gda-sql.1.in
index 0573f98..fa31965 100644
--- a/tools/gda-sql.1.in
+++ b/tools/gda-sql.1.in
@@ -200,6 +200,11 @@ Lists all schemas if no argument is provided. \fB.d <SCHEMA_NAME>\fP lists speci
 Lists all tables if no argument is provided. \fB.d <TABLE_NAME>\fP lists specified table.
 .IP \fB.dv\fP
 Lists all views if no argument is provided. \fB.d <VIEW_NAME>\fP lists specified view.
+.IP \fB.fkdeclare\fP
+Declares a new foreign key (no constraint is added to the database). The meta data is modified to take into account a foreign key constraint. The foreign key specification is \fB <fkname> <tableA>(<colA>,...) <tableB>(<colB>,...)\fP where \fB<fkname>\fP is the name given to the foreign key constraint and \fB<tableA>\fP references \fB<tableB>\fP using the columns mentionned between the parenthesis. Note that the (\fB<fkname>\fP, \fB<tableA>\fP, \fB<tableB>\fP) triplet uniquely identifies a declared foreign key (declaring a new foreign key with the same triplet will remove any previously declared one).
+\fINote:\fP any actual foreign key constraint will always have precedence over any declared foreign key.
+.IP \fB.fkundeclare\fP
+Un-declares a foreign key (does the opposite of \fB.fkdeclare\fP).
 .IP \fB.e\fP
 Edits the query buffer with external editor, if no argument is provided. \fB.e <FILE_NAME>\fP
 edits the specified file name. The external editor can be specified using environment variables.
diff --git a/tools/gda-sql.c b/tools/gda-sql.c
index a25772b..5880760 100644
--- a/tools/gda-sql.c
+++ b/tools/gda-sql.c
@@ -1984,6 +1984,14 @@ static GdaInternalCommandResult *extra_command_set2 (SqlConsole *console, GdaCon
 						     const gchar **args,
 						     GError **error, gpointer data);
 
+static GdaInternalCommandResult *extra_command_declare_fk (SqlConsole *console, GdaConnection *cnc,
+							   const gchar **args,
+							   GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_undeclare_fk (SqlConsole *console, GdaConnection *cnc,
+							     const gchar **args,
+							     GError **error, gpointer data);
+
 static GdaInternalCommandsList *
 build_internal_commands_list (void)
 {
@@ -2016,6 +2024,30 @@ build_internal_commands_list (void)
 
 	c = g_new0 (GdaInternalCommand, 1);
 	c->group = _("Information");
+	c->name = g_strdup_printf (_("%s <fkname> <tableA>(<colA>,...) <tableB>(<colB>,...)"), "fkdeclare");
+	c->description = _("Declare a new foreign key (not actually in database): tableA references tableB");
+	c->args = NULL;
+	c->command_func = extra_command_declare_fk;
+	c->user_data = NULL;
+	c->arguments_delimiter_func = args_as_string_func;
+	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
+	commands->commands = g_slist_prepend (commands->commands, c);
+
+	c = g_new0 (GdaInternalCommand, 1);
+	c->group = _("Information");
+	c->name = g_strdup_printf (_("%s <fkname> <tableA> <tableB>"), "fkundeclare");
+	c->description = _("Un-declare a foreign key (not actually in database)");
+	c->args = NULL;
+	c->command_func = extra_command_undeclare_fk;
+	c->user_data = NULL;
+	c->arguments_delimiter_func = args_as_string_func;
+	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
+	commands->commands = g_slist_prepend (commands->commands, c);
+
+	c = g_new0 (GdaInternalCommand, 1);
+	c->group = _("Information");
 	c->name = g_strdup_printf (_("%s [TABLE]"), "dt");
 	c->description = _("List all tables (or named table)");
 	c->args = NULL;
@@ -3965,6 +3997,360 @@ foreach_param_set (const gchar *pname, GdaHolder *param, GdaDataModel *model)
 	gda_value_free (value);
 }
 
+typedef struct {
+	gchar *fkname;
+	gchar *table;
+	gchar *ref_table;
+	GArray *columns;
+	GArray *ref_columns;
+} FkDeclData;
+
+static void
+fk_decl_data_free (FkDeclData *data)
+{
+	g_free (data->fkname);
+	g_free (data->table);
+	g_free (data->ref_table);
+	if (data->columns) {
+		gint i;
+		for (i = 0; i < data->columns->len; i++)
+			g_free (g_array_index (data->columns, gchar*, i));
+		g_array_free (data->columns, TRUE);
+	}
+	if (data->ref_columns) {
+		gint i;
+		for (i = 0; i < data->ref_columns->len; i++)
+			g_free (g_array_index (data->ref_columns, gchar*, i));
+		g_array_free (data->ref_columns, TRUE);
+	}
+	g_free (data);
+}
+
+static FkDeclData *
+parse_fk_decl_spec (const gchar *spec, gboolean columns_required, GError **error)
+{
+	FkDeclData *decldata;
+	decldata = g_new0 (FkDeclData, 1);
+
+	gchar *ptr, *dspec, *start, *subptr, *substart;
+
+	if (!spec || !*spec) {
+		g_set_error (error, 0, 0,
+			     _("Missing foreign key declaration specification"));
+		return NULL;
+	}
+	dspec = g_strstrip (g_strdup (spec));
+
+	/* FK name */
+	for (ptr = dspec; *ptr && !g_ascii_isspace (*ptr); ptr++) {
+		if (! g_ascii_isalnum (*ptr) && (*ptr != '_'))
+			goto onerror;
+	}
+	if (!*ptr)
+		goto onerror;
+	*ptr = 0;
+	decldata->fkname = g_strstrip (g_strdup (dspec));
+#ifdef GDA_DEBUG_NO
+	g_print ("KFNAME [%s]\n", decldata->fkname);
+#endif
+
+	/* table name */
+	start = ++ptr;
+	if (columns_required)
+		for (ptr = start; *ptr && (*ptr != '('); ptr++);
+	else {
+		for (ptr = start; *ptr && g_ascii_isspace (*ptr); ptr++);
+		start = ptr;
+		for (; *ptr && !g_ascii_isspace (*ptr); ptr++);
+	}
+	if (!*ptr)
+		goto onerror;
+	*ptr = 0;
+	if (!*start)
+		goto onerror;
+	decldata->table = g_strstrip (g_strdup (start));
+#ifdef GDA_DEBUG_NO
+	g_print ("TABLE [%s]\n", decldata->table);
+#endif
+
+	if (columns_required) {
+		/* columns names */
+		start = ++ptr;
+		for (ptr = start; *ptr && (*ptr != ')'); ptr++);
+		if (!*ptr)
+			goto onerror;
+		*ptr = 0;
+#ifdef GDA_DEBUG_NO
+		g_print ("COLS [%s]\n", start);
+#endif
+		substart = start;
+		for (substart = start; substart < ptr; substart = ++subptr) {
+			gchar *tmp;
+			for (subptr = substart; *subptr && (*subptr != ','); subptr++);
+			*subptr = 0;
+			if (!decldata->columns)
+				decldata->columns = g_array_new (FALSE, FALSE, sizeof (gchar*));
+			tmp = g_strstrip (g_strdup (substart));
+			g_array_append_val (decldata->columns, tmp);
+#ifdef GDA_DEBUG_NO
+			g_print ("  COL [%s]\n", tmp);
+#endif
+		}
+		if (! decldata->columns)
+			goto onerror;
+	}
+
+	/* ref table */
+	start = ++ptr;
+	if (columns_required)
+		for (ptr = start; *ptr && (*ptr != '('); ptr++);
+	else {
+		for (ptr = start; *ptr && g_ascii_isspace (*ptr); ptr++);
+		start = ptr;
+		for (; *ptr && !g_ascii_isspace (*ptr); ptr++);
+	}
+	*ptr = 0;
+	if (!*start)
+		goto onerror;
+	decldata->ref_table = g_strstrip (g_strdup (start));
+#ifdef GDA_DEBUG_NO
+	g_print ("REFTABLE [%s]\n", decldata->ref_table);
+#endif
+
+	if (columns_required) {
+		/* ref columns names */
+		start = ++ptr;
+		for (ptr = start; *ptr && (*ptr != ')'); ptr++);
+		if (!*ptr)
+			goto onerror;
+		*ptr = 0;
+#ifdef GDA_DEBUG_NO
+		g_print ("REF COLS [%s]\n", start);
+#endif
+		for (substart = start; substart < ptr; substart = ++subptr) {
+			gchar *tmp;
+			for (subptr = substart; *subptr && (*subptr != ','); subptr++);
+			*subptr = 0;
+			if (!decldata->ref_columns)
+				decldata->ref_columns = g_array_new (FALSE, FALSE, sizeof (gchar*));
+			tmp = g_strstrip (g_strdup (substart));
+			g_array_append_val (decldata->ref_columns, tmp);
+#ifdef GDA_DEBUG_NO
+			g_print ("  COL [%s]\n", tmp);
+#endif
+		}
+		if (! decldata->ref_columns)
+			goto onerror;
+	}
+
+	g_free (dspec);
+
+	if (columns_required && (decldata->columns->len != decldata->ref_columns->len))
+		goto onerror;
+
+	return decldata;
+
+ onerror:
+	fk_decl_data_free (decldata);
+	g_set_error (error, 0, 0,
+		     _("Malformed foreign key declaration specification"));
+	return NULL;
+}
+
+/*
+ * decomposes @table and sets the the out* variables
+ */
+static gboolean
+fk_decl_analyse_table_name (const gchar *table, GdaMetaStore *mstore,
+			    gchar **out_catalog, gchar **out_schema, gchar **out_table,
+			    GError **error)
+{
+	gchar **id_array;
+	gint size = 0, l;
+
+	id_array = gda_sql_identifier_split (table);
+	if (id_array) {
+		size = g_strv_length (id_array) - 1;
+		g_assert (size >= 0);
+		l = size;
+		*out_table = g_strdup (id_array[l]);
+		l--;
+		if (l >= 0) {
+			*out_schema = g_strdup (id_array[l]);
+			l--;
+			if (l >= 0)
+				*out_catalog = g_strdup (id_array[l]);
+		}
+		g_strfreev (id_array);
+	}
+	else {
+		g_set_error (error, 0, 0,
+			     _("Malformed table name specification '%s'"), table);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static GdaInternalCommandResult *
+extra_command_declare_fk (SqlConsole *console, GdaConnection *cnc,
+			  const gchar **args,
+			  GError **error, gpointer data)
+{
+	GdaInternalCommandResult *res = NULL;
+
+	if (!main_data->current) {
+		g_set_error (error, 0, 0, "%s", _("No connection opened"));
+		return NULL;
+	}
+
+	if (args[0] && *args[0]) {
+		FkDeclData *decldata;
+		GdaMetaStore *mstore;
+		gchar *catalog = NULL, *schema = NULL, *table = NULL;
+		gchar *ref_catalog = NULL, *ref_schema = NULL, *ref_table = NULL;
+
+		mstore = gda_connection_get_meta_store (main_data->current->cnc);
+		if (! (decldata = parse_fk_decl_spec (args[0], TRUE, error)))
+			return NULL;
+		
+		/* table name */
+		if (!fk_decl_analyse_table_name (decldata->table, mstore,
+						 &catalog, &schema, &table, error)) {
+			fk_decl_data_free (decldata);
+			return NULL;
+		}
+		
+		/* ref table name */
+		if (!fk_decl_analyse_table_name (decldata->ref_table, mstore,
+						 &ref_catalog, &ref_schema, &ref_table, error)) {
+			fk_decl_data_free (decldata);
+			g_free (catalog);
+			g_free (schema);
+			g_free (table);
+			return NULL;
+		}
+
+#ifdef GDA_DEBUG_NO
+		g_print ("NOW: %s.%s.%s REFERENCES %s.%s.%s\n",
+			 gda_value_stringify (gda_set_get_holder_value (params, "tcal")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "tschema")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "tname")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tcal")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tschema")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tname")));
+#endif
+
+		gchar **colnames, **ref_colnames;
+		gint l;
+		gboolean allok;
+		colnames = g_new0 (gchar *, decldata->columns->len);
+		ref_colnames = g_new0 (gchar *, decldata->columns->len);
+		for (l = 0; l < decldata->columns->len; l++) {
+			colnames [l] = g_array_index (decldata->columns, gchar*, l);
+			ref_colnames [l] = g_array_index (decldata->ref_columns, gchar*, l);
+		}
+
+		allok = gda_meta_store_declare_foreign_key (mstore, NULL,
+							    decldata->fkname,
+							    catalog, schema, table,
+							    ref_catalog, ref_schema, ref_table,
+							    decldata->columns->len,
+							    colnames, ref_colnames, error);
+		g_free (catalog);
+		g_free (schema);
+		g_free (table);
+		g_free (ref_catalog);
+		g_free (ref_schema);
+		g_free (ref_table);
+		g_free (colnames);
+		g_free (ref_colnames);
+		fk_decl_data_free (decldata);
+
+		if (allok) {
+			res = g_new0 (GdaInternalCommandResult, 1);
+			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		}
+	}
+	else
+		g_set_error (error, 0, 0,
+			     _("Missing foreign key name argument"));
+	return res;
+}
+
+static GdaInternalCommandResult *
+extra_command_undeclare_fk (SqlConsole *console, GdaConnection *cnc,
+			    const gchar **args,
+			    GError **error, gpointer data)
+{
+	GdaInternalCommandResult *res = NULL;
+
+	if (!main_data->current) {
+		g_set_error (error, 0, 0, "%s", _("No connection opened"));
+		return NULL;
+	}
+
+	if (args[0] && *args[0]) {
+		FkDeclData *decldata;
+		GdaMetaStore *mstore;
+		gchar *catalog = NULL, *schema = NULL, *table = NULL;
+		gchar *ref_catalog = NULL, *ref_schema = NULL, *ref_table = NULL;
+
+		mstore = gda_connection_get_meta_store (main_data->current->cnc);
+		if (! (decldata = parse_fk_decl_spec (args[0], FALSE, error)))
+			return NULL;
+		
+		/* table name */
+		if (!fk_decl_analyse_table_name (decldata->table, mstore,
+						 &catalog, &schema, &table, error)) {
+			fk_decl_data_free (decldata);
+			return NULL;
+		}
+		
+		/* ref table name */
+		if (!fk_decl_analyse_table_name (decldata->ref_table, mstore,
+						 &ref_catalog, &ref_schema, &ref_table, error)) {
+			fk_decl_data_free (decldata);
+			g_free (catalog);
+			g_free (schema);
+			g_free (table);
+			return NULL;
+		}
+
+#ifdef GDA_DEBUG_NO
+		g_print ("NOW: %s.%s.%s REFERENCES %s.%s.%s\n",
+			 gda_value_stringify (gda_set_get_holder_value (params, "tcal")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "tschema")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "tname")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tcal")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tschema")),
+			 gda_value_stringify (gda_set_get_holder_value (params, "ref_tname")));
+#endif
+
+		gboolean allok;
+		allok = gda_meta_store_undeclare_foreign_key (mstore, NULL,
+							      decldata->fkname,
+							      catalog, schema, table,
+							      ref_catalog, ref_schema, ref_table,
+							      error);
+		g_free (catalog);
+		g_free (schema);
+		g_free (table);
+		g_free (ref_catalog);
+		g_free (ref_schema);
+		g_free (ref_table);
+		fk_decl_data_free (decldata);
+
+		if (allok) {
+			res = g_new0 (GdaInternalCommandResult, 1);
+			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		}
+	}
+	else
+		g_set_error (error, 0, 0,
+			     _("Missing foreign key name argument"));
+	return res;
+}
+
 static const GValue *
 get_table_value_at_cell (GdaConnection *cnc, GError **error, G_GNUC_UNUSED MainData *data,
 			 const gchar *table, const gchar *column, const gchar *row_cond,
diff --git a/tools/information-schema-types.c b/tools/information-schema-types.c
index d6f7f90..634a518 100644
--- a/tools/information-schema-types.c
+++ b/tools/information-schema-types.c
@@ -1,5 +1,5 @@
 /* GDA - Information schema data types extractor
- * Copyright (C) 2009 The GNOME Foundation.
+ * Copyright (C) 2009 - 2011 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -76,18 +76,20 @@ main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char** argv)
 		if (!strcmp ((gchar *) node->name, "table")) {
 			xmlChar *prop, *descr;
 			xmlNodePtr child;
+			GString *tmp_out_str;
 
+			tmp_out_str = g_string_new ("");
 			descr = xmlGetProp (node, BAD_CAST "descr");
 			prop = xmlGetProp (node, BAD_CAST "name");
 			if (prop) {
-				g_string_append (out_str, "\n\n");
+				g_string_append (tmp_out_str, "\n\n");
 				if (descr)
-					g_string_append_printf (out_str, "/*\n * TABLE: %s\n *\n * %s\n */\n",
+					g_string_append_printf (tmp_out_str, "/*\n * TABLE: %s\n *\n * %s\n */\n",
 								(gchar*) prop, (gchar *) descr);
 				else
-					g_string_append_printf (out_str, "/*\n * TABLE: %s\n */\n",
+					g_string_append_printf (tmp_out_str, "/*\n * TABLE: %s\n */\n",
 								(gchar*) prop);
-				g_string_append_printf (out_str,
+				g_string_append_printf (tmp_out_str,
 							"static GType _col_types%s[] = {\n", prop);
 				xmlFree (prop);
 			}
@@ -104,21 +106,31 @@ main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char** argv)
 				if (!strcmp ((gchar *) child->name, "column")) {
 					if (firstcol) {
 						firstcol = FALSE;
-						g_string_append (out_str, "  ");
+						g_string_append (tmp_out_str, "  ");
 					}
 					else
-						g_string_append (out_str, ", ");
+						g_string_append (tmp_out_str, ", ");
 
 					prop = xmlGetProp (child, BAD_CAST "type");
 					if (prop) {
 						GType type;
 						type = gda_g_type_from_string ((gchar*) prop);
 						if (type == G_TYPE_STRING)
-							g_string_append (out_str, "G_TYPE_STRING");
+							g_string_append (tmp_out_str, "G_TYPE_STRING");
 						else if (type == G_TYPE_BOOLEAN)
-							g_string_append (out_str, "G_TYPE_BOOLEAN");
+							g_string_append (tmp_out_str, "G_TYPE_BOOLEAN");
 						else if (type == G_TYPE_INT)
-							g_string_append (out_str, "G_TYPE_INT");
+							g_string_append (tmp_out_str, "G_TYPE_INT");
+						else if (type == GDA_TYPE_TIMESTAMP) {
+							xmlChar *tname;
+							tname = xmlGetProp (node, BAD_CAST "name");
+							g_print ("Warning: ignoring table %s because the '%s' type is "
+								 "not constant.\n", (gchar*) tname, (gchar*) prop);
+							xmlFree (tname);
+							g_string_free (tmp_out_str, TRUE);
+							tmp_out_str = NULL;
+							break;
+						}
 						else
 							g_error ("Non handled type "
 								 "%s (interpreted as %s)\n", prop,
@@ -127,20 +139,24 @@ main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char** argv)
 						xmlFree (prop);
 					}
 					else
-						g_string_append (out_str, "G_TYPE_STRING");
+						g_string_append (tmp_out_str, "G_TYPE_STRING");
 
 					prop = xmlGetProp (child, BAD_CAST "name");
 					if (prop) {
-						g_string_append_printf (out_str, "  /* column: %s */\n",
+						g_string_append_printf (tmp_out_str, "  /* column: %s */\n",
 									prop);
 						xmlFree (prop);
 					}
 					else
-						g_string_append (out_str, "\n");
+						g_string_append (tmp_out_str, "\n");
 				}
 			}
-			g_string_append (out_str, ", G_TYPE_NONE /* end of array marker */\n");
-			g_string_append (out_str, "};\n\n");
+			if (tmp_out_str) {
+				g_string_append (tmp_out_str, ", G_TYPE_NONE /* end of array marker */\n");
+				g_string_append (tmp_out_str, "};\n\n");
+				g_string_append (out_str, tmp_out_str->str);
+				g_string_free (tmp_out_str, TRUE);
+			}
 		}
 	}
 	xmlFreeDoc (doc);
diff --git a/tools/tools-utils.c b/tools/tools-utils.c
new file mode 100644
index 0000000..433dbb8
--- /dev/null
+++ b/tools/tools-utils.c
@@ -0,0 +1,52 @@
+/* 
+ * Copyright (C) 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "tools-utils.h"
+#include <glib/gi18n-lib.h>
+
+/**
+ * tools_utils_fk_policy_to_string
+ * @policy: a #GdaMetaForeignKeyPolicy
+ *
+ * Returns: the human readable version of @policy
+ */
+const gchar *
+tools_utils_fk_policy_to_string (GdaMetaForeignKeyPolicy policy)
+{
+	switch (policy) {
+	default:
+		g_assert_not_reached ();
+	case GDA_META_FOREIGN_KEY_UNKNOWN:
+		return _("Unknown");
+	case GDA_META_FOREIGN_KEY_NONE:
+		return _("not enforced");
+	case GDA_META_FOREIGN_KEY_NO_ACTION:
+		return _("stop with error");
+	case GDA_META_FOREIGN_KEY_RESTRICT:
+		return _("stop with error, not deferrable");
+	case GDA_META_FOREIGN_KEY_CASCADE:
+		return _("cascade changes");
+	case GDA_META_FOREIGN_KEY_SET_NULL:
+		return _("set to NULL");
+	case GDA_META_FOREIGN_KEY_SET_DEFAULT:
+		return _("set to default value");
+	}
+}
diff --git a/tools/tools-utils.h b/tools/tools-utils.h
new file mode 100644
index 0000000..f87abbb
--- /dev/null
+++ b/tools/tools-utils.h
@@ -0,0 +1,29 @@
+/* 
+ * Copyright (C) 2010 - 2011 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __GDA_TOOLS_UTILS__
+#define __GDA_TOOLS_UTILS__
+
+#include <libgda/libgda.h>
+
+const gchar *tools_utils_fk_policy_to_string (GdaMetaForeignKeyPolicy policy);
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]