libgda r3085 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite po providers/postgres tests/providers tools



Author: vivien
Date: Mon Mar 17 19:34:20 2008
New Revision: 3085
URL: http://svn.gnome.org/viewvc/libgda?rev=3085&view=rev

Log:
2008-03-17  Vivien Malerba <malerba gnome-db org>

        * libgda/gda-statement.h: correctly define gda_statement_to_sql(), thanks
        to Carlos Savoretti
        * po/POTFILES.skip: remove mention of temporary files
        * libgda/gda-data-model.c: fixed documentation for bug #522365
        * doc/C: dosumentation improvements
        * libgda/gda-meta-store.[ch]:
        * libgda/gda-connection.c: make the partial metadata sync with the database work, and
        added the possibility for applications to add their own database objects in a GdaMetaStore
        * libgda/gda-server-provider.c: fix a memory leak in the server operations handling (reported by
        Phil Longstaff)
        * libgda/information_schema.xml: more descriptions of the tables and columns
        * libgda/sqlite/gda-sqlite-provider.c: make the "internal_stmt" array static to avoid collisions
        * libgda/gda-server-provider.h:
        * libgda/gda-connection.c:
        * providers/postgres/gda-postgres-provider.c:
        * providers/postgres/gda-postgres-meta.[ch]:
        * libgda/sqlite/gda-sqlite-provider.c:
        * libgda/sqlite/gda-sqlite-meta.[ch]: more meta data retreival implemented (constraints' columns)
        * tests/providers/prov-test-common.c: follow API changes
        * tools/command-exec.c: added a description of the table's constraints



Added:
   trunk/doc/C/MetaStore1.dia   (contents, props changed)
   trunk/doc/C/MetaStore1.png   (contents, props changed)
   trunk/doc/C/MetaStore2.dia   (contents, props changed)
   trunk/doc/C/MetaStore2.png   (contents, props changed)
Modified:
   trunk/ChangeLog
   trunk/doc/C/Makefile.am
   trunk/doc/C/libgda-4.0-docs.sgml
   trunk/doc/C/migration2.xml
   trunk/doc/C/tmpl/gda-meta-store.sgml
   trunk/doc/C/tmpl/gda-server-provider.sgml
   trunk/libgda/gda-connection.c
   trunk/libgda/gda-data-model.c
   trunk/libgda/gda-meta-store.c
   trunk/libgda/gda-meta-store.h
   trunk/libgda/gda-server-provider.c
   trunk/libgda/gda-server-provider.h
   trunk/libgda/gda-statement.h
   trunk/libgda/gda-util.c
   trunk/libgda/information_schema.xml
   trunk/libgda/sqlite/gda-sqlite-meta.c
   trunk/libgda/sqlite/gda-sqlite-meta.h
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/po/POTFILES.skip
   trunk/providers/postgres/gda-postgres-meta.c
   trunk/providers/postgres/gda-postgres-meta.h
   trunk/providers/postgres/gda-postgres-provider.c
   trunk/tests/providers/prov-test-common.c
   trunk/tools/command-exec.c

Modified: trunk/doc/C/Makefile.am
==============================================================================
--- trunk/doc/C/Makefile.am	(original)
+++ trunk/doc/C/Makefile.am	Mon Mar 17 19:34:20 2008
@@ -34,6 +34,7 @@
 	-I$(top_srcdir)/libgda \
         $(LIBGDA_CFLAGS) \
 	-DGETTEXT_PACKAGE=\""$(GETTEXT_PACKAGE)"\"
+
 GTKDOC_LIBS =  $(top_builddir)/libgda/libgda-4.0.la \
 	$(top_builddir)/libgda-report/libgda-report-4.0.la \
 	$(LIBGDA_LIBS)
@@ -47,7 +48,8 @@
 # Images to copy into HTML directory
 HTML_IMAGES = DataModels.png \
 	architecture.png parts.png stmt-unknown.png stmt-select.png stmt-insert1.png stmt-insert2.png \
-	stmt-update.png stmt-compound.png information_schema.png
+	stmt-update.png stmt-compound.png information_schema.png \
+	MetaStore1.png MetaStore2.png
 
 # Extra options to supply to gtkdoc-fixref
 FIXXREF_OPTIONS=

Added: trunk/doc/C/MetaStore1.dia
==============================================================================
Binary file. No diff available.

Added: trunk/doc/C/MetaStore1.png
==============================================================================
Binary file. No diff available.

Added: trunk/doc/C/MetaStore2.dia
==============================================================================
Binary file. No diff available.

Added: trunk/doc/C/MetaStore2.png
==============================================================================
Binary file. No diff available.

Modified: trunk/doc/C/libgda-4.0-docs.sgml
==============================================================================
--- trunk/doc/C/libgda-4.0-docs.sgml	(original)
+++ trunk/doc/C/libgda-4.0-docs.sgml	Mon Mar 17 19:34:20 2008
@@ -302,7 +302,8 @@
           <listitem>
             <para>Create, maintain and extend a view of the database's structure and meta data including the definitions of
 	      the tables, constraints, data types, .... It is defined as closely as possible to the information schema SQL
-	    standard (ISO/IEC 9075).</para>
+	    standard (ISO/IEC 9075); see the <link linkend="gda-dict">Dictionary - metadata</link> section 
+	      for more information.</para>
           </listitem>
           <listitem>
             <para>Easy to extend data models for custom requirements</para>
@@ -627,26 +628,159 @@
       &libgda-GdaHandlerType;
     </chapter>
 
-    <chapter>
+    <chapter id="gda-dict">
       <title>Dictionary - metadata</title>
       <para>
 	Each connection has a dictionary object (a <link linkend="GdaMetaStore">GdaMetaStore</link>) attached to it. That
-	dictionary is either created by the connection when it needs it, or is set by the user (to be able to re-use
-	a dictionary), using the <link linkend="GdaConnection--meta-store">"meta-store"</link> property of the 
-	<link linkend="GdaConnection">GdaConnection</link> object.
+	dictionary is either created by the connection when it needs it, or is created and set by the user 
+	(to be able to re-use a dictionary), using the <link linkend="GdaConnection--meta-store">"meta-store"</link> 
+	property of the <link linkend="GdaConnection">GdaConnection</link> object.
+      </para>
+      <para>
+	Previous versions of &LIBGDA; used an XML file based dictionary which had several drawbacks (see the 
+	<link linkend="migration-2-dict">migrations notes</link> for more details), now a database is used: the default
+	is to use an SQLite base database which is easily transportable and can be used in the same way the previous
+	XML based dictionary was used.
       </para>
       <para>
-	&LIBGDA; requires (and creates) a default dictionary structure which is a set of tables implementing an 
-	"information schema" schema (as defined in the SQL standard and adapted to add more information). The user
-	is then free to add more tables to contain his own data in the dictionary.
+	Each <link linkend="GdaMetaStore">GdaMetaStore</link> requires (and creates) a dictionary 
+	structure which is a set of tables implementing an 
+	"information schema" schema (as defined in the information schema SQL standard (ISO/IEC 9075), and adapted). 
+	The user is then free to add more database objects (tables and views) to contain his own data in the dictionary,
+	using the <link linkend="gda-meta-store-schema-add-custom-object">gda_meta_store_schema_add_custom_object()</link>
+	method.
       </para>
       <para>
-	Extracting information about database objects can easily be done using a 
-	<link linkend="GdaMetaStruct">GdaMetaStruct</link> object which creates an easy to use in-memory representation
-	of some database objects.
+	Extracting information can be done using SQL statements on the dictionary database (a special 
+	<link linkend="gda_meta_store_extract">gda_meta_store_extract()</link> method), or, for information about
+	the database structure, using the <link linkend="GdaMetaStruct">GdaMetaStruct</link> object which creates
+	an easy to use in-memory representation	of some database objects.
       </para>
+
       &libgda-GdaMetaStore;
       &libgda-GdaMetaStruct;
+
+      <sect2 id="GdaMetaStoreSetup">
+	<title>GdaConnection and GdaMetaStore setups</title>
+	<para>
+	  Each <link linkend="GdaMetaStore">GdaMetaStore</link> object internally uses a (private)
+	  <link linkend="GdaConnection">GdaConnection</link> connection object. The following figure illustrates
+	  the situation when the programmer uses a <link linkend="GdaConnection">GdaConnection</link> connection object
+	  when the meta data is stored in a different database (usually an SQLite file):
+	  <mediaobject>
+	    <imageobject role="html">
+              <imagedata fileref="MetaStore1.png" format="PNG"/>
+	    </imageobject>
+	    <textobject>
+              <phrase></phrase>
+	    </textobject>
+	    <caption>
+              <para>
+		GdaConnection object and its associated GdaMetaStore using its own database to store the
+		meta data.
+              </para>
+	    </caption>
+	  </mediaobject>
+	</para>
+	<para>
+	  From a programmer's point of view, the following code example shows how to get a connection's associated
+	  meta store object, without being able to specify anything about the meta store's private connection (in which
+	  case the private connection will be an in-memory database destroyed when the meta store object is destroyed):
+	  <programlisting>
+GdaConnection *cnc;
+GdaMetaStore *store;
+
+cnc = gda_connection_open_from_dsn (...);
+g_object_get (G_OBJECT (cnc), "meta-store", &amp;store, NULL);
+
+[... use the meta store object ...]
+g_object_unref (store);
+	  </programlisting>
+	</para>
+	<para>
+	  One can also specify the meta store object to be used by a connection, as in:
+	  <programlisting>
+GdaConnection *cnc;
+GdaMetaStore *store;
+
+cnc = gda_connection_open_from_dsn (...);
+store = gda_meta_store_new_with_file ("/path/to/file");
+/* or */
+store = gda_meta_store_new ("PostgreSQL://DB_NAME=meta_db");
+
+g_object_set (G_OBJECT (cnc), "meta-store", store, NULL);
+g_object_unref (store);
+	  </programlisting>
+	</para>
+
+	<para>
+	  The meta data can also be stored in the same database as the connection the meta data is for. In this case
+	  (and if the associated database provider supports it), the dictionary structure can be placed into a spearate
+	  schema. The next figure illustrates this situation:
+	  <mediaobject>
+	    <imageobject role="html">
+              <imagedata fileref="MetaStore2.png" format="PNG"/>
+	    </imageobject>
+	    <textobject>
+              <phrase></phrase>
+	    </textobject>
+	    <caption>
+              <para>
+		GdaConnection object and its associated GdaMetaStore using the same database.
+              </para>
+	    </caption>
+	  </mediaobject>
+	</para>
+	<para>
+	  From a programmer's point of view, the following code example shows how to do the setup:
+	  <programlisting>
+GdaConnection *cnc;
+GdaMetaStore *store;
+
+cnc = gda_connection_open_from_dsn (...);
+store = GDA_META_STORE (GDA_TYPE_META_STORE, "cnc", cnc, NULL);
+g_object_set (G_OBJECT (cnc), "meta-store", store, NULL);
+g_object_unref (store);
+	  </programlisting>
+	</para>
+      </sect2>
+
+      <sect2 id="GdaMetaStoreCustomData">
+	<title>How to store custom data in a GdaMetaStore</title>
+	<para>
+	  This section explains how to add application specific data to a #GdaMetaStore object.
+	</para>
+	<para>
+	  Applications of course don't need to use that feature to manage their own data but it makes sense to use
+	  it if the application also uses metadata to avoid having to manipulate several files for the same "task".
+	</para>
+	<sect3>
+	  <title>Storing data as (key, value) pairs</title>
+	  <para>
+	    In very simple cases, when the data to store is made of named values, the easiest way is to use the
+	    <link linkend="gda-meta-store-set-attribute-value">gda_meta_store_set_attribute_value()</link> and
+	    <link linkend="gda-meta-store-get-attribute-value">gda_meta_store_get_attribute_value()</link> methods
+	    where the values are stored and retreived as strings.
+	  </para>
+	</sect3>
+	<sect3>
+	  <title>Declaring the new custom database objects</title>
+	  <para>
+	  </para>
+	</sect3>
+	<sect3>
+	  <title>Adding and removing data</title>
+	  <para>
+	  </para>
+	</sect3>
+	<sect3>
+	  <title>Removing custom database objects</title>
+	  <para>
+	  </para>
+	</sect3>
+	<para>
+	</para>
+      </sect2>
     </chapter>
 
     <chapter>

Modified: trunk/doc/C/migration2.xml
==============================================================================
--- trunk/doc/C/migration2.xml	(original)
+++ trunk/doc/C/migration2.xml	Mon Mar 17 19:34:20 2008
@@ -49,7 +49,7 @@
       See this object's documentation for more information.</para>
   </sect1>  
 
-  <sect1><title>Dictionary changes</title>
+  <sect1 id="migration-2-dict"><title>Dictionary changes</title>
     <para>The GdaDict object has been removed and the <link linkend="GdaMetaStore">GdaMetaStore</link> object introduced to
       replace it, but with slightly different features.</para>
     <para>The GdaDict object used an XML file to store its data which imposed parsing potentially big XML files and creating its own

Modified: trunk/doc/C/tmpl/gda-meta-store.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-meta-store.sgml	(original)
+++ trunk/doc/C/tmpl/gda-meta-store.sgml	Mon Mar 17 19:34:20 2008
@@ -68,6 +68,11 @@
 @arg1: 
 @Returns: 
 
+<!-- ##### ARG GdaMetaStore:catalog ##### -->
+<para>
+
+</para>
+
 <!-- ##### ARG GdaMetaStore:cnc ##### -->
 <para>
 
@@ -78,6 +83,11 @@
 
 </para>
 
+<!-- ##### ARG GdaMetaStore:schema ##### -->
+<para>
+
+</para>
+
 <!-- ##### ENUM GdaMetaStoreError ##### -->
 <para>
 
@@ -86,10 +96,12 @@
 @GDA_META_STORE_INCORRECT_SCHEMA: 
 @GDA_META_STORE_UNSUPPORTED_PROVIDER: 
 @GDA_META_STORE_INTERNAL_ERROR: 
+ GDA_META_STORE_META_CONTEXT_ERROR: 
 @GDA_META_STORE_MODIFY_CONTENTS_ERROR: 
 @GDA_META_STORE_EXTRACT_SQL_ERROR: 
 @GDA_META_STORE_ATTRIBUTE_NOT_FOUND_ERROR: 
 @GDA_META_STORE_ATTRIBUTE_ERROR: 
+ GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR: 
 @GDA_META_STORE_SCHEMA_OBJECT_CONFLICT_ERROR: 
 @GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR: 
 

Modified: trunk/doc/C/tmpl/gda-server-provider.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-server-provider.sgml	(original)
+++ trunk/doc/C/tmpl/gda-server-provider.sgml	Mon Mar 17 19:34:20 2008
@@ -103,6 +103,8 @@
 @constraints_tab: 
 @_constraints_ref: 
 @constraints_ref: 
+ _key_columns: 
+ key_columns: 
 
 <!-- ##### USER_FUNCTION GdaServerProviderAsyncCallback ##### -->
 <para>

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Mon Mar 17 19:34:20 2008
@@ -1676,6 +1676,65 @@
 	return gda_server_provider_supports_feature (cnc->priv->provider_obj, cnc, feature);
 }
 
