[libgda] Added the notion of declared foreign key in the database's meta data
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Added the notion of declared foreign key in the database's meta data
- Date: Wed, 9 Feb 2011 20:01:00 +0000 (UTC)
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],
+ ¶ms, 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],
+ ¶ms, 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 (®istering);
+ if (type == 0)
+ type = g_type_register_static (GTK_TYPE_DIALOG, "FkDeclare", &info, 0);
+ g_static_mutex_unlock (®istering);
+ }
+
+ 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,
- ¤t,
- _("Foreign key on "), -1,
+ ¤t, 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, ¤t,
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, ¤t, "\n(", -1);
+ gtk_text_buffer_insert_with_tags (tbuffer, ¤t,
+ _("Remove"), -1, tag, NULL);
+ gtk_text_buffer_insert (tbuffer, ¤t, ")", -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, ¤t, _("Policy on UPDATE"), -1);
gtk_text_buffer_insert (tbuffer, ¤t, ": ", -1);
gtk_text_buffer_insert (tbuffer, ¤t,
- 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, ¤t, _("Policy on DELETE"), -1);
gtk_text_buffer_insert (tbuffer, ¤t, ": ", -1);
gtk_text_buffer_insert (tbuffer, ¤t,
- fk_policy_to_string (policy), -1);
+ tools_utils_fk_policy_to_string (policy),
+ -1);
}
gtk_text_buffer_insert (tbuffer, ¤t, "\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,
- ¤t,
- _("Tables referencing this one"), -1,
- "section", NULL);
- gtk_text_buffer_insert (tbuffer, ¤t, "\n", -1);
-
- gint i;
- const GValue *cvalue[3];
- for (i = 0; i < nrows; i++) {
- if (i > 0)
- gtk_text_buffer_insert (tbuffer, ¤t, "\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,
+ ¤t,
+ _("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, ¤t, "\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, ¤t,
- 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, ¤t, "\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]