+/* builds a list of #GdaMetaContext contexts templates: contexts which have a non NULL table_name,
+ * and empty or partially filled column names and values specifications */
+GSList *
+build_upstream_context_templates (GdaMetaStore *store, GdaMetaContext *context, GSList *elist, GError **error)
+{
+	GSList *depend_on_contexts;
+	GSList *retlist;
+	GError *lerror = NULL;
+	depend_on_contexts = _gda_meta_store_schema_get_upstream_contexts (store, context, &lerror);
+	if (!depend_on_contexts) {
+		if (lerror) {
+			/* error while getting dependencies */
+			g_propagate_error (error, lerror);
+			return FALSE;
+		}
+		return elist;
+	}
+	else {
+		GSList *list;
+		retlist = NULL;
+		for (list = depend_on_contexts; list; list = list->next) 
+			retlist = build_upstream_context_templates (store, (GdaMetaContext *) list->data,
+								    retlist, error);
+		list = g_slist_concat (depend_on_contexts, elist);
+		retlist = g_slist_concat (retlist, list);
+		return retlist;
+	}
+}
+
+
+/* builds a list of #GdaMetaContext contexts templates: contexts which have a non NULL table_name,
+ * and empty or partially filled column names and values specifications */
+GSList *
+build_downstream_context_templates (GdaMetaStore *store, GdaMetaContext *context, GSList *elist, GError **error)
+{
+	GSList *depending_contexts;
+	GSList *retlist;
+	GError *lerror = NULL;
+	depending_contexts = _gda_meta_store_schema_get_downstream_contexts (store, context, &lerror);
+	if (!depending_contexts) {
+		if (lerror) {
+			/* error while getting dependencies */
+			g_propagate_error (error, lerror);
+			return NULL;
+		}
+		return elist;
+	}
+	else {
+		GSList *list;
+		retlist = NULL;
+		for (list = depending_contexts; list; list = list->next) 
+			retlist = build_downstream_context_templates (store, (GdaMetaContext *) list->data,
+								      retlist, error);
+		list = g_slist_concat (elist, depending_contexts);
+		retlist = g_slist_concat (list, retlist);
+		return retlist;
+	}
+}
+
 /*
  *
  */
@@ -1742,12 +1801,29 @@
 	}
 	else 
 		g_set_error (error, 0, 0,
-			     _("Missing and/or wrong arguments"));
+			     _("Missing or wrong arguments"));
 
 	/*g_print ("Check arguments context => found %d\n", retval);*/
 	return retval;
 }
 
+static void
+meta_context_dump (GdaMetaContext *context)
+{
+	gint i;
+	g_print ("GdaMetaContext for table %s:", context->table_name);
+	for (i = 0; i < context->size; i++) {
+		gchar *str;
+		str = gda_value_stringify (context->column_values[i]);
+		g_print (" [%s => %s]", context->column_names[i], str);
+		g_free (str);
+	}
+	if (i == 0)
+		g_print (" ---\n");
+	else
+		g_print ("\n");
+}
+
 static gboolean
 local_meta_update (GdaServerProvider *provider, GdaConnection *cnc, GdaMetaContext *context, GError **error)
 {
@@ -1764,6 +1840,11 @@
 	GdaMetaStore *store;
 	gboolean retval;
 
+#ifdef GDA_DEBUG_NO
+	g_print ("%s() => ", __FUNCTION__);
+	meta_context_dump (context);
+#endif
+
 	if (*tname != '_')
 		return TRUE;
 	tname ++;
@@ -1783,18 +1864,7 @@
 		WARN_META_UPDATE_FAILURE (retval, "_btypes");
 		return retval;
 	}
-	case 'i':
-		/* _information_schema_catalog_name, params: 
-		 *  - none
-		 */
-		ASSERT_TABLE_NAME (tname, "information_schema_catalog_name");
-		if (!PROV_CLASS (provider)->meta_funcs._info) {
-			WARN_METHOD_NOT_IMPLEMENTED (provider, "_info");
-			break;
-		}
-		retval = PROV_CLASS (provider)->meta_funcs._info (provider, cnc, store, context, error);
-		WARN_META_UPDATE_FAILURE (retval, "_info");
-		return retval;
+	
 	case 'c': 
 		if ((tname[1] == 'o') && (tname[2] == 'l') && (tname[3] == 'u')) {
 			/* _columns,  params: 
@@ -1834,6 +1904,56 @@
 		}
 		break;
 
+	case 'i':
+		/* _information_schema_catalog_name, params: 
+		 *  - none
+		 */
+		ASSERT_TABLE_NAME (tname, "information_schema_catalog_name");
+		if (!PROV_CLASS (provider)->meta_funcs._info) {
+			WARN_METHOD_NOT_IMPLEMENTED (provider, "_info");
+			break;
+		}
+		retval = PROV_CLASS (provider)->meta_funcs._info (provider, cnc, store, context, error);
+		WARN_META_UPDATE_FAILURE (retval, "_info");
+		return retval;
+
+	case 'k': {
+		/* _key_column_usage, params: 
+		 *  -0- @table_catalog, @table_schema, @table_name, @constraint_name
+		 *  -0- @ref_table_catalog, @ref_table_schema, @ref_table_name, @ref_constraint_name
+		 */
+		const GValue *catalog = NULL;
+		const GValue *schema = NULL;
+		const GValue *tabname = NULL;
+		const GValue *cname = NULL;
+		gint i;
+		i = check_parameters (context, error, 2,
+				      &catalog, G_TYPE_STRING,
+				      &schema, G_TYPE_STRING,
+				      &tabname, G_TYPE_STRING,
+				      &cname, G_TYPE_STRING, NULL,
+				      "table_catalog", &catalog, "table_schema", &schema, "table_name", &tabname, "constraint_name", &cname, NULL,
+				      "table_catalog", &catalog, "table_schema", &schema, "table_name", &tabname, "column_name", &cname, NULL);
+		if (i < 0)
+			return FALSE;
+		
+		ASSERT_TABLE_NAME (tname, "key_column_usage");
+		if (i == 0) {
+			if (!PROV_CLASS (provider)->meta_funcs.key_columns) {
+				WARN_METHOD_NOT_IMPLEMENTED (provider, "key_columns");
+				break;
+			}
+			retval = PROV_CLASS (provider)->meta_funcs.key_columns (provider, cnc, store, context, error, 
+										catalog, schema, tabname, cname);
+			WARN_META_UPDATE_FAILURE (retval, "key_columns");
+			return retval;
+		}
+		else {
+			/* nothing to do */
+			return TRUE;
+		}
+	}
+
 	case 'r': 
 		if ((tname[1] == 'e') && (tname[2] == 'f')) {
 			/* _referential_constraints, params: 
@@ -1971,14 +2091,90 @@
 	GdaConnection      *cnc;
 	GError            **error;
 	gboolean            error_set;
-} DetailledCallbackData;
+	GSList             *context_templates;
+	GHashTable         *context_templates_hash;
+} DownstreamCallbackData;
 
 static GError *
-suggest_update_cb_detailled (GdaMetaStore *store, GdaMetaContext *suggest, DetailledCallbackData *data)
+suggest_update_cb_downstream (GdaMetaStore *store, GdaMetaContext *suggest, DownstreamCallbackData *data)
 {
+#define MAX_CONTEXT_SIZE 10
 	if (data->error && *(data->error))
 		return *(data->error);
 
+	GdaMetaContext *templ_context;
+	GdaMetaContext loc_suggest;
+
+	/* if there is no context with the same table name in the templates, then exit right now */
+	templ_context = g_hash_table_lookup (data->context_templates_hash, suggest->table_name);
+	if (!templ_context)
+		return NULL;
+	
+	if (templ_context->size > 0) {
+		/* setup @loc_suggest */
+
+		gchar *column_names[MAX_CONTEXT_SIZE];
+		GValue *column_values[MAX_CONTEXT_SIZE];
+		gint i, j;
+
+		if (suggest->size > MAX_CONTEXT_SIZE) {
+			g_warning ("Internal limitation at %s(), limitation should be at least %d, please report a bug",
+				   __FUNCTION__, suggest->size);
+			return NULL;
+		}
+		loc_suggest.size = suggest->size;
+		loc_suggest.table_name = suggest->table_name;
+		loc_suggest.column_names = column_names;
+		loc_suggest.column_values = column_values;
+		memcpy (loc_suggest.column_names, suggest->column_names, sizeof (gchar *) * suggest->size);
+		memcpy (loc_suggest.column_values, suggest->column_values, sizeof (GValue *) * suggest->size);	
+		
+		/* check that any @suggest's columns which is in @templ_context's has the same values */
+		for (j = 0; j < suggest->size; j++) {
+			for (i = 0; i < templ_context->size; i++) {
+				if (!strcmp (templ_context->column_names[i], suggest->column_names[j])) {
+					/* same column name, now check column value */
+					if (G_VALUE_TYPE (templ_context->column_values[i]) != 
+					    G_VALUE_TYPE (suggest->column_values[j])) {
+						g_warning ("Internal error: column types mismatch for GdaMetaContext "
+							   "table '%s' and column '%s' (%s/%s)",
+							   templ_context->table_name, templ_context->column_names[i], 
+							   g_type_name (G_VALUE_TYPE (templ_context->column_values[i])),
+							   g_type_name (G_VALUE_TYPE (suggest->column_values[j])));
+						return NULL;
+					}
+					if (gda_value_compare_ext (templ_context->column_values[i], 
+								   (suggest->column_values[j])))
+						/* different values */
+						return NULL;
+					break;
+				}
+			}
+		}
+
+		/* @templ_context may contain some more columns => add them to @loc_suggest */
+		for (i = 0; i < templ_context->size; i++) {
+			for (j = 0; j < suggest->size; j++) {
+				if (!strcmp (templ_context->column_names[i], suggest->column_names[j])) {
+					j = -1;
+					break;
+				}
+			}
+			if (j >= 0) {
+				if (loc_suggest.size >= MAX_CONTEXT_SIZE) {
+					g_warning ("Internal limitation at %s(), limitation should be at least %d, please report a bug",
+						   __FUNCTION__, loc_suggest.size + 1);
+					return NULL;
+				}
+				loc_suggest.column_names [loc_suggest.size] = templ_context->column_names [i];
+				loc_suggest.column_values [loc_suggest.size] = templ_context->column_values [i];
+				loc_suggest.size ++;
+			}
+		}
+
+		suggest = &loc_suggest;
+	}
+
 	if (!local_meta_update (data->prov, data->cnc, suggest, data->error)) {
 		data->error_set = TRUE;
 
@@ -1988,11 +2184,10 @@
 
 		return *(data->error);
 	}
+
 	return NULL;
 }
 
-static gboolean gda_connection_update_meta_clean_first = TRUE;
-
 /**
  * gda_connection_update_meta_store
  * @cnc: a #GdaConnection object.
@@ -2008,7 +2203,6 @@
 gda_connection_update_meta_store (GdaConnection *cnc, GdaMetaContext *context, GError **error)
 {
 	GdaMetaStore *store;
-	gboolean retval = TRUE;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (cnc->priv->provider_obj, FALSE);
@@ -2019,15 +2213,97 @@
 
 	/* prepare local context */
 	GdaMetaContext lcontext;
+
 	if (context) {
+		GSList *list;
+		GSList *up_templates;
+		GSList *dn_templates;
+		GError *lerror = NULL;
 		lcontext = *context;
 		/* alter local context because "_tables" and "_views" always go together so only
 		   "_tables" should be updated and providers should always update "_tables" and "_views"
 		*/
 		if (!strcmp (lcontext.table_name, "_views"))
 			lcontext.table_name = "_tables";
+
+		up_templates = build_upstream_context_templates (store, context, NULL, &lerror);
+		if (!up_templates) {
+			if (lerror) {
+				g_propagate_error (error, lerror);
+				return FALSE;
+			}
+		}
+		dn_templates = build_downstream_context_templates (store, context, NULL, &lerror);
+		if (!dn_templates) {
+			if (lerror) {
+				g_propagate_error (error, lerror);
+				return FALSE;
+			}
+		}
+
+#ifdef GDA_DEBUG_NO
+		g_print ("\n*********** TEMPLATES:\n");
+		for (list = up_templates; list; list = list->next) {
+			g_print ("UP: ");
+			meta_context_dump ((GdaMetaContext*) list->data);
+		}
+		g_print ("->: ");
+		meta_context_dump (context);
+		for (list = dn_templates; list; list = list->next) {
+			g_print ("DN: ");
+			meta_context_dump ((GdaMetaContext*) list->data);
+		}
+#endif
+					
+		gulong signal_id;
+		DownstreamCallbackData cbd;
+		gboolean retval = TRUE;
+		
+		cbd.prov = cnc->priv->provider_obj;
+		cbd.cnc = cnc;
+		cbd.error = &lerror;
+		cbd.error_set = FALSE;
+		cbd.context_templates = g_slist_concat (g_slist_append (up_templates, context), dn_templates);
+		cbd.context_templates_hash = g_hash_table_new (g_str_hash, g_str_equal);
+		for (list = cbd.context_templates; list; list = list->next) 
+			g_hash_table_insert (cbd.context_templates_hash, ((GdaMetaContext*)list->data)->table_name,
+					     list->data);
+		
+		signal_id = g_signal_connect (store, "suggest_update",
+					      G_CALLBACK (suggest_update_cb_downstream), &cbd);
+		
+		retval = local_meta_update (cnc->priv->provider_obj, cnc, 
+					    (GdaMetaContext*) (cbd.context_templates->data), error);
+		
+		g_signal_handler_disconnect (store, signal_id);
+		if (cbd.error_set) {
+			if (lerror) {
+				if (error && !*error)
+					g_propagate_error (error, lerror);
+				else
+					g_error_free (lerror);
+			}
+			retval = FALSE;
+		}
+
+		/* free the memory associated with each template */
+		for (list = cbd.context_templates; list; list = list->next) {
+			GdaMetaContext *c = (GdaMetaContext *) list->data;
+			if (c != context) {
+				if (c->size > 0) {
+					g_free (c->column_names);
+					g_free (c->column_values);
+				}
+				g_free (c);
+			}
+		}
+		g_slist_free (cbd.context_templates);
+		g_hash_table_destroy (cbd.context_templates_hash);
+
+		return retval;
 	}
 	else {
+		/* no context specified => update everything */
 		memset (&lcontext, 0, sizeof (GdaMetaContext));
 		lcontext.table_name = "_builtin_data_types";
 		if (!gda_connection_update_meta_store (cnc, &lcontext, error))
@@ -2040,29 +2316,6 @@
 			return FALSE;
 		return TRUE;
 	}
-	
-	/* actual update */
-	gulong signal_id;
-	DetailledCallbackData cbd;
-	GError *lerror = NULL;
-	
-	cbd.prov = cnc->priv->provider_obj;
-	cbd.cnc = cnc;
-	cbd.error = &lerror;
-	cbd.error_set = FALSE;
-	signal_id = g_signal_connect (store, "suggest_update",
-				      G_CALLBACK (suggest_update_cb_detailled), &cbd);
-	
-	retval = local_meta_update (cnc->priv->provider_obj, cnc, &lcontext, NULL);
-	
-	g_signal_handler_disconnect (store, signal_id);
-	if (cbd.error_set) {
-		if (lerror)
-			g_propagate_error (error, lerror);
-		retval = FALSE;
-	}
-
-	return retval;
 }
 
 /*

Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c	(original)
+++ trunk/libgda/gda-data-model.c	Mon Mar 17 19:34:20 2008
@@ -165,7 +165,10 @@
  * @model: a #GdaDataModel object.
  * @row: row number.
  *
- * Emits the 'row_inserted' and 'changed' signals on @model.
+ * Emits the 'row_inserted' and 'changed' signals on @model. 
+ *
+ * This method should only be used by #GdaDataModel implementations to 
+ * signal that a row has been inserted.
  */
 void
 gda_data_model_row_inserted (GdaDataModel *model, gint row)
@@ -204,6 +207,9 @@
  * @row: row number.
  *
  * Emits the 'row_updated' and 'changed' signals on @model.
+ *
+ * This method should only be used by #GdaDataModel implementations to 
+ * signal that a row has been updated.
  */
 void
 gda_data_model_row_updated (GdaDataModel *model, gint row)
@@ -225,6 +231,9 @@
  * @row: row number.
  *
  * Emits the 'row_removed' and 'changed' signal on @model.
+ *
+ * This method should only be used by #GdaDataModel implementations to 
+ * signal that a row has been removed
  */
 void
 gda_data_model_row_removed (GdaDataModel *model, gint row)
@@ -536,8 +545,10 @@
  * gda_data_model_create_iter().
  *
  * Note that the returned #GValue must not be modified directly (unexpected behaviours may
- * occur if you do so). If you want to
- * modify a value stored in a #GdaDataModel, use the gda_data_model_set_value() method.
+ * occur if you do so). Also that value may become invalid as soon as any Libgda part is executed again,
+ * which means if you want to keep the value, a copy must be made. 
+ *
+ * If you want to modify a value stored in a #GdaDataModel, use the gda_data_model_set_value() method.
  *
  * Returns: a #GValue containing the value stored in the given
  * position, or %NULL on error (out-of-bound position, etc).

Modified: trunk/libgda/gda-meta-store.c
==============================================================================
--- trunk/libgda/gda-meta-store.c	(original)
+++ trunk/libgda/gda-meta-store.c	Mon Mar 17 19:34:20 2008
@@ -116,6 +116,8 @@
  * It is available for tables, views, triggers, ...
  */
 typedef struct {
+	GdaMetaStore            *store; /* if not NULL, the store in which this db object is, 
+					 * or %NULL if it's a class db object */
 	GdaServerOperationType  obj_type;
 	gchar                  *obj_name; /* may be %NULL */
 	GdaServerOperation     *create_op; /* may be %NULL */
@@ -195,12 +197,15 @@
 	gint           version;
 	gboolean       schema_ok;
 
-	GSList        *custom_db_objects; /* list of DbObject structures */
-	GHashTable    *custom_db_objects_hash; /* key = table name, value = a DbObject structure */
+	gchar         *catalog; /* name of the catalog in which all the objects are created, or NULL if none specified */
+	gchar         *schema;  /* name of the schema in which all the objects are created, or NULL if none specified */
+
+	GSList        *p_db_objects; /* list of DbObject structures */
+	GHashTable    *p_db_objects_hash; /* key = table name, value = a DbObject structure */
 };
 
 static void db_object_free    (DbObject *dbobj);
-static void create_db_objects (GdaMetaStoreClass *klass);
+static void create_db_objects (GdaMetaStoreClass *klass, GdaMetaStore *store);
 
 
 /* get a pointer to the parents to be able to call their destructor */
@@ -220,6 +225,8 @@
 	PROP_0,
 	PROP_CNC_STRING,
 	PROP_CNC_OBJECT,
+	PROP_CATALOG,
+	PROP_SCHEMA
 };
 
 /* module error */
@@ -308,7 +315,8 @@
 }
 
 static void
-gda_meta_store_class_init (GdaMetaStoreClass *klass) {
+gda_meta_store_class_init (GdaMetaStoreClass *klass) 
+{
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	
 	parent_class = g_type_class_peek_parent (klass);
@@ -354,6 +362,12 @@
 		g_param_spec_object ("cnc", _ ("Connection object internally used"),
 		NULL, GDA_TYPE_CONNECTION,
 		(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+	g_object_class_install_property (object_class, PROP_CATALOG,
+		g_param_spec_string ("catalog", _ ("Catalog in which the database objects will be created"), NULL, NULL,
+		(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
+	g_object_class_install_property (object_class, PROP_SCHEMA,
+		g_param_spec_string ("schema", _ ("Schema in which the database objects will be created"), NULL, NULL,
+		(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)));
 	
 	object_class->constructor = gda_meta_store_constructor;
 	object_class->dispose = gda_meta_store_dispose;
@@ -365,7 +379,7 @@
 	klass->cpriv->parser = gda_sql_parser_new ();
 	klass->cpriv->provider_specifics = g_hash_table_new (ProviderSpecific_hash, ProviderSpecific_equal);
 	klass->cpriv->db_objects_hash = g_hash_table_new (g_str_hash, g_str_equal);
-	create_db_objects (klass);
+	create_db_objects (klass, NULL);
         klass->cpriv->table_cond_info_hash = g_hash_table_new (g_str_hash, g_str_equal);
 
 	klass->cpriv->prep_stmts[STMT_SET_VERSION] =
@@ -424,20 +438,25 @@
 
 
 static void
-gda_meta_store_init (GdaMetaStore *store) {
+gda_meta_store_init (GdaMetaStore *store) 
+{
 	store->priv = g_new0 (GdaMetaStorePrivate, 1);
 	store->priv->cnc = NULL;
 	store->priv->schema_ok = FALSE;
 	store->priv->version = 0;
 
-	store->priv->custom_db_objects = NULL;
-	store->priv->custom_db_objects_hash = g_hash_table_new (g_str_hash, g_str_equal);
+	store->priv->catalog = NULL;
+	store->priv->schema = NULL;
+
+	store->priv->p_db_objects = NULL;
+	store->priv->p_db_objects_hash = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 static GObject *
 gda_meta_store_constructor (GType type,
-	guint n_construct_properties,
-	GObjectConstructParam *construct_properties) {
+			    guint n_construct_properties,
+			    GObjectConstructParam *construct_properties) 
+{
 	GObject *object;
 	gint i;
 	GError *error = NULL;
@@ -473,6 +492,25 @@
 				g_error_free (error);
 		}
 	}
+
+	/* create a local copy of all the DbObject structures defined in klass->cpriv */
+	if (store->priv->catalog && !store->priv->schema) {
+		g_warning (_("Catalog specified but no schema specified, store will not be useable"));
+		store->priv->schema_ok = FALSE;
+	}
+	else {
+		GdaMetaStoreClass *klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
+		GSList *list;
+		if (!store->priv->schema) {
+			/* uses all the klass->cpriv->db_objects AS IS in store->priv->p_db_objects */
+			store->priv->p_db_objects = g_slist_copy (klass->cpriv->db_objects);
+			for (list = store->priv->p_db_objects; list; list = list->next) 
+				g_hash_table_insert (store->priv->p_db_objects_hash, DB_OBJECT (list->data)->obj_name,
+						     list->data);
+		}
+		else 
+			create_db_objects (klass, store);
+	}
 	
 	return object;
 }
@@ -543,10 +581,17 @@
 	
 	store = GDA_META_STORE (object);
 	if (store->priv) {
+		GSList *list;
+		g_free (store->priv->catalog);
+		g_free (store->priv->schema);
+
 		/* custom db objects */
-		g_hash_table_destroy (store->priv->custom_db_objects_hash);
-		g_slist_foreach (store->priv->custom_db_objects, (GFunc) db_object_free, NULL);
-		g_slist_free (store->priv->custom_db_objects);
+		g_hash_table_destroy (store->priv->p_db_objects_hash);
+		for (list = store->priv->p_db_objects; list; list = list->next) {
+			if (DB_OBJECT (list->data)->store == store)
+				db_object_free (DB_OBJECT (list->data));
+		}
+		g_slist_free (store->priv->p_db_objects);
 
 		/* internal connection */
 		if (store->priv->cnc) {
@@ -589,20 +634,34 @@
 	store = GDA_META_STORE (object);
 	if (store->priv) {
 		switch (param_id) {
-			case PROP_CNC_STRING:
-				if (!store->priv->cnc) {
-					cnc_string = g_value_get_string (value);
-					if (cnc_string) {
-						GdaConnection *cnc;
-						cnc = gda_connection_open_from_string (NULL, cnc_string, NULL, 0, NULL);
-						store->priv->cnc = cnc;
-					}
+		case PROP_CNC_STRING:
+			if (!store->priv->cnc) {
+				cnc_string = g_value_get_string (value);
+				if (cnc_string) {
+					GdaConnection *cnc;
+					cnc = gda_connection_open_from_string (NULL, cnc_string, NULL, 0, NULL);
+					store->priv->cnc = cnc;
 				}
-				break;
-			case PROP_CNC_OBJECT:
-				if (!store->priv->cnc)
-					store->priv->cnc = g_value_get_object (value);
-				break;
+			}
+			break;
+		case PROP_CNC_OBJECT:
+			if (!store->priv->cnc)
+				store->priv->cnc = g_value_get_object (value);
+			break;
+		case PROP_CATALOG:
+			g_free (store->priv->catalog);
+			if (g_value_get_string (value) && *g_value_get_string (value))
+				store->priv->catalog = g_strdup (g_value_get_string (value));
+			else
+				store->priv->catalog = NULL;
+			break;
+		case PROP_SCHEMA:
+			g_free (store->priv->schema);
+			if (g_value_get_string (value) && *g_value_get_string (value))
+				store->priv->schema = g_strdup (g_value_get_string (value));
+			else
+				store->priv->schema = NULL;
+			break;
 		}
 	}
 }
@@ -618,12 +677,18 @@
 	
 	if (store->priv) {
 		switch (param_id) {
-			case PROP_CNC_STRING:
-				g_assert_not_reached ();
-				break;
-			case PROP_CNC_OBJECT:
-				g_value_set_object (value, (GObject *) store->priv->cnc);
-				break;
+		case PROP_CNC_STRING:
+			g_assert_not_reached ();
+			break;
+		case PROP_CNC_OBJECT:
+			g_value_set_object (value, (GObject *) store->priv->cnc);
+			break;
+		case PROP_CATALOG:
+			g_value_set_string (value, store->priv->catalog);
+			break;
+		case PROP_SCHEMA:
+			g_value_set_string (value, store->priv->schema);
+			break;
 		}
 	}
 }
@@ -694,44 +759,54 @@
 static GdaServerOperation *create_server_operation_for_view  (GHashTable *specific_hash, 
 							      GdaServerProvider *prov, GdaConnection *cnc, 
 							      DbObject *dbobj, GError **error);
+static gboolean prepare_dbo_server_operation (GdaMetaStoreClass *klass, GdaMetaStore *store, GdaServerProvider *prov,
+					      DbObject *dbo, GError **error);
 static gboolean
 prepare_server_operations (GdaMetaStore *store, GError **error)
 {
 	GSList *objects;
-	GdaServerProvider *prov;
 	GdaMetaStoreClass *klass;
 
 	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
-	prov = gda_connection_get_provider_obj (store->priv->cnc);
 
 	for (objects = klass->cpriv->db_objects; objects; objects = objects->next) {
 		DbObject *dbo = DB_OBJECT (objects->data);
-		if (dbo->create_op) {
-			g_object_unref (dbo->create_op);
-			dbo->create_op = NULL;
-		}
-		
-		switch (dbo->obj_type) {
-		case GDA_SERVER_OPERATION_CREATE_TABLE:
-			dbo->create_op = create_server_operation_for_table (klass->cpriv->provider_specifics, 
-									    prov, store->priv->cnc, dbo, error);
-			if (!dbo->create_op)
-				return FALSE;
-			break;
-		case GDA_SERVER_OPERATION_CREATE_VIEW:
-			dbo->create_op = create_server_operation_for_view (klass->cpriv->provider_specifics,
-									   prov, store->priv->cnc, dbo, error);
-			if (!dbo->create_op)
-				return FALSE;
-			break;
-		default:
-			break;
-		}
+		if (! prepare_dbo_server_operation (klass, store, gda_connection_get_provider_obj (store->priv->cnc), 
+						    dbo, error))
+			return FALSE;
 	}
 
 	return TRUE;
 }
 
+static gboolean
+prepare_dbo_server_operation (GdaMetaStoreClass *klass, GdaMetaStore *store, GdaServerProvider *prov,
+			      DbObject *dbo, GError **error)
+{
+	if (dbo->create_op) {
+		g_object_unref (dbo->create_op);
+		dbo->create_op = NULL;
+	}
+	
+	switch (dbo->obj_type) {
+	case GDA_SERVER_OPERATION_CREATE_TABLE:
+		dbo->create_op = create_server_operation_for_table (klass->cpriv->provider_specifics, 
+								    prov, store->priv->cnc, dbo, error);
+		if (!dbo->create_op)
+			return FALSE;
+		break;
+	case GDA_SERVER_OPERATION_CREATE_VIEW:
+		dbo->create_op = create_server_operation_for_view (klass->cpriv->provider_specifics,
+								   prov, store->priv->cnc, dbo, error);
+		if (!dbo->create_op)
+			return FALSE;
+		break;
+	default:
+		break;
+	}
+	return TRUE;
+}
+
 static const gchar *
 provider_specific_match (GHashTable *specific_hash, GdaServerProvider *prov, const gchar *expr, const gchar *path)
 {
@@ -844,16 +919,19 @@
 	return NULL;
 }
 
-static DbObject *create_table_object (GdaMetaStoreClass *klass, xmlNodePtr node, GError **error);
-static DbObject *create_view_object (GdaMetaStoreClass *klass, xmlNodePtr node, GError **error);
+static DbObject *create_table_object (GdaMetaStoreClass *klass, GdaMetaStore *store, xmlNodePtr node, GError **error);
+static DbObject *create_view_object (GdaMetaStoreClass *klass, GdaMetaStore *store, xmlNodePtr node, GError **error);
 static GSList *reorder_db_objects (GSList *objects, GHashTable *hash);
 static gboolean complement_db_objects (GSList *objects, GHashTable *hash, GError **error);
 
 /*
- * Creates all DbObject structures and place them into klass->cpriv->db_objects
+ * Creates all DbObject structures and place them into klass->cpriv->db_objects or store->priv->p_db_objects
+ *
+ * If @store is %NULL, then all the DB objects created are attached to klass->cpriv->db_objects, otherwise
+ * they are placed in store->priv->p_db_objects
  */
 static void
-create_db_objects (GdaMetaStoreClass *klass)
+create_db_objects (GdaMetaStoreClass *klass, GdaMetaStore *store)
 {
 	xmlNodePtr node;
 	GError *lerror = NULL;
@@ -864,8 +942,10 @@
 	/* load information schema's structure XML file */
 	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "information_schema.xml", NULL);
 	doc = xmlParseFile (file);
-	if (!doc) 
-		g_error ("Missing or malformed file '%s', check your installation", file);
+	if (!doc) {
+		g_warning ("Missing or malformed file '%s', check your installation", file);
+		return;
+	}
 	
 	node = xmlDocGetRootElement (doc);
 	if (!node || strcmp ((gchar *) node->name, "schema")) 
@@ -873,7 +953,10 @@
 	g_free (file);
 	
 	/* walk through the xmlDoc */
-	klass->cpriv->db_objects = NULL;
+	if (store)
+		store->priv->p_db_objects = NULL;
+	else
+		klass->cpriv->db_objects = NULL;
 	for (node = node->children; node; node = node->next) {
 		/* <specifics> tag to allow for provider specific transformations */
 		if (!strcmp ((gchar *) node->name, "specifics")) {
@@ -920,7 +1003,7 @@
 		/* <table> tag for table creation */
 		else if (!strcmp ((gchar *) node->name, "table")) {
 			DbObject *dbo;
-			dbo = create_table_object (klass, node, error);
+			dbo = create_table_object (klass, store, node, error);
 			if (!dbo)
 				g_error ("Information schema creation error: %s", 
 					 lerror && lerror->message ? lerror->message : "No detail");
@@ -928,7 +1011,7 @@
 		/* <view> tag for view creation */
 		else if (!strcmp ((gchar *) node->name, "view")) {
 			DbObject *dbo;
-			dbo = create_view_object (klass, node, error);
+			dbo = create_view_object (klass, store, node, error);
 			if (!dbo)
 				g_error ("Information schema creation error: %s", 
 					 lerror && lerror->message ? lerror->message : "No detail");
@@ -936,10 +1019,18 @@
 	}
 	xmlFreeDoc (doc);
 	
-	klass->cpriv->db_objects = reorder_db_objects (klass->cpriv->db_objects, klass->cpriv->db_objects_hash);
-	if (!complement_db_objects (klass->cpriv->db_objects, klass->cpriv->db_objects_hash, error)) 
-		g_error ("Information schema structure error: %s", 
-			 lerror && lerror->message ? lerror->message : "No detail");
+	if (store) {
+		store->priv->p_db_objects = reorder_db_objects (store->priv->p_db_objects, store->priv->p_db_objects_hash);
+		if (!complement_db_objects (store->priv->p_db_objects, store->priv->p_db_objects_hash, error)) 
+			g_error ("Information schema structure error: %s", 
+				 lerror && lerror->message ? lerror->message : "No detail");
+	}
+	else {
+		klass->cpriv->db_objects = reorder_db_objects (klass->cpriv->db_objects, klass->cpriv->db_objects_hash);
+		if (!complement_db_objects (klass->cpriv->db_objects, klass->cpriv->db_objects_hash, error)) 
+			g_error ("Information schema structure error: %s", 
+				 lerror && lerror->message ? lerror->message : "No detail");
+	}
 }
 
 /*
@@ -956,26 +1047,45 @@
 	return -1;
 }
 
-static void compute_view_dependencies (GdaMetaStoreClass *klass, DbObject *view_dbobj, GdaSqlStatement *sqlst);
+static void compute_view_dependencies (GdaMetaStoreClass *klass, GdaMetaStore *store, 
+				       DbObject *view_dbobj, GdaSqlStatement *sqlst);
 static DbObject *
-create_view_object (GdaMetaStoreClass *klass, xmlNodePtr node, GError **error)
+create_view_object (GdaMetaStoreClass *klass, GdaMetaStore *store, xmlNodePtr node, GError **error)
 {
 	DbObject *dbobj;
 	xmlChar *view_name;
+	gchar *complete_obj_name;
+
 	view_name = xmlGetProp (node, BAD_CAST "name");
 	if (!view_name) {
 		g_set_error (error, 0, 0, 
 			     _("Missing view name from <view> node"));
 		goto onerror;
 	}
-	
+
+	/* determine object's complete name */
+	if (store && store->priv->schema) 
+		complete_obj_name = g_strdup_printf ("%s.%s", store->priv->schema, (gchar *) view_name);
+	else
+		complete_obj_name = g_strdup ((gchar *) view_name);
+
 	/* DbObject structure */
-	dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, view_name);
+	if (store)
+		dbobj = g_hash_table_lookup (store->priv->p_db_objects_hash, view_name);
+	else
+		dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, view_name);
 	if (!dbobj) {
 		dbobj = g_new0 (DbObject, 1);
-		klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, dbobj);
+		dbobj->store = store;
 		dbobj->obj_name = g_strdup ((gchar *) view_name);
-		g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
+		if (store) {
+			store->priv->p_db_objects = g_slist_prepend (store->priv->p_db_objects, dbobj);
+			g_hash_table_insert (store->priv->p_db_objects_hash, dbobj->obj_name, dbobj);
+		}
+		else {
+			klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, dbobj);
+			g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
+		}
 	}
 	xmlFree (view_name);
 	dbobj->obj_type = GDA_SERVER_OPERATION_CREATE_VIEW;
@@ -1004,7 +1114,7 @@
 		if (remain) {
 			g_set_error (error, 0, 0, 
 				     _("View definition contains more than one statement (for view '%s')"),
-				     dbobj->obj_name);
+				     complete_obj_name);
 			g_object_unref (stmt);
 			xmlFree (def);
 			goto onerror;
@@ -1016,12 +1126,12 @@
 		    (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND)) {
 			GdaSqlStatement *sqlst;
 			g_object_get (G_OBJECT (stmt), "structure", &sqlst, NULL);
-			compute_view_dependencies (klass, dbobj, sqlst);
+			compute_view_dependencies (klass, store, dbobj, sqlst);
 			gda_sql_statement_free (sqlst);
 			g_object_unref (stmt);
 					
 #ifdef GDA_DEBUG_NO
-			g_print ("View %s depends on: ", dbobj->obj_name);
+			g_print ("View %s depends on: ", complete_obj_name);
 			GSList *list;
 			for (list = dbobj->depend_list; list; list = list->next) 
 				g_print ("%s ", DB_OBJECT (list->data)->obj_name);
@@ -1031,7 +1141,7 @@
 		else {
 			g_set_error (error, 0, 0, 
 				     _("View definition is not a selection statement (for view '%s')"), 
-				     dbobj->obj_name);
+				     complete_obj_name);
 			g_object_unref (stmt);
 			goto onerror;	
 		}
@@ -1046,10 +1156,12 @@
 static GdaSqlExpr *make_expr_EQUAL (GdaSqlAnyPart *parent, xmlChar *cname, xmlChar *type, GType ptype, gboolean nullok, gint index);
 static GdaSqlExpr *make_expr_AND (GdaSqlAnyPart *parent, GdaSqlExpr *current);
 static DbObject *
-create_table_object (GdaMetaStoreClass *klass, xmlNodePtr node, GError **error)
+create_table_object (GdaMetaStoreClass *klass, GdaMetaStore *store, xmlNodePtr node, GError **error)
 {
 	DbObject *dbobj;
 	xmlChar *table_name;
+	gchar *complete_obj_name;
+
 	table_name = xmlGetProp (node, BAD_CAST "name");
 	if (!table_name) {
 		g_set_error (error, 0, 0, 
@@ -1057,37 +1169,53 @@
 		return NULL;
 	}
 
+	/* determine object's complete name */
+	if (store && store->priv->schema) 
+		complete_obj_name = g_strdup_printf ("%s.%s", store->priv->schema, (gchar *) table_name);
+	else
+		complete_obj_name = g_strdup ((gchar *) table_name);
+
 	/* DbObject structure */
-	dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, table_name);
+	if (store)
+		dbobj = g_hash_table_lookup (store->priv->p_db_objects_hash, table_name);
+	else
+		dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, table_name);
 	if (!dbobj) {
 		dbobj = g_new0 (DbObject, 1);
-		klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, dbobj);
+		dbobj->store = store;
 		dbobj->obj_name = g_strdup ((gchar *) table_name);
-		g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
+		if (store) {
+			store->priv->p_db_objects = g_slist_prepend (store->priv->p_db_objects, dbobj);
+			g_hash_table_insert (store->priv->p_db_objects_hash, dbobj->obj_name, dbobj);
+		}
+		else {
+			klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, dbobj);
+			g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
+		}
 	}
 	xmlFree (table_name);
 	dbobj->obj_type = GDA_SERVER_OPERATION_CREATE_TABLE;
 
 	/* current_all */
 	gchar *sql;
-	sql = g_strdup_printf ("SELECT * FROM %s", dbobj->obj_name);
+	sql = g_strdup_printf ("SELECT * FROM %s", complete_obj_name);
 	TABLE_INFO (dbobj)->current_all = compute_prepared_stmt (klass->cpriv->parser, sql);
 	g_free (sql);
 	if (!TABLE_INFO (dbobj)->current_all) {
 		g_set_error (error, 0, 0,
 			     "Internal fatal error: could not create SELECT ALL statement (for table '%s')", 
-			     dbobj->obj_name);
+			     complete_obj_name);
 		goto onerror;
 	}
 	
 	/* delete all */
-	sql = g_strdup_printf ("DELETE FROM %s", dbobj->obj_name);
+	sql = g_strdup_printf ("DELETE FROM %s", complete_obj_name);
 	TABLE_INFO (dbobj)->delete_all = compute_prepared_stmt (klass->cpriv->parser, sql);
 	g_free (sql);
 	if (!TABLE_INFO (dbobj)->delete_all) {
 		g_set_error (error, 0, 0,
 			     "Internal fatal error: could not create DELETE ALL statement (for table '%s')", 
-			     dbobj->obj_name);
+			     complete_obj_name);
 		goto onerror;
 	}
 
@@ -1103,13 +1231,13 @@
 	GDA_SQL_ANY_PART (dst)->type = GDA_SQL_ANY_STMT_DELETE;
 	
 	ist->table = gda_sql_table_new (GDA_SQL_ANY_PART (ist));
-	ist->table->table_name = g_strdup ((gchar *) dbobj->obj_name);
+	ist->table->table_name = g_strdup ((gchar *) complete_obj_name);
 	
 	ust->table = gda_sql_table_new (GDA_SQL_ANY_PART (ust));
-	ust->table->table_name = g_strdup ((gchar *) dbobj->obj_name);
+	ust->table->table_name = g_strdup ((gchar *) complete_obj_name);
 	
 	dst->table = gda_sql_table_new (GDA_SQL_ANY_PART (dst));
-	dst->table->table_name = g_strdup ((gchar *) dbobj->obj_name);
+	dst->table->table_name = g_strdup ((gchar *) complete_obj_name);
 	
 	/* walk through the columns and Fkey nodes */
 	xmlNodePtr cnode;
@@ -1125,7 +1253,7 @@
                                 continue;
                         cname = xmlGetProp (cnode, BAD_CAST "name");
                         if (!cname)
-                                g_error ("Missing column name (table=%s)", dbobj->obj_name);
+                                g_error ("Missing column name (table=%s)", complete_obj_name);
                         xstr = xmlGetProp (cnode, BAD_CAST "pkey");
                         if (xstr) {
                                 if ((*xstr == 't') || (*xstr == 'T'))
@@ -1246,18 +1374,30 @@
 			if (!ref_table) {
 				g_set_error (error, 0, 0, 
 					     _("Missing foreign key's referenced table name (for table '%s')"), 
-					     dbobj->obj_name);
+					     complete_obj_name);
 				goto onerror;
 			}
 			
 			/* referenced DbObject */
 			DbObject *ref_obj;
-			ref_obj = g_hash_table_lookup (klass->cpriv->db_objects_hash, ref_table);
+			if (store)
+				ref_obj = g_hash_table_lookup (store->priv->p_db_objects_hash, ref_table);
+			else
+				ref_obj = g_hash_table_lookup (klass->cpriv->db_objects_hash, ref_table);
 			if (!ref_obj) {
 				ref_obj = g_new0 (DbObject, 1);
-				klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, ref_obj);
+				ref_obj->store = store;
 				ref_obj->obj_name = g_strdup ((gchar *) ref_table);
-				g_hash_table_insert (klass->cpriv->db_objects_hash, ref_obj->obj_name, ref_obj);
+				if (store) {
+					store->priv->p_db_objects = g_slist_prepend (store->priv->p_db_objects, 
+											  ref_obj);
+					g_hash_table_insert (store->priv->p_db_objects_hash, ref_obj->obj_name, 
+							     ref_obj);
+				}
+				else {
+					klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, ref_obj);
+					g_hash_table_insert (klass->cpriv->db_objects_hash, ref_obj->obj_name, ref_obj);
+				}
 			}
 			xmlFree (ref_table);
 			dbobj->depend_list = g_slist_append (dbobj->depend_list, ref_obj);
@@ -1287,7 +1427,7 @@
 				if (!col) {
 					g_set_error (error, 0, 0, 
 						     _("Missing foreign key's column name (for table '%s')"), 
-						     dbobj->obj_name);
+						     complete_obj_name);
 					table_fkey_free (tfk);
 					goto onerror;
 				}
@@ -1298,7 +1438,7 @@
 				if (tfk->fk_cols_array [fkcolindex] < 0) {
 					g_set_error (error, 0, 0,
 						     _("Column '%s' not found in table '%s'"), (gchar *) col,
-						     dbobj->obj_name);
+						     complete_obj_name);
 					table_fkey_free (tfk);
 					goto onerror;
 				}
@@ -1335,7 +1475,7 @@
 	gda_sql_statement_free (st);
 	
 	if (TABLE_INFO (dbobj)->pk_cols_nb == 0)
-		g_error ("Missing key fields identification (table=%s)", dbobj->obj_name);
+		g_error ("Missing key fields identification (table=%s)", complete_obj_name);
 
 #ifdef GDA_DEBUG_NO
 	/* debug */
@@ -1355,25 +1495,30 @@
 	GdaSet *params;
 	if (!gda_statement_get_parameters (TABLE_INFO (dbobj)->insert, &(TABLE_INFO (dbobj)->params), NULL))
 		g_error ("Internal fatal error: could not get INSERT statement's parameters (table=%s)",
-			 dbobj->obj_name);
+			 complete_obj_name);
 	if (!gda_statement_get_parameters (TABLE_INFO (dbobj)->update, &params, NULL))
 		g_error ("Internal fatal error: could not get UPDATE statement's parameters (table=%s)",
-			 dbobj->obj_name);
+			 complete_obj_name);
 	gda_set_merge_with_set (TABLE_INFO (dbobj)->params, params);
 	g_object_unref (params);
 	
 	if (!gda_statement_get_parameters (TABLE_INFO (dbobj)->delete, &params, NULL))
 		g_error ("Internal fatal error: could not get DELETE statement's parameters (table=%s)",
-			 dbobj->obj_name);
+			 complete_obj_name);
 	gda_set_merge_with_set (TABLE_INFO (dbobj)->params, params);
 	g_object_unref (params);
 	
 	/* insert DbObject */
-	g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
+	if (store)
+		g_hash_table_insert (store->priv->p_db_objects_hash, dbobj->obj_name, dbobj);
+	else
+		g_hash_table_insert (klass->cpriv->db_objects_hash, dbobj->obj_name, dbobj);
 	
+	g_free (complete_obj_name);
 	return dbobj;
 
  onerror:
+	g_free (complete_obj_name);
 	db_object_free (dbobj);
 	return NULL;
 }
@@ -1424,7 +1569,8 @@
 
 
 static void
-compute_view_dependencies (GdaMetaStoreClass *klass, DbObject *view_dbobj, GdaSqlStatement *sqlst) {	
+compute_view_dependencies (GdaMetaStoreClass *klass, GdaMetaStore *store, 
+			   DbObject *view_dbobj, GdaSqlStatement *sqlst) {	
 	if (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT) {
 		GdaSqlStatementSelect *selst;
 		selst = (GdaSqlStatementSelect*) (sqlst->contents);
@@ -1434,13 +1580,26 @@
 			
 			if (!t->table_name)
 				continue;
-			DbObject *ref_obj;
-			ref_obj = g_hash_table_lookup (klass->cpriv->db_objects_hash, t->table_name);
+			DbObject *ref_obj = NULL;
+			if (store)
+				ref_obj = g_hash_table_lookup (store->priv->p_db_objects_hash, t->table_name);
+			else
+				ref_obj = g_hash_table_lookup (klass->cpriv->db_objects_hash, t->table_name);
+
 			if (!ref_obj) {
 				ref_obj = g_new0 (DbObject, 1);
-				klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, ref_obj);
+				ref_obj->store = store;
 				ref_obj->obj_name = g_strdup (t->table_name);
-				g_hash_table_insert (klass->cpriv->db_objects_hash, ref_obj->obj_name, ref_obj);
+				if (store) {
+					store->priv->p_db_objects = g_slist_prepend (store->priv->p_db_objects, 
+											  ref_obj);
+					g_hash_table_insert (store->priv->p_db_objects_hash, ref_obj->obj_name, 
+							     ref_obj);
+				}
+				else {
+					klass->cpriv->db_objects = g_slist_prepend (klass->cpriv->db_objects, ref_obj);
+					g_hash_table_insert (klass->cpriv->db_objects_hash, ref_obj->obj_name, ref_obj);
+				}
 			}
 			view_dbobj->depend_list = g_slist_append (view_dbobj->depend_list, ref_obj);
 		}
@@ -1450,7 +1609,7 @@
 		GSList *list;
 		cst = (GdaSqlStatementCompound*) (sqlst->contents);
 		for (list = cst->stmt_list; list; list = list->next)
-			compute_view_dependencies (klass, view_dbobj, (GdaSqlStatement*) list->data);
+			compute_view_dependencies (klass, store, view_dbobj, (GdaSqlStatement*) list->data);
 	}
 	else
 		g_assert_not_reached ();
@@ -2118,9 +2277,9 @@
 					       &suggest_reports_error);
 				g_free (context.column_values);
 				if (suggest_reports_error) {
-					/*g_print ("SUGGEST META UPDATE Returned FALSE: %s\n",
+					g_print ("SUGGEST META UPDATE Returned FALSE: %s\n",
 						 suggest_reports_error && suggest_reports_error->message ? 
-						 suggest_reports_error->message : "???");*/
+						 suggest_reports_error->message : "???");
 					retval = FALSE;
 					if (error && !(*error))
 						g_propagate_error (error, suggest_reports_error);
@@ -2299,9 +2458,8 @@
 	/* fetch or create *out_table_infos */
 	DbObject *dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, table_name);
 	if (!dbobj) {
-		/* FIXME: allow for external definition of TableInfo structures for
-		 * other kind of data to be stored in meta store object */
-		TO_IMPLEMENT;
+		g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
+			     _("Unknown database object '%s'"), table_name);
 		return FALSE;
 	}
 	*out_table_infos = TABLE_INFO (dbobj);
@@ -2411,7 +2569,7 @@
 	g_return_val_if_fail (table_name && *table_name, FALSE);
 
 	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
-	dbobj = g_hash_table_lookup (klass->cpriv->db_objects_hash, table_name);
+	dbobj = g_hash_table_lookup (store->priv->p_db_objects_hash, table_name);
 	if (!dbobj) {
 		g_warning ("Table '%s' is not known by the GdaMetaStore", table_name);
 		return NULL;
@@ -2435,7 +2593,7 @@
 }
 
 /**
- * gda_meta_store_schema_get_tables
+ * gda_meta_store_schema_get_all_tables
  * @store: a #GdaMetaStore object
  *
  * Get an ordered list of the tables @store knows about. The tables are ordered in a way that tables dependencies
@@ -2446,7 +2604,7 @@
  * but the strings present in the list must not be modified.
  */
 GSList *
-gda_meta_store_schema_get_tables (GdaMetaStore *store)
+gda_meta_store_schema_get_all_tables (GdaMetaStore *store)
 {
 	GSList *list, *ret;
 	GdaMetaStoreClass *klass;
@@ -2459,6 +2617,49 @@
 		if (dbobj->obj_type == GDA_SERVER_OPERATION_CREATE_TABLE)
 			ret = g_slist_prepend (ret, dbobj->obj_name);
 	}
+	for (ret = NULL, list = store->priv->p_db_objects; list; list = list->next) {
+		DbObject *dbobj = DB_OBJECT (list->data);
+		if (dbobj->obj_type == GDA_SERVER_OPERATION_CREATE_TABLE)
+			ret = g_slist_prepend (ret, dbobj->obj_name);
+	}
+
+	return g_slist_reverse (ret);
+}
+
+/**
+ * gda_meta_store_schema_get_depend_tables
+ * @store: a #GdaMetaStore object
+ * @table_name: the name of the table for which all the dependencies must be listed
+ * 
+ *
+ * Get an ordered list of the tables @store knows about on which the @table_name table depends (recursively). 
+ * The tables are ordered in a way that tables dependencies
+ * are respected: if table B has a foreign key on table A, then table A will be listed before table B in the returned
+ * list.
+ *
+ * Returns: a new list of tables names (as gchar*), the list must be freed when no longer needed, 
+ * but the strings present in the list must not be modified.
+ */
+GSList *
+gda_meta_store_schema_get_depend_tables (GdaMetaStore *store, const gchar *table_name)
+{
+	GSList *list, *ret;
+	GdaMetaStoreClass *klass;
+	DbObject *dbo;
+
+	g_return_val_if_fail (GDA_IS_META_STORE (store), NULL);
+	g_return_val_if_fail (table_name && *table_name, NULL);
+
+	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
+	dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, table_name);
+	if (!dbo) 
+		return NULL;
+
+	for (ret = NULL, list = dbo->depend_list; list; list = list->next) {
+		DbObject *dbobj = DB_OBJECT (list->data);
+		if (dbobj->obj_type == GDA_SERVER_OPERATION_CREATE_TABLE)
+			ret = g_slist_prepend (ret, dbobj->obj_name);
+	}
 
 	return g_slist_reverse (ret);
 }
@@ -2489,13 +2690,15 @@
 
 	/* create a GdaMetaStruct */
 	pstore = gda_connection_get_meta_store (store->priv->cnc);
-	model = gda_meta_store_extract (pstore, "SELECT table_catalog, table_schema, table_name FROM _tables", error, NULL);
+	model = gda_meta_store_extract (pstore, "SELECT table_catalog, table_schema, table_name FROM _tables", 
+					error, NULL);
 	if (!model)
 		return NULL;
 
 	mstruct = gda_meta_struct_new (GDA_META_STRUCT_FEATURE_ALL);
 	nrows = gda_data_model_get_n_rows (model);
 	for (i = 0; i < nrows; i++) {
+		/* FIXME: only take into account the database objects which have a corresponding DbObject */
 		if (!gda_meta_struct_complement (mstruct, pstore, GDA_META_DB_UNKNOWN,
 						 gda_data_model_get_value_at (model, 0, i),
 						 gda_data_model_get_value_at (model, 1, i),
@@ -2508,11 +2711,15 @@
 	g_object_unref (model);
 
 	/* complement the meta struct with some info about dependencies */
-	GSList *list;
+	GSList *list, *all_db_obj_list;
 	GdaMetaStoreClass *klass;
 
 	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (pstore);
-	for (list = klass->cpriv->db_objects; list; list = list->next) {
+	all_db_obj_list = g_slist_copy (klass->cpriv->db_objects);
+	if (store->priv->p_db_objects)
+		all_db_obj_list = g_slist_concat (all_db_obj_list, g_slist_copy (store->priv->p_db_objects));
+
+	for (list = all_db_obj_list; list; list = list->next) {
 		DbObject *dbobj = DB_OBJECT (list->data);
 		if (dbobj->obj_type == GDA_SERVER_OPERATION_CREATE_TABLE) {
 			GdaMetaDbObject *mdbo;
@@ -2537,6 +2744,7 @@
 			}
 		}
 	}
+	g_slist_free (all_db_obj_list);
 	
 	return mstruct;
 }
@@ -2701,8 +2909,11 @@
 	GdaMetaStoreClass *klass;
 	DbObject *dbo = NULL;
 	GValue *value;
-	GdaMetaStore *pstore;
-	GdaMetaStruct *mstruct;
+	GdaMetaStore *pstore = NULL;
+	GdaMetaStruct *mstruct = NULL;
+	GError *lerror = NULL;
+
+	GSList *pre_p_db_objects;
 
 	g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
 	g_return_val_if_fail (xml_description && *xml_description, FALSE);
@@ -2718,53 +2929,313 @@
 
 	klass = (GdaMetaStoreClass *) G_OBJECT_GET_CLASS (store);
 
-	/* create DbObject structure from XML description */
-	/* FIXME: create the DbObject but store it into @store->priv instead of klass->cpriv */
-	if (!strcmp ((gchar *) node->name, "table")) {
-		xmlChar *prop;
-		prop = xmlGetProp (node, BAD_CAST "name");
-		if (!prop) 
-			g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
-				     _("Missing custom database object name"));
-		else if (*prop == '_') 
-			g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
-				     _("Custom database object names starting with a '_' are reserved for internal usage"));
-		else
-			dbo = create_table_object (klass, node, error);
+	/* check that object name does not start with '_' */
+	xmlChar *prop;
+	prop = xmlGetProp (node, BAD_CAST "name");
+	if (!prop) {
+		g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
+			     _("Missing custom database object name"));
+		goto onerror;
 	}
-	else if (!strcmp ((gchar *) node->name, "view")) {
-		xmlChar *prop;
-		prop = xmlGetProp (node, BAD_CAST "name");
-		if (!prop) 
-			g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
-				     _("Missing custom database object name"));
-		else if (*prop == '_') 
-			g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
-				     _("Custom database object names starting with a '_' are reserved for internal usage"));
-		else
-			dbo = create_view_object (klass, node, error);
+	else if (*prop == '_') { 
+		g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR,
+			     _("Custom database object names starting with a '_' are reserved for internal usage"));
+		goto onerror;
 	}
 
-	xmlFreeDoc (doc);
+	/* keep a list of custom DB objects _before_ adding the new one(s) (more than
+	 * one if there are dependencies) */
+	pre_p_db_objects = g_slist_copy (store->priv->p_db_objects);
+
+	/* create DbObject structure from XML description, stored in @store's custom db objects */
+	if (!strcmp ((gchar *) node->name, "table")) 
+		dbo = create_table_object (klass, store, node, error);
+	else if (!strcmp ((gchar *) node->name, "view")) 
+		dbo = create_view_object (klass, store, node, error);
 
 	if (!dbo) 
-		return FALSE;
+		goto onerror;
+	xmlFreeDoc (doc);
+	doc = NULL;
 
 	/* check for an already existing database object with the same name */
 	g_print ("Obj name: %s\n", dbo->obj_name);
 
-	/* make sure the private connection's meta store is up to date */
-	if (! gda_connection_update_meta_store (store->priv->cnc, NULL, error))
-		return FALSE;
+	/* make sure the private connection's meta store is up to date about the requested object */
+	switch (dbo->obj_type) {
+	case GDA_SERVER_OPERATION_CREATE_TABLE: 	
+	case GDA_SERVER_OPERATION_CREATE_VIEW: {
+		GdaMetaContext context;
+		gboolean upd_ok;
+		memset (&context, 0, sizeof (GdaMetaContext));
+		context.table_name = "_tables";
+		context.size = 1;
+		context.column_names = g_new0 (gchar *, 3);
+		context.column_values = g_new0 (GValue *, 3);
+		context.column_names[0] = "table_name";
+		g_value_set_string ((context.column_values[0] = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+		upd_ok = gda_connection_update_meta_store (store->priv->cnc, &context, error);
+		if (!upd_ok) 
+			goto onerror;
+		break;
+	}
+	default:
+		TO_IMPLEMENT;
+	}
 
 	/* create a GdaMetaStruct */
+	GdaMetaDbObject *eobj;
+	gboolean needs_creation = TRUE;
 	pstore = gda_connection_get_meta_store (store->priv->cnc);
 	mstruct = gda_meta_struct_new (GDA_META_STRUCT_FEATURE_ALL);
 	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
-	if (!gda_meta_struct_complement (mstruct, pstore, GDA_META_DB_UNKNOWN,
-					 NULL, NULL, value, error))
-		return FALSE;
+	if (!(eobj = gda_meta_struct_complement (mstruct, pstore, GDA_META_DB_UNKNOWN,
+						 NULL, NULL, value, &lerror))) {
+		if (lerror && (lerror->domain == GDA_META_STRUCT_ERROR) &&
+		    (lerror->code == GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR))
+			g_error_free (lerror);
+		else {
+			g_propagate_error (error, lerror);
+			goto onerror;
+		}
+	}
 	gda_value_free (value);
 	
+	if (eobj) {
+		gboolean conflict = FALSE;
+
+		g_print ("Check Existing object's conformance...\n");
+		switch (eobj->obj_type) {
+		case GDA_META_DB_TABLE:
+			if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE)
+				conflict = TRUE;
+			else {
+				GdaMetaTable *mt = GDA_META_DB_OBJECT_GET_TABLE (eobj);
+				TableInfo *ti = TABLE_INFO (dbo);
+				if (g_slist_length (mt->columns) != g_slist_length (ti->columns))
+					conflict = TRUE;
+			}
+			break;
+		case GDA_META_DB_VIEW:
+			if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_VIEW)
+				conflict = TRUE;
+			else {
+				GdaMetaView *mv = GDA_META_DB_OBJECT_GET_VIEW (eobj);
+				ViewInfo *vi = VIEW_INFO (dbo);
+				if (!mv->view_def ||
+				    !vi->view_def ||
+				    strcmp (mv->view_def, vi->view_def))
+					conflict = TRUE;
+			}
+			break;
+		default:
+			TO_IMPLEMENT;
+		}
+
+		if (conflict) {
+			g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_CONFLICT_ERROR,
+				     _("Another object with the same name already exists"));
+			goto onerror;
+		}
+		needs_creation = FALSE;
+	}
+	g_object_unref (mstruct);
+	mstruct = NULL;
+	
+	if (needs_creation) {
+		/* prepare the create operation */
+		GdaServerProvider *prov;
+		prov = gda_connection_get_provider_obj (store->priv->cnc);
+		if (! prepare_dbo_server_operation (klass, store, prov, dbo, error))
+			goto onerror;
+		
+		/* actually create the object in database */
+		g_print ("Creating object: %s\n", dbo->obj_name);
+		if (dbo->create_op) {
+			if (!gda_server_provider_perform_operation (prov, store->priv->cnc, dbo->create_op, error))
+				goto onerror;
+			g_object_unref (dbo->create_op);
+			dbo->create_op = NULL;
+		}
+	}
+
 	return TRUE;
+
+ onerror:
+	if (doc)
+		xmlFreeDoc (doc);
+	if (dbo) {
+		GSList *current_objects, *list;
+		current_objects = g_slist_copy (store->priv->p_db_objects);
+		for (list = current_objects; list; list = list->next) {
+			dbo = DB_OBJECT (list->data);
+			if (!g_slist_find (pre_p_db_objects, dbo)) {
+				/* remove the DbObject */
+				store->priv->p_db_objects = g_slist_remove (store->priv->p_db_objects, dbo);
+				g_hash_table_remove (store->priv->p_db_objects_hash, dbo->obj_name);
+				db_object_free (dbo);
+			}
+		}
+		g_slist_free (current_objects);
+	}
+	g_slist_free (pre_p_db_objects);
+	if (pstore)
+		g_object_unref (pstore);
+	if (mstruct)
+		g_object_unref (mstruct);
+	
+	return FALSE;
+}
+
+/**
+ * gda_meta_store_schema_remove_custom_object
+ * @store: a #GdaMetaStore object
+ * @obj_name: name of the custom object to remove
+ * @error: a place to store errors, or %NULL
+ *
+ * Removes the custom database object named @obj_name.
+ *
+ * Returns: TRUE if the custom object has sucessfully been removed
+ */
+gboolean
+gda_meta_store_schema_remove_custom_object (GdaMetaStore *store, const gchar *obj_name, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_META_STORE (store), FALSE);
+	g_return_val_if_fail (obj_name && *obj_name, FALSE);
+	
+	TO_IMPLEMENT;
+	return FALSE;
+}
+
+/*
+ * Returns: a list of new #GdaMetaContext structures, one for each dependency context->table_name has,
+ * or %NULL if there is no downstream context, or if an error occurred (check @error to make the difference).
+ *
+ * WARNING: each new GdaMetaContext structure is allocated, but:
+ *    - the @table_name argument is not copied
+ *    - if @size > 0 then @column_names and @column_values are allocated, but their contents is not!
+ */
+GSList *
+_gda_meta_store_schema_get_upstream_contexts (GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	DbObject *dbo;
+	GSList *list, *retlist = NULL;
+	TableInfo *tinfo;
+
+	/* find the associated DbObject */
+	dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, context->table_name);
+	if (!dbo) {
+		g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
+			     _("Unknown database object '%s'"), context->table_name);
+		return NULL;
+	}
+	if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE)
+		return NULL;
+
+	tinfo = TABLE_INFO (dbo);
+	if (!tinfo->fk_list)
+		/* this is not an error, just that there are no dependency */
+		return NULL;
+
+	/* Identify the TableFKey if @context permits it */
+	if (context->size > 0) {
+		for (list = tinfo->fk_list; list; list = list->next) {
+			TableFKey *tfk = (TableFKey*) list->data;
+			gint i, j, partial_parts = 0;
+			gint *cols_array;
+			
+			cols_array = g_new (gint, tfk->cols_nb);
+			for (i = 0; i < tfk->cols_nb; i++) {
+				cols_array [i] = -1;
+				for (j = 0; j < context->size; j++) {
+					if (!strcmp (tfk->fk_names_array[i], context->column_names[j])) {
+						cols_array [i] = j;
+						partial_parts++;
+						break;
+					}
+				}
+			}
+			if (partial_parts > 0) {
+				GdaMetaContext *ct;
+				ct = g_new0 (GdaMetaContext, 1);
+				ct->table_name = tfk->depend_on->obj_name;
+				ct->size = partial_parts;
+				ct->column_names = g_new0 (gchar *, ct->size);
+				ct->column_values = g_new0 (GValue *, ct->size);
+				retlist = g_slist_prepend (retlist, ct);
+				for (j = 0, i = 0; i < tfk->cols_nb; i++) {
+					if (cols_array [i] >= 0) {
+						ct->column_names [j] = tfk->ref_pk_names_array [i];
+						ct->column_values [j] = context->column_values [cols_array [i]];
+						j++;
+					}
+				}
+				break;
+			}
+			else {
+				GdaMetaContext *ct;
+				ct = g_new0 (GdaMetaContext, 1);
+				ct->table_name = tfk->depend_on->obj_name;
+				ct->size = 0;
+				retlist = g_slist_prepend (retlist, ct);
+			}
+			g_free (cols_array);
+		}
+	}
+	else {
+		for (list = tinfo->fk_list; list; list = list->next) {
+			TableFKey *tfk = (TableFKey*) list->data;
+			GdaMetaContext *ct;
+			ct = g_new0 (GdaMetaContext, 1);
+			ct->table_name = tfk->depend_on->obj_name;
+			ct->size = 0;
+			retlist = g_slist_prepend (retlist, ct);
+		}
+	}
+	return g_slist_reverse (retlist);
+}
+
+/*
+ * Returns: a list of new #GdaMetaContext structures, one for each reverse dependency context->table_name has,
+ * or %NULL if there is no downstream context, or if an error occurred (check @error to make the difference).
+ *
+ * WARNING: each new GdaMetaContext structure is allocated, but:
+ *    - the @table_name argument is not copied
+ *    - if @size > 0 then @column_names and @column_values are allocated, but their contents is not!
+ */
+GSList *
+_gda_meta_store_schema_get_downstream_contexts (GdaMetaStore *store, GdaMetaContext *context, GError **error)
+{
+	DbObject *dbo;
+	GSList *list, *retlist = NULL;
+	TableInfo *tinfo;
+
+	/* find the associated DbObject */
+	dbo = g_hash_table_lookup (store->priv->p_db_objects_hash, context->table_name);
+	if (!dbo) {
+		g_set_error (error, GDA_META_STORE_ERROR, GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
+			     _("Unknown database object '%s'"), context->table_name);
+		return NULL;
+	}
+	if (dbo->obj_type != GDA_SERVER_OPERATION_CREATE_TABLE)
+		return NULL;
+
+	tinfo = TABLE_INFO (dbo);
+	if (!tinfo->reverse_fk_list)
+		/* this is not an error, just that there are no dependency */
+		return NULL;
+
+	for (list = tinfo->reverse_fk_list; list; list = list->next) {
+		TableFKey *tfk = (TableFKey*) list->data;
+		GdaMetaContext *ct;
+
+		/* REM: there may be duplicates, but we don't really care here (it'd take more ressources to get rid of
+		* them than it takes to put duplicates in a hash table */
+		ct = g_new0 (GdaMetaContext, 1);
+		ct->table_name = tfk->table_info->obj_name;
+		ct->size = 0;
+		retlist = g_slist_prepend (retlist, ct);
+	}
+
+	return g_slist_reverse (retlist);
 }

Modified: trunk/libgda/gda-meta-store.h
==============================================================================
--- trunk/libgda/gda-meta-store.h	(original)
+++ trunk/libgda/gda-meta-store.h	Mon Mar 17 19:34:20 2008
@@ -41,10 +41,12 @@
 	GDA_META_STORE_INCORRECT_SCHEMA,
 	GDA_META_STORE_UNSUPPORTED_PROVIDER,
 	GDA_META_STORE_INTERNAL_ERROR,
+	GDA_META_STORE_META_CONTEXT_ERROR,
 	GDA_META_STORE_MODIFY_CONTENTS_ERROR,
 	GDA_META_STORE_EXTRACT_SQL_ERROR,
 	GDA_META_STORE_ATTRIBUTE_NOT_FOUND_ERROR,
 	GDA_META_STORE_ATTRIBUTE_ERROR,
+	GDA_META_STORE_SCHEMA_OBJECT_NOT_FOUND_ERROR,
 	GDA_META_STORE_SCHEMA_OBJECT_CONFLICT_ERROR,
 	GDA_META_STORE_SCHEMA_OBJECT_DESCR_ERROR
 } GdaMetaStoreError;
@@ -105,18 +107,21 @@
 							   GdaDataModel *new_data, GError **error);
 GdaDataModel     *gda_meta_store_create_modify_data_model (GdaMetaStore *store, const gchar *table_name);
 
-GdaMetaStruct    *gda_meta_store_schema_get_structure     (GdaMetaStore *store, GError **error);
-
 gboolean          gda_meta_store_get_attribute_value      (GdaMetaStore *store, const gchar *att_name, 
 							   gchar **att_value, GError **error);
 gboolean          gda_meta_store_set_attribute_value      (GdaMetaStore *store, const gchar *att_name, 
 							   const gchar *att_value, GError **error);
 
-gboolean          gda_meta_store_schema_add_custom_object (GdaMetaStore *store, const gchar *xml_description, 
-							   GError **error);
+gboolean          gda_meta_store_schema_add_custom_object    (GdaMetaStore *store, const gchar *xml_description, 
+							      GError **error);
+gboolean          gda_meta_store_schema_remove_custom_object (GdaMetaStore *store, const gchar *obj_name, GError **error);
+
+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);
 
-/* TO REMOVE */
-GSList           *gda_meta_store_schema_get_tables        (GdaMetaStore *store);
+GSList           *_gda_meta_store_schema_get_upstream_contexts (GdaMetaStore *store, GdaMetaContext *context, GError **error);
+GSList           *_gda_meta_store_schema_get_downstream_contexts (GdaMetaStore *store, GdaMetaContext *context, GError **error);
 
 G_END_DECLS
 

Modified: trunk/libgda/gda-server-provider.c
==============================================================================
--- trunk/libgda/gda-server-provider.c	(original)
+++ trunk/libgda/gda-server-provider.c	Mon Mar 17 19:34:20 2008
@@ -416,7 +416,7 @@
 				      GdaServerOperationType type, 
 				      GdaSet *options, GError **error)
 {
-	OpReq **op_req_table = NULL;
+	static OpReq **op_req_table = NULL;
 
 	if (! op_req_table) {
 		op_req_table = g_new0 (OpReq *, GDA_SERVER_OPERATION_NB);

Modified: trunk/libgda/gda-server-provider.h
==============================================================================
--- trunk/libgda/gda-server-provider.h	(original)
+++ trunk/libgda/gda-server-provider.h	Mon Mar 17 19:34:20 2008
@@ -96,6 +96,12 @@
 	gboolean (*constraints_ref)  (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
 				      const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
 				      const GValue *constraint_name);
+
+	/* _key_column_usage */
+	gboolean (*_key_columns)     (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **);
+	gboolean (*key_columns)      (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
+				      const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+				      const GValue *constraint_name);
 } GdaServerProviderMeta;
 
 typedef void (*GdaServerProviderAsyncCallback) (GdaServerProvider *provider, GdaConnection *cnc, guint task_id, 

Modified: trunk/libgda/gda-statement.h
==============================================================================
--- trunk/libgda/gda-statement.h	(original)
+++ trunk/libgda/gda-statement.h	Mon Mar 17 19:34:20 2008
@@ -88,7 +88,7 @@
 GdaStatement       *gda_statement_deserialize            (const gchar *str, GError **error);
 
 gboolean            gda_statement_get_parameters         (GdaStatement *stmt, GdaSet **out_params, GError **error);
-#define             gda_statement_to_sql(stmt,params,error) gda_statement_to_sql_extended ((stmt), NULL, (params), NULL, GDA_STATEMENT_SQL_PARAMS_SHORT, NULL, (error))
+#define             gda_statement_to_sql(stmt,params,error) gda_statement_to_sql_extended ((stmt), NULL, (params), GDA_STATEMENT_SQL_PARAMS_SHORT, NULL, (error))
 gchar              *gda_statement_to_sql_extended        (GdaStatement *stmt, GdaConnection *cnc, 
 							  GdaSet *params, GdaStatementSqlFlag flags, 
 							  GSList **params_used, GError **error);

Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c	(original)
+++ trunk/libgda/gda-util.c	Mon Mar 17 19:34:20 2008
@@ -662,7 +662,7 @@
 		col_ids = g_new0 (gchar *, rnb_cols);
 		for (c = 0; c < rnb_cols; c++) {
 			GdaColumn *column;
-			const gchar *id;
+			gchar *id;
 		
 			column = gda_data_model_describe_column (model, rcols [c]);
 			g_object_get (G_OBJECT (column), "id", &id, NULL);

Modified: trunk/libgda/information_schema.xml
==============================================================================
--- trunk/libgda/information_schema.xml	(original)
+++ trunk/libgda/information_schema.xml	Mon Mar 17 19:34:20 2008
@@ -363,9 +363,9 @@
     <column name="ref_table_name"/>
     <column name="ref_constraint_name"/>
 
-    <column name="match_option" nullok="TRUE"/>
-    <column name="update_rule" nullok="TRUE"/>
-    <column name="delete_rule" nullok="TRUE"/>
+    <column name="match_option" nullok="TRUE" descr="FULL, PARTIAL or NONE"/>
+    <column name="update_rule" nullok="TRUE" descr="CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION or NONE"/>
+    <column name="delete_rule" nullok="TRUE" descr="CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION or NONE"/>
     <fkey ref_table="_table_constraints">
       <part column="table_catalog"/>
       <part column="table_schema"/>

Modified: trunk/libgda/sqlite/gda-sqlite-meta.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-meta.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-meta.c	Mon Mar 17 19:34:20 2008
@@ -40,7 +40,9 @@
  */
 typedef enum {
         I_PRAGMA_DATABASE_LIST,
-	I_PRAGMA_TABLE_INFO
+	I_PRAGMA_TABLE_INFO,
+	I_PRAGMA_INDEX_LIST,
+	I_PRAGMA_FK_LIST
 } InternalStatementItem;
 
 
@@ -48,8 +50,17 @@
  * predefined statements' SQL
  */
 static gchar *internal_sql[] = {
+	/* I_PRAGMA_DATABASE_LIST */
 	"PRAGMA database_list",
-	"PRAGMA table_info (##tblname::string)"
+
+	/* I_PRAGMA_TABLE_INFO */
+	"PRAGMA table_info (##tblname::string)",
+
+	/* I_PRAGMA_INDEX_LIST */
+	"PRAGMA index_list (##tblname::string)",
+
+	/* I_PRAGMA_FK_LIST */
+	"PRAGMA foreign_key_list (##tblname::string)"
 };
 
 /*
@@ -68,6 +79,7 @@
 static GValue       *view_check_option;
 static GValue       *false_value;
 static GValue       *zero_value;
+static GValue       *rule_value;
 static GdaSet       *pragma_set;
 
 /*
@@ -94,14 +106,13 @@
 		}
         }
 
-	catalog_value = gda_value_new (G_TYPE_STRING);
-	g_value_set_string (catalog_value, "main");
-
+	g_value_set_string ((catalog_value = gda_value_new (G_TYPE_STRING)), "main");
 	g_value_set_string ((table_type_value = gda_value_new (G_TYPE_STRING)), "BASE TABLE");
 	g_value_set_string ((view_type_value = gda_value_new (G_TYPE_STRING)), "VIEW");
 	g_value_set_string ((view_check_option = gda_value_new (G_TYPE_STRING)), "NONE");
 	g_value_set_boolean ((false_value = gda_value_new (G_TYPE_BOOLEAN)), FALSE);
 	g_value_set_int ((zero_value = gda_value_new (G_TYPE_INT)), 0);
+	g_value_set_string ((rule_value = gda_value_new (G_TYPE_STRING)), "NONE");
 
 	pragma_set = gda_set_new_inline (1, "tblname", G_TYPE_STRING, "");
 }
@@ -418,7 +429,6 @@
 		if (pAutoinc)
 			g_value_set_string ((v5 = gda_value_new (G_TYPE_STRING)), "AUTO_INCREMENT");
 		g_value_set_int (v1, g_value_get_int (v1) + 1);
-
 		
 		if (pzDataType) {
 			gchar *tmp = g_strdup (pzDataType);
@@ -467,6 +477,8 @@
 	return retval;
 }
 
+
+
 gboolean
 _gda_sqlite_meta_columns (GdaServerProvider *prov, GdaConnection *cnc, 
 			  GdaMetaStore *store, GdaMetaContext *context, GError **error, 
@@ -491,24 +503,486 @@
 	return retval;
 }
 
+static gboolean 
+fill_constraints_tab_model (GdaConnection *cnc, SqliteConnectionData *cdata, GdaDataModel *mod_model, 
+			    const GValue *p_table_schema, const GValue *p_table_name, const GValue *constraint_name_n,
+			    GError **error)
+{
+	GdaDataModel *tmpmodel;
+	gboolean retval = TRUE;
+	gint nrows;
+	const gchar *schema_name;
+	gint i;
+
+	/*
+	 * Setup pragma_set
+	 */
+	schema_name = g_value_get_string (p_table_schema);
+	if (strcmp (schema_name, "main")) {
+		gchar *str;
+		str = g_strdup_printf ("%s.%s", schema_name, g_value_get_string (p_table_name));
+		gda_set_set_holder_value (pragma_set, "tblname", str);
+		g_free (str);
+	}
+	else
+		gda_set_set_holder_value (pragma_set, "tblname", g_value_get_string (p_table_name));
+
+	
+	/* 
+	 * PRIMARY KEY
+	 *
+	 * SQLite only allows 1 primary key to be defined per table.
+	 */
+	GType pk_col_types[] = {G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, 
+				G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_NONE};
+	gboolean has_pk = FALSE;
+
+	tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_TABLE_INFO], pragma_set, 
+								 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+								 pk_col_types, error);
+	if (!tmpmodel)
+		return FALSE;
+		
+	nrows = gda_data_model_get_n_rows (tmpmodel);
+	for (i = 0; i < nrows; i++) {
+		char const *pzDataType; /* Declared data type */
+		char const *pzCollSeq; /* Collation sequence name */
+		int pNotNull; /* True if NOT NULL constraint exists */
+		int pPrimaryKey; /* True if column part of PK */
+		int pAutoinc; /* True if column is auto-increment */
+		const gchar *this_table_name;
+		const gchar *this_col_name;
+		const GValue *this_col_pname;
+		
+		this_col_pname = gda_data_model_get_value_at (tmpmodel, 1, i);
+		this_table_name = g_value_get_string (p_table_name);
+		g_assert (this_table_name);
+		if (!strcmp (this_table_name, "sqlite_sequence"))
+			continue; /* ignore that table */
+		
+		this_col_name = g_value_get_string (this_col_pname);
+		if (sqlite3_table_column_metadata (cdata->connection, g_value_get_string (p_table_schema), 
+						   this_table_name, this_col_name,
+						   &pzDataType, &pzCollSeq, &pNotNull, &pPrimaryKey, &pAutoinc)
+		    != SQLITE_OK) 
+			/* may fail because we have a view and not a table => use @tmpmodel to fetch info. */
+			pPrimaryKey = g_value_get_boolean (gda_data_model_get_value_at (tmpmodel, 5, i));
+		
+		if (pPrimaryKey) {
+			has_pk = TRUE;
+			break;
+		}
+	}
+
+	if (has_pk) {
+		if (!constraint_name_n || ! strcmp (g_value_get_string (constraint_name_n), "primary_key")) {
+			GValue *v1, *v2;
+			g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), "primary_key");
+			g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), "PRIMARY KEY");
+			
+			if (! append_a_row (mod_model, error, 10, 
+					    FALSE, catalog_value, /* constraint_catalog */
+					    FALSE, p_table_schema, /* constraint_schema */
+					    TRUE, v1, /* constraint_name */
+					    FALSE, catalog_value, /* table_catalog */
+					    FALSE, p_table_schema, /* table_schema */
+					    FALSE, p_table_name, /* table_name */
+					    TRUE, v2, /* constraint_type */
+					    FALSE, NULL, /* check_clause */
+					    FALSE, NULL, /* is_deferrable */
+					    FALSE, NULL /* initially_deferred */))
+				retval = FALSE;
+		}
+	}
+
+	g_object_unref (tmpmodel);
+	if (!retval)
+		return FALSE;
+
+	/* 
+	 * UNIQUE 
+	 */
+	GType unique_col_types[] = {G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_NONE};
+	
+	tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_INDEX_LIST], pragma_set, 
+								 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+								 unique_col_types, error);
+	if (!tmpmodel)
+		return FALSE;
+		
+	nrows = gda_data_model_get_n_rows (tmpmodel);
+	for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+		GValue *v2;
+
+		cvalue = gda_data_model_get_value_at (tmpmodel, 2, i);
+		if (!g_value_get_int (cvalue))
+			continue;
+		
+		cvalue = gda_data_model_get_value_at (tmpmodel, 1, i);
+		if (constraint_name_n && strcmp (g_value_get_string (constraint_name_n), g_value_get_string (cvalue)))
+			continue;
+
+		g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), "UNIQUE");
+		
+		if (! append_a_row (mod_model, error, 10, 
+				    FALSE, catalog_value, /* constraint_catalog */
+				    FALSE, p_table_schema, /* constraint_schema */
+				    FALSE, cvalue, /* constraint_name */
+				    FALSE, catalog_value, /* table_catalog */
+				    FALSE, p_table_schema, /* table_schema */
+				    FALSE, p_table_name, /* table_name */
+				    TRUE, v2, /* constraint_type */
+				    FALSE, NULL, /* check_clause */
+				    FALSE, NULL, /* is_deferrable */
+				    FALSE, NULL /* initially_deferred */))
+			retval = FALSE;
+	}
+	g_object_unref (tmpmodel);
+	if (!retval)
+		return FALSE;
+
+	/*
+	 * FOREIGN KEYS
+	 */
+	GType fk_col_types[] = {G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE};
+	gchar *ref_table = NULL;
+	tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_FK_LIST], pragma_set, 
+								 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+								 fk_col_types, error);
+	if (!tmpmodel)
+		return FALSE;
+		
+	nrows = gda_data_model_get_n_rows (tmpmodel);
+	for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+
+		cvalue = gda_data_model_get_value_at (tmpmodel, 2, i);
+		if (! ref_table || strcmp (ref_table, g_value_get_string (cvalue))) {
+			gchar *constname;
+			GValue *v1, *v2;
+
+			g_free (ref_table);
+			ref_table = g_strdup (g_value_get_string (cvalue));
+			constname = g_strdup_printf ("fk_%s", ref_table);
+			if (constraint_name_n && strcmp (g_value_get_string (constraint_name_n), constname)) {
+				g_free (constname);
+				continue;
+			}
+		
+			g_value_take_string ((v1 = gda_value_new (G_TYPE_STRING)), constname);
+			g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), "FOREIGN KEY");
+		
+			if (! append_a_row (mod_model, error, 10, 
+					    FALSE, catalog_value, /* constraint_catalog */
+					    FALSE, p_table_schema, /* constraint_schema */
+					    TRUE, v1, /* constraint_name */
+					    FALSE, catalog_value, /* table_catalog */
+					    FALSE, p_table_schema, /* table_schema */
+					    FALSE, p_table_name, /* table_name */
+					    TRUE, v2, /* constraint_type */
+					    FALSE, NULL, /* check_clause */
+					    FALSE, NULL, /* is_deferrable */
+					    FALSE, NULL /* initially_deferred */))
+				retval = FALSE;
+		}
+	}
+	g_free (ref_table);
+	g_object_unref (tmpmodel);
+
+	/*
+	 * CHECK constraint
+	 * FIXME: how to get that information?
+	 */
+
+	return retval;
+}
+
 gboolean 
 _gda_sqlite_meta_constraints_tab (GdaServerProvider *prov, GdaConnection *cnc, 
 				  GdaMetaStore *store, GdaMetaContext *context, GError **error,
 				  const GValue *table_catalog, const GValue *table_schema, const GValue *table_name,
 				  const GValue *constraint_name_n)
 {
-	/* not implemented in SQLite */
-	return TRUE;
+	gboolean retval = TRUE;
+	GdaDataModel *mod_model = NULL;
+	SqliteConnectionData *cdata;
+	
+	cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+
+	mod_model = gda_meta_store_create_modify_data_model (store, context->table_name);
+	g_assert (mod_model);
+
+	retval = fill_constraints_tab_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name_n, error);	
+	if (retval)
+		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
+	g_object_unref (mod_model);
+
+	return retval;
+}
+
+static gboolean 
+fill_constraints_ref_model (GdaConnection *cnc, SqliteConnectionData *cdata, GdaDataModel *mod_model, 
+			    const GValue *p_table_schema, const GValue *p_table_name, const GValue *constraint_name,
+			    GError **error)
+{
+	GdaDataModel *tmpmodel;
+	gboolean retval = TRUE;
+	gint nrows;
+	const gchar *schema_name;
+	gint i;
+
+	/*
+	 * Setup pragma_set
+	 */
+	schema_name = g_value_get_string (p_table_schema);
+	if (strcmp (schema_name, "main")) {
+		gchar *str;
+		str = g_strdup_printf ("%s.%s", schema_name, g_value_get_string (p_table_name));
+		gda_set_set_holder_value (pragma_set, "tblname", str);
+		g_free (str);
+	}
+	else
+		gda_set_set_holder_value (pragma_set, "tblname", g_value_get_string (p_table_name));	
+
+	GType fk_col_types[] = {G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE};
+	gchar *ref_table = NULL;
+	tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_FK_LIST], pragma_set, 
+								 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+								 fk_col_types, error);
+	if (!tmpmodel)
+		return FALSE;
+		
+	nrows = gda_data_model_get_n_rows (tmpmodel);
+	for (i = 0; i < nrows; i++) {
+		const GValue *cvalue;
+
+		cvalue = gda_data_model_get_value_at (tmpmodel, 2, i);
+		if (! ref_table || strcmp (ref_table, g_value_get_string (cvalue))) {
+			gchar *constname;
+			GValue *v2, *v3, *v4;
+
+			g_free (ref_table);
+			ref_table = g_strdup (g_value_get_string (cvalue));
+			constname = g_strdup_printf ("fk_%s", ref_table);
+			if (strcmp (g_value_get_string (constraint_name), constname)) {
+				g_free (constname);
+				continue;
+			}
+		
+			g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), "FOREIGN KEY");
+			g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), ref_table);
+			g_value_set_string ((v4 = gda_value_new (G_TYPE_STRING)), "primary_key");
+		
+			if (! append_a_row (mod_model, error, 11, 
+					    FALSE, catalog_value, /* table_catalog */
+					    FALSE, p_table_schema, /* table_schema */
+					    FALSE, p_table_name, /* table_name */
+					    FALSE, constraint_name, /* constraint_name */
+					    FALSE, catalog_value, /* ref_table_catalog */
+					    FALSE, p_table_schema, /* ref_table_schema */
+					    TRUE, v3, /* ref_table_name */
+					    TRUE, v4, /* ref_constraint_name */
+					    FALSE, NULL, /* match_option */
+					    FALSE, rule_value, /* update_rule */
+					    FALSE, rule_value /* delete_rule */))
+				retval = FALSE;
+		}
+	}
+	g_free (ref_table);
+	g_object_unref (tmpmodel);
+
+	return retval;
 }
 
+
 gboolean
 _gda_sqlite_meta_constraints_ref (GdaServerProvider *prov, GdaConnection *cnc, 
 				  GdaMetaStore *store, GdaMetaContext *context, GError **error,
 				  const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
 				  const GValue *constraint_name)
 {
-	/* not implemented in SQLite */
-	return TRUE;
+	gboolean retval = TRUE;
+	GdaDataModel *mod_model = NULL;
+	SqliteConnectionData *cdata;
+	
+	cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+
+	mod_model = gda_meta_store_create_modify_data_model (store, context->table_name);
+	g_assert (mod_model);
+
+	retval = fill_constraints_ref_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name, error);
+	if (retval)
+		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
+	g_object_unref (mod_model);
+
+	return retval;
+}
+
+static gboolean 
+fill_key_columns_model (GdaConnection *cnc, SqliteConnectionData *cdata, GdaDataModel *mod_model, 
+			const GValue *p_table_schema, const GValue *p_table_name, const GValue *constraint_name,
+			GError **error)
+{
+	GdaDataModel *tmpmodel;
+	gboolean retval = TRUE;
+	gint nrows;
+	const gchar *schema_name, *const_name;
+	gint i;
+
+	/*
+	 * Setup pragma_set
+	 */
+	schema_name = g_value_get_string (p_table_schema);
+	if (strcmp (schema_name, "main")) {
+		gchar *str;
+		str = g_strdup_printf ("%s.%s", schema_name, g_value_get_string (p_table_name));
+		gda_set_set_holder_value (pragma_set, "tblname", str);
+		g_free (str);
+	}
+	else
+		gda_set_set_holder_value (pragma_set, "tblname", g_value_get_string (p_table_name));	
+
+	const_name = g_value_get_string (constraint_name);
+	if (!strcmp (const_name, "primary_key")) {
+		/* 
+		 * PRIMARY KEY columns
+		 */
+		GType pk_col_types[] = {G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, 
+					G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_NONE};
+		gint ord_pos = 1;
+		tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_TABLE_INFO], pragma_set, 
+									 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+									 pk_col_types, error);
+		if (!tmpmodel)
+			return FALSE;
+		
+		nrows = gda_data_model_get_n_rows (tmpmodel);
+		for (i = 0; i < nrows; i++) {
+			char const *pzDataType; /* Declared data type */
+			char const *pzCollSeq; /* Collation sequence name */
+			int pNotNull; /* True if NOT NULL constraint exists */
+			int pPrimaryKey; /* True if column part of PK */
+			int pAutoinc; /* True if column is auto-increment */
+			const gchar *this_table_name;
+			const gchar *this_col_name;
+			const GValue *this_col_pname;
+			GValue *v1;
+			
+			this_col_pname = gda_data_model_get_value_at (tmpmodel, 1, i);
+			this_table_name = g_value_get_string (p_table_name);
+			g_assert (this_table_name);
+			if (!strcmp (this_table_name, "sqlite_sequence"))
+				continue; /* ignore that table */
+			
+			this_col_name = g_value_get_string (this_col_pname);
+			if (sqlite3_table_column_metadata (cdata->connection, g_value_get_string (p_table_schema), 
+							   this_table_name, this_col_name,
+							   &pzDataType, &pzCollSeq, &pNotNull, &pPrimaryKey, &pAutoinc)
+			    != SQLITE_OK) 
+				/* may fail because we have a view and not a table => use @tmpmodel to fetch info. */
+				pPrimaryKey = g_value_get_boolean (gda_data_model_get_value_at (tmpmodel, 5, i));
+			if (pPrimaryKey) {
+				g_value_set_int ((v1 = gda_value_new (G_TYPE_INT)), ord_pos++);
+				if (! append_a_row (mod_model, error, 6, 
+						    FALSE, catalog_value, /* table_catalog */
+						    FALSE, p_table_schema, /* table_schema */
+						    FALSE, p_table_name, /* table_name */
+						    FALSE, constraint_name, /* constraint_name */
+						    FALSE, gda_data_model_get_value_at (tmpmodel, 1, i), /* column_name */
+						    TRUE, v1 /* ordinal_position */)) {
+					retval = FALSE;
+					break;
+				}
+			}
+		}
+		g_object_unref (tmpmodel);
+	}
+	else if ((*const_name == 'f')  && (const_name[1] == 'k') && (const_name[2] == '_')) {
+		/*
+		 * FOREIGN key columns
+		 */
+		GType fk_col_types[] = {G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE};
+		gchar *ref_table = NULL;
+		gint ord_pos;
+		tmpmodel = gda_connection_statement_execute_select_full (cnc, internal_stmt[I_PRAGMA_FK_LIST], pragma_set, 
+									 GDA_STATEMENT_MODEL_RANDOM_ACCESS, 
+									 fk_col_types, error);
+		if (!tmpmodel)
+			return FALSE;
+		
+		nrows = gda_data_model_get_n_rows (tmpmodel);
+		for (i = 0; i < nrows; i++) {
+			const GValue *cvalue;
+			GValue *v1;
+			
+			cvalue = gda_data_model_get_value_at (tmpmodel, 2, i);
+			if (! ref_table || strcmp (ref_table, g_value_get_string (cvalue))) {
+				gchar *constname;
+				
+				g_free (ref_table);
+				ref_table = g_strdup (g_value_get_string (cvalue));
+				constname = g_strdup_printf ("fk_%s", ref_table);
+				if (strcmp (g_value_get_string (constraint_name), constname)) {
+					g_free (constname);
+					g_free (ref_table);
+					ref_table = NULL;
+					continue;
+				}
+				ord_pos = 1;
+			}
+			
+			g_value_set_int ((v1 = gda_value_new (G_TYPE_INT)), ord_pos++);
+			if (! append_a_row (mod_model, error, 6, 
+					    FALSE, catalog_value, /* table_catalog */
+					    FALSE, p_table_schema, /* table_schema */
+					    FALSE, p_table_name, /* table_name */
+					    FALSE, constraint_name, /* constraint_name */
+					    FALSE, gda_data_model_get_value_at (tmpmodel, 3, i), /* column_name */
+					    TRUE, v1 /* ordinal_position */))
+				retval = FALSE;
+		}
+
+		g_free (ref_table);
+		g_object_unref (tmpmodel);
+	}
+	else {
+		/*
+		 * UNIQUE columns
+		 */
+		TO_IMPLEMENT;
+	}
+
+	return retval;
+}
+
+gboolean 
+_gda_sqlite_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+			      GdaMetaStore *store, GdaMetaContext *context, GError **error,
+			      const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+			      const GValue *constraint_name)
+{
+	gboolean retval = TRUE;
+	GdaDataModel *mod_model = NULL;
+	SqliteConnectionData *cdata;
+	
+	cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+
+	mod_model = gda_meta_store_create_modify_data_model (store, context->table_name);
+	g_assert (mod_model);
+
+	retval = fill_key_columns_model (cnc, cdata, mod_model, table_schema, table_name, constraint_name, error);
+	if (retval)
+		retval = gda_meta_store_modify_with_context (store, context, mod_model, error);
+	g_object_unref (mod_model);
+
+	return retval;
 }
 
 

Modified: trunk/libgda/sqlite/gda-sqlite-meta.h
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-meta.h	(original)
+++ trunk/libgda/sqlite/gda-sqlite-meta.h	Mon Mar 17 19:34:20 2008
@@ -43,6 +43,9 @@
 gboolean _gda_sqlite_meta_constraints_ref  (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
 					    const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
 					    const GValue *constraint_name);
+gboolean _gda_sqlite_meta_key_columns      (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
+					    const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+					    const GValue *constraint_name);
 G_END_DECLS
 
 #endif

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Mon Mar 17 19:34:20 2008
@@ -147,7 +147,7 @@
 /*
  * Prepared internal statements
  */
-GdaStatement **internal_stmt;
+static GdaStatement **internal_stmt;
 
 typedef enum {
 	INTERNAL_PRAGMA_INDEX_LIST,
@@ -234,6 +234,7 @@
 	provider_class->meta_funcs.columns = _gda_sqlite_meta_columns;
 	provider_class->meta_funcs.constraints_tab = _gda_sqlite_meta_constraints_tab;
 	provider_class->meta_funcs.constraints_ref = _gda_sqlite_meta_constraints_ref;
+	provider_class->meta_funcs.key_columns = _gda_sqlite_meta_key_columns;
 }
 
 static void

Modified: trunk/po/POTFILES.skip
==============================================================================
--- trunk/po/POTFILES.skip	(original)
+++ trunk/po/POTFILES.skip	Mon Mar 17 19:34:20 2008
@@ -1,7 +1,4 @@
-libgda/sql-parser/delimiter.c
-libgda/sql-parser/parser.c
 libgda/sqlite/sqlite-src/sqlite3.c
-providers/postgres/parser.c
 providers/skel-implementation/capi/capi_specs_create_table.xml.in
 providers/skel-implementation/capi/capi_specs_dsn.xml.in
 providers/skel-implementation/capi/gda-capi-blob-op.c

Modified: trunk/providers/postgres/gda-postgres-meta.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-meta.c	(original)
+++ trunk/providers/postgres/gda-postgres-meta.c	Mon Mar 17 19:34:20 2008
@@ -50,7 +50,8 @@
 	I_STMT_COLUMNS_OF_TABLE,
 	I_STMT_TABLES_CONSTRAINTS,
 	I_STMT_TABLES_CONSTRAINT_NAMED,
-	I_STMT_REF_CONSTRAINTS
+	I_STMT_REF_CONSTRAINTS,
+	I_STMT_KEY_COLUMN_USAGE
 } InternalStatementItem;
 
 
@@ -92,7 +93,10 @@
 	"SELECT constraint_catalog, constraint_schema, constraint_name, table_catalog, table_schema, table_name, constraint_type, NULL, CASE WHEN is_deferrable = 'YES' THEN TRUE ELSE FALSE END, CASE WHEN initially_deferred = 'YES' THEN TRUE ELSE FALSE END FROM information_schema.table_constraints WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string",
 
 	/* I_STMT_REF_CONSTRAINTS */
-	" SELECT  current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f'::\"char\" THEN 'FULL'::text WHEN 'p'::\"char\" THEN 'PARTIAL'::text WHEN 'u'::\"char\" THEN 'NONE'::text ELSE NULL::text END AS match_option, CASE c.confupdtype WHEN 'c'::\"char\" THEN 'CASCADE'::text WHEN 'n'::\"char\" THEN 'SET NULL'::text WHEN 'd'::\"char\" THEN 'SET DEFAULT'::text WHEN 'r'::\"char\" THEN 'RESTRICT'::text WHEN 'a'::\"char\" THEN 'NO ACTION'::text ELSE NULL::text END AS update_rule, CASE c.confdeltype WHEN 'c'::\"char\" THEN 'CASCADE'::text WHEN 'n'::\"char\" THEN 'SET NULL'::text WHEN 'd'::\"char\" THEN 'SET DEFAULT'::text WHEN 'r'::\"char\" THEN 'RESTRICT'::text WHEN 'a'::\"char\" THEN 'NO ACTION'::text ELSE NULL::text END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid)
  INNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey)) WHERE c.contype = 'f' AND current_database() = ##cat::string AND nt.nspname = ##schema::string AND t.relname = ##name::string AND c.conname = ##name2::string"
+	"SELECT current_database(), nt.nspname, t.relname, c.conname, current_database(), nref.nspname, ref.relname, pkc.conname, CASE c.confmatchtype WHEN 'f'::\"char\" THEN 'FULL'::text WHEN 'p'::\"char\" THEN 'PARTIAL'::text WHEN 'u'::\"char\" THEN 'NONE'::text ELSE NULL::text END AS match_option, CASE c.confupdtype WHEN 'c'::\"char\" THEN 'CASCADE'::text WHEN 'n'::\"char\" THEN 'SET NULL'::text WHEN 'd'::\"char\" THEN 'SET DEFAULT'::text WHEN 'r'::\"char\" THEN 'RESTRICT'::text WHEN 'a'::\"char\" THEN 'NO ACTION'::text ELSE NULL::text END AS update_rule, CASE c.confdeltype WHEN 'c'::\"char\" THEN 'CASCADE'::text WHEN 'n'::\"char\" THEN 'SET NULL'::text WHEN 'd'::\"char\" THEN 'SET DEFAULT'::text WHEN 'r'::\"char\" THEN 'RESTRICT'::text WHEN 'a'::\"char\" THEN 'NO ACTION'::text ELSE NULL::text END AS delete_rule FROM pg_constraint c INNER JOIN pg_class t ON (c.conrelid=t.oid) INNER JOIN pg_namespace nt ON (nt.oid=t.relnamespace) INNER JOIN pg_class ref ON (c.confrelid=ref.oid) I
 NNER JOIN pg_namespace nref ON (nref.oid=ref.relnamespace) INNER JOIN pg_constraint pkc ON (c.confrelid = pkc.conrelid AND information_schema._pg_keysequal(c.confkey, pkc.conkey)) WHERE c.contype = 'f' AND current_database() = ##cat::string AND nt.nspname = ##schema::string AND t.relname = ##name::string AND c.conname = ##name2::string",
+
+	/* I_STMT_KEY_COLUMN_USAGE */
+	"SELECT table_catalog, table_schema, table_name, constraint_name, column_name, ordinal_position FROM information_schema.key_column_usage WHERE table_catalog = ##cat::string AND table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string"
 };
 
 /*
@@ -422,3 +426,37 @@
 	return retval;
 }
 
+gboolean 
+_gda_postgres_meta_key_columns (GdaServerProvider *prov, GdaConnection *cnc, 
+				GdaMetaStore *store, GdaMetaContext *context, GError **error,
+				const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+				const GValue *constraint_name)
+{
+	GdaDataModel *model;
+	gboolean retval = TRUE;
+	PostgresConnectionData *cdata;
+
+	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
+	if (!cdata)
+		return FALSE;
+
+	gda_holder_set_value (gda_set_get_holder (i_set, "cat"), table_catalog);
+	gda_holder_set_value (gda_set_get_holder (i_set, "schema"), table_schema);
+	gda_holder_set_value (gda_set_get_holder (i_set, "name"), table_name);
+	gda_holder_set_value (gda_set_get_holder (i_set, "name2"), constraint_name);
+	model = gda_connection_statement_execute_select (cnc, internal_stmt[I_STMT_KEY_COLUMN_USAGE], i_set, 
+							 error);
+	if (!model)
+		return FALSE;
+
+
+	/* modify meta store */
+	if (retval)
+		retval = gda_meta_store_modify (store, context->table_name, model, 
+						"table_schema = ##schema::string AND table_name = ##name::string AND constraint_name = ##name2::string", 
+						error, 
+						"schema", table_schema, "name", table_name, "name2", constraint_name, NULL);
+	g_object_unref (model);
+
+	return retval;	
+}

Modified: trunk/providers/postgres/gda-postgres-meta.h
==============================================================================
--- trunk/providers/postgres/gda-postgres-meta.h	(original)
+++ trunk/providers/postgres/gda-postgres-meta.h	Mon Mar 17 19:34:20 2008
@@ -43,7 +43,9 @@
 gboolean _gda_postgres_meta_constraints_ref  (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
 					      const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
 					      const GValue *constraint_name);
-
+gboolean _gda_postgres_meta_key_columns      (GdaServerProvider *, GdaConnection *, GdaMetaStore *, GdaMetaContext *, GError **,
+					      const GValue *table_catalog, const GValue *table_schema, const GValue *table_name, 
+					      const GValue *constraint_name);
 
 G_END_DECLS
 

Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c	(original)
+++ trunk/providers/postgres/gda-postgres-provider.c	Mon Mar 17 19:34:20 2008
@@ -192,6 +192,7 @@
 	provider_class->meta_funcs.columns = _gda_postgres_meta_columns;
 	provider_class->meta_funcs.constraints_tab = _gda_postgres_meta_constraints_tab;
 	provider_class->meta_funcs.constraints_ref = _gda_postgres_meta_constraints_ref;
+	provider_class->meta_funcs.key_columns = _gda_postgres_meta_key_columns;
 }
 
 static void

Modified: trunk/tests/providers/prov-test-common.c
==============================================================================
--- trunk/tests/providers/prov-test-common.c	(original)
+++ trunk/tests/providers/prov-test-common.c	Mon Mar 17 19:34:20 2008
@@ -91,7 +91,7 @@
 
 	/* dump all tables */
 	store = gda_connection_get_meta_store (cnc);
-	tables = gda_meta_store_schema_get_tables (store);
+	tables = gda_meta_store_schema_get_all_tables (store);
 	ntables = g_slist_length (tables);
 	dump1 = g_new0 (gchar *, ntables + 1);
 

Modified: trunk/tools/command-exec.c
==============================================================================
--- trunk/tools/command-exec.c	(original)
+++ trunk/tools/command-exec.c	Mon Mar 17 19:34:20 2008
@@ -716,75 +716,161 @@
 		res->u.model = model;
 		return res;
 	}
+	else if (nb_objects == 0) {
+		g_set_error (error, 0, 0, _("No object found"));
+		return NULL;
+	}
 
+	/* 
+	 * Information about a single object
+	 */
 	res = g_new0 (GdaInternalCommandResult, 1);
 	res->type = GDA_INTERNAL_COMMAND_RESULT_MULTIPLE;
 	res->u.multiple_results = NULL;
+	GdaMetaDbObject *dbo;
 
 	for (dbo_list = mstruct->db_objects; dbo_list; dbo_list = dbo_list->next) {
-		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (dbo_list->data);
-		GdaInternalCommandResult *subres;
-
-		switch (dbo->obj_type) {
-		case GDA_META_DB_UNKNOWN:
+		dbo = GDA_META_DB_OBJECT (dbo_list->data);
+		if (dbo->obj_type == GDA_META_DB_UNKNOWN)
+			dbo = NULL;
+		else
 			break;
-		case GDA_META_DB_VIEW: 
-		case GDA_META_DB_TABLE: {
-			GdaMetaTable *mt = GDA_META_DB_OBJECT_GET_TABLE (dbo);
-			GSList *list;
-
-			model = gda_data_model_array_new (4);
-			gda_data_model_set_column_title (model, 0, _("Column"));
-			gda_data_model_set_column_title (model, 1, _("Type"));
-			gda_data_model_set_column_title (model, 2, _("Nullable"));
-			gda_data_model_set_column_title (model, 3, _("Default"));
-			if (dbo->obj_type == GDA_META_DB_VIEW)
-				g_object_set_data_full (G_OBJECT (model), "name", 
-							g_strdup_printf (_("List of columns for view '%s'"), 
-									 dbo->obj_short_name), 	g_free);
-			else
-				g_object_set_data_full (G_OBJECT (model), "name", 
-							g_strdup_printf (_("List of columns for table '%s'"), 
-									 dbo->obj_short_name), g_free);
-			for (list = mt->columns; list; list = list->next) {
-				GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
-				GList *values = NULL;
-				GValue *val;
-
-				g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->column_name);
-				values = g_list_append (values, val);
-				g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->column_type);
-				values = g_list_append (values, val);
-				g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->nullok ? _("yes") : _("no"));
-				values = g_list_append (values, val);
-				g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->default_value);
-				values = g_list_append (values, val);
-				gda_data_model_append_values (model, values, NULL);
-				g_list_foreach (values, (GFunc) gda_value_free, NULL);
-				g_list_free (values);
-			}
+	}
+	g_assert (dbo);
+
+	if ((dbo->obj_type == GDA_META_DB_VIEW) || (dbo->obj_type == GDA_META_DB_TABLE)) {
+		GdaInternalCommandResult *subres;
+		GdaMetaTable *mt = GDA_META_DB_OBJECT_GET_TABLE (dbo);
+		GSList *list;
 
+		model = gda_data_model_array_new (4);
+		gda_data_model_set_column_title (model, 0, _("Column"));
+		gda_data_model_set_column_title (model, 1, _("Type"));
+		gda_data_model_set_column_title (model, 2, _("Nullable"));
+		gda_data_model_set_column_title (model, 3, _("Default"));
+		if (dbo->obj_type == GDA_META_DB_VIEW)
+			g_object_set_data_full (G_OBJECT (model), "name", 
+						g_strdup_printf (_("List of columns for view '%s'"), 
+								 dbo->obj_short_name), 	g_free);
+		else
+			g_object_set_data_full (G_OBJECT (model), "name", 
+						g_strdup_printf (_("List of columns for table '%s'"), 
+								 dbo->obj_short_name), g_free);
+		for (list = mt->columns; list; list = list->next) {
+			GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
+			GList *values = NULL;
+			GValue *val;
+			
+			g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->column_name);
+			values = g_list_append (values, val);
+			g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->column_type);
+			values = g_list_append (values, val);
+			g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->nullok ? _("yes") : _("no"));
+			values = g_list_append (values, val);
+			g_value_set_string ((val = gda_value_new (G_TYPE_STRING)), tcol->default_value);
+			values = g_list_append (values, val);
+			gda_data_model_append_values (model, values, NULL);
+			g_list_foreach (values, (GFunc) gda_value_free, NULL);
+			g_list_free (values);
+		}
+		
+		subres = g_new0 (GdaInternalCommandResult, 1);
+		subres->type = GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL;
+		subres->u.model = model;
+		res->u.multiple_results = g_slist_append (res->u.multiple_results, subres);
+		
+		if (dbo->obj_type == GDA_META_DB_VIEW) {
+			/* VIEW specific */
+			GdaMetaView *mv = GDA_META_DB_OBJECT_GET_VIEW (dbo);
+			
 			subres = g_new0 (GdaInternalCommandResult, 1);
-			subres->type = GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL;
-			subres->u.model = model;
+			subres->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
+			subres->u.txt = g_string_new ("");
+			g_string_append_printf (subres->u.txt, _("View definition: %s"), mv->view_def);
 			res->u.multiple_results = g_slist_append (res->u.multiple_results, subres);
-
-			if (dbo->obj_type == GDA_META_DB_VIEW) {
-				GdaMetaView *mv = GDA_META_DB_OBJECT_GET_VIEW (dbo);
-				
-				subres = g_new0 (GdaInternalCommandResult, 1);
-				subres->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
-				subres->u.txt = g_string_new ("");
-				g_string_append_printf (subres->u.txt, _("View definition: %s"), mv->view_def);
-				res->u.multiple_results = g_slist_append (res->u.multiple_results, subres);
-			}
-			break;
 		}
-		default:
-			TO_IMPLEMENT;
-			break;
+		else {
+			/* TABLE specific */
+			GValue *catalog, *schema, *name;
+			gint i, nrows;
+			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 "
+				"ORDER BY constraint_type, constraint_name";
+
+			g_value_set_string ((catalog = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog);
+			g_value_set_string ((schema = gda_value_new (G_TYPE_STRING)), dbo->obj_schema);
+			g_value_set_string ((name = gda_value_new (G_TYPE_STRING)), dbo->obj_name);
+			model = gda_meta_store_extract (gda_connection_get_meta_store (cnc), sql, error, 
+							"tc", catalog, "ts", schema, "tname", name, NULL);
+			nrows = gda_data_model_get_n_rows (model);
+			for (i = 0; i < nrows; i++) {
+				GString *string = NULL;
+				const GValue *cvalue;
+				const gchar *str;
+
+				cvalue = gda_data_model_get_value_at (model, 0, i);
+				str = g_value_get_string (cvalue);
+				if (*str == 'P') {
+					/* primary key */
+					GdaDataModel *cols;
+					cvalue = gda_data_model_get_value_at (model, 1, i);
+					string = g_string_new ("Primary key ");
+					g_string_append_printf (string, "'%s'", g_value_get_string (cvalue));
+					str = "SELECT column_name, ordinal_position "
+						"FROM _key_column_usage WHERE table_catalog = ##tc::string "
+						"AND table_schema = ##ts::string AND table_name = ##tname::string AND "
+						"constraint_name = ##cname::string "
+						"ORDER BY ordinal_position";
+					
+					cols = gda_meta_store_extract (gda_connection_get_meta_store (cnc), str, error, 
+								       "tc", catalog, "ts", schema, "tname", name, "cname", cvalue, 
+								       NULL);
+					if (cols) {
+						gint j, cnrows;
+						cnrows = gda_data_model_get_n_rows (cols);
+						for (j = 0; j < cnrows; j++) {
+							if (j == 0)
+								g_string_append (string, ": ");
+							else
+								g_string_append (string, ", ");
+							g_string_append (string, 
+									 g_value_get_string (gda_data_model_get_value_at (cols, 0, j)));
+						}
+						g_object_unref (cols);
+					}
+				}
+				else if (*str == 'F') {
+					/* foreign key */
+					cvalue = gda_data_model_get_value_at (model, 1, i);
+					string = g_string_new ("Foreign key ");
+					g_string_append_printf (string, "'%s'", g_value_get_string (cvalue));
+				}
+				else if (*str == 'U') {
+					/* Unique constraint */
+					cvalue = gda_data_model_get_value_at (model, 1, i);
+					string = g_string_new ("Unique ");
+					g_string_append_printf (string, "'%s'", g_value_get_string (cvalue));
+				}
+
+				if (string) {
+					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);
+				}
+			}
+
+			gda_value_free (catalog);
+			gda_value_free (schema);
+			gda_value_free (name);
+
+			g_object_unref (model);
+			return res;
 		}
 	}
+	else 
+		TO_IMPLEMENT;
 
 	g_object_unref (mstruct);
 	return res;



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