libgda r3105 - in trunk: . doc/C doc/C/tmpl libgda libgda/sql-parser libgda/sqlite providers/postgres tests/parser tools



Author: vivien
Date: Wed Apr  2 20:22:59 2008
New Revision: 3105
URL: http://svn.gnome.org/viewvc/libgda?rev=3105&view=rev

Log:
2008-04-02  Vivien Malerba <malerba gnome-db org>

	* libgda/sqlite/gda-sqlite-provider.c: corrected mem leak (thanks to Phil Longstaff)
	* libgda/gda-connection.c: corrected a mis-usage of variable number of arguments in
	gda_connection_get_meta_store_data(), possibly fixes bug #524837
	* libgda/gda-data-comparator.[ch]: new object to make it easy to compare the contents
	of two data models.
	* libgda/gda-server-provider.h:
	* libgda/gda-connection.h:
	* libgda/Makefile.am:
	* libgda/libgda.h.in:
	* libgda/gda-column-index.[ch]:
	* libgda/gda-data-model-index.[ch]: removed useless code
	* libgda/gda-data-model.c:
	* libgda/gda-util.[ch]: removed unused functions: gda_utility_build_encoded_id(),
	gda_utility_build_decoded_id(), gda_file_load(), gda_file_save(), gda_string_hash_to_list(),
	and started to implement a new gda_compute_dml_statements() to compute INSERT, DELETE
	and UPDATE statements corresponding to a SELECT statement (for writable data models)
	* doc/C: added a HOWTO section for most common tasks, and misc. doc updates
	* tools/gda-list-server-op.c: corrected version 3.0 to 4.0
	* libgda/gda-statement.[ch]:
	* libgda/sql-parser/gda-statement-struct.c: s/statement_check_connection/statement_check_validity/
	for easier API understanding
	* libgda/sql-parser/gda-statement-struct.[ch]:
	* gda-statement-struct-parts.[ch]:
	* gda-statement-struct-pspec.h: when validating a statement, first make sure it has a correct
	structure, and then use a GdaMetaStruct object to find references to database objects.
	* tests/parser/Makefile.am:
	* tests/parser/testvalid.xml:
	* tests/parser/check_validation.c: added a new test to verify statement validation and
	computed (INSERT, UPDATE, DELETE) statements from SELECT statement
	* libgda/gda-easy.[ch]:
	* libgda/gda-init.c:
	* libgda/gda-connection.[ch]: applied patch from Daniel Espinosa to implement the
	gda_prepare_drop_table(), gda_perform_drop_table(), gda_prepare_create_table(),
	gda_perform_create_table() and gda_connection_create_operation(), and also implemented
	the gda_connection_perform_operation(), see bug #525603
	* providers/postgres/gda-postgres-meta.c: code cleanup
	* libgda/sql-parser/gda-statement-struct-*: added a check_validity() method which
	can be implemented by some kind of statement to validate a statement using a
	connection's meta data
	* tests/parser/testvalid.xml:
	* tests/parser/check_validation.c: new test for statement validation purposes


Added:
   trunk/doc/C/howto.xml
   trunk/doc/C/tmpl/gda-data-comparator.sgml
   trunk/libgda/gda-data-comparator.c
   trunk/libgda/gda-data-comparator.h
   trunk/tests/parser/check_validation.c
   trunk/tests/parser/testvalid.xml
Removed:
   trunk/doc/C/tmpl/gda-column-index.sgml
   trunk/doc/C/tmpl/gda-data-model-index.sgml
   trunk/libgda/gda-column-index.c
   trunk/libgda/gda-column-index.h
   trunk/libgda/gda-data-model-index.c
   trunk/libgda/gda-data-model-index.h
Modified:
   trunk/ChangeLog
   trunk/doc/C/gettingstarted.xml
   trunk/doc/C/libgda-4.0-docs.sgml
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/libgda-4.0.types.in
   trunk/doc/C/tmpl/gda-connection.sgml
   trunk/doc/C/tmpl/gda-convenient.sgml
   trunk/doc/C/tmpl/gda-sql-statement.sgml
   trunk/doc/C/tmpl/gda-statement.sgml
   trunk/doc/C/tmpl/gda-util.sgml
   trunk/libgda/Makefile.am
   trunk/libgda/gda-connection.c
   trunk/libgda/gda-connection.h
   trunk/libgda/gda-data-model.c
   trunk/libgda/gda-easy.c
   trunk/libgda/gda-easy.h
   trunk/libgda/gda-init.c
   trunk/libgda/gda-marshal.list
   trunk/libgda/gda-meta-store.c
   trunk/libgda/gda-meta-struct.c
   trunk/libgda/gda-server-provider.c
   trunk/libgda/gda-server-provider.h
   trunk/libgda/gda-statement.c
   trunk/libgda/gda-statement.h
   trunk/libgda/gda-util.c
   trunk/libgda/gda-util.h
   trunk/libgda/libgda.h.in
   trunk/libgda/sql-parser/gda-statement-struct-compound.c
   trunk/libgda/sql-parser/gda-statement-struct-decl.h
   trunk/libgda/sql-parser/gda-statement-struct-delete.c
   trunk/libgda/sql-parser/gda-statement-struct-insert.c
   trunk/libgda/sql-parser/gda-statement-struct-parts.c
   trunk/libgda/sql-parser/gda-statement-struct-parts.h
   trunk/libgda/sql-parser/gda-statement-struct-pspec.h
   trunk/libgda/sql-parser/gda-statement-struct-select.c
   trunk/libgda/sql-parser/gda-statement-struct-trans.c
   trunk/libgda/sql-parser/gda-statement-struct-unknown.c
   trunk/libgda/sql-parser/gda-statement-struct-update.c
   trunk/libgda/sql-parser/gda-statement-struct.c
   trunk/libgda/sql-parser/gda-statement-struct.h
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/providers/postgres/gda-postgres-meta.c
   trunk/tests/parser/   (props changed)
   trunk/tests/parser/Makefile.am
   trunk/tools/gda-list-server-op.c

Modified: trunk/doc/C/gettingstarted.xml
==============================================================================
--- trunk/doc/C/gettingstarted.xml	(original)
+++ trunk/doc/C/gettingstarted.xml	Wed Apr  2 20:22:59 2008
@@ -55,7 +55,9 @@
     <title>Connecting</title>
     <para>
       &LIBGDA; allows data sources (DSN) to be defined and refered to by a unique name which contains all the 
-      required information to actually open a connection (except the name and password if they are required). Of
+      required information to actually open a connection (except the name and password if they are required);
+      see the <link linkend="libgda-40-Configuration">Configuration section</link> for more information about
+      how to manage data sources. Of
       course it's still possible to open a connection without having defined a DSN, in which case a <emphasis>connection
       string</emphasis> is used to specify all the parameters resuired to open a connection. For more information
       about connection strings, see the <link linkend="gda-connection-open-from-string">gda_connection_open_from_string ()</link>'s documentation.
@@ -74,6 +76,10 @@
 	g_print ("CONNECTING\n");
 	connection = gda_connection_open_from_dsn ("calvaris", NULL, 
                                                    GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);
+	if (!connection) {
+		g_print ("CONNECTION FAILED\n");
+		return;
+	}
 	g_print ("CONNECTED\n");
       
         /* use the connection */

Added: trunk/doc/C/howto.xml
==============================================================================
--- (empty file)
+++ trunk/doc/C/howto.xml	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,345 @@
+<chapter id="howto">
+  <title>HOWTO for common tasks</title>
+  <para>
+    This section is a list of small HOWTOs to get started quickly for some specific tasks, without the
+    need to read all the documentation: quickly get to the code and read the corresponding documentation
+    for further details.
+  </para>
+
+  <sect1>
+    <title>Open a connection</title>
+    <para>
+      Opening a connection to a database creates a <link linkend="GdaConnection">GdaConnection</link>
+      object which is required to execute commands. The connections' parameters (which are specific to each
+      type of database) can be either specified when opening the connection, or be used to define a
+      data source (<link linkend="connections">DSN</link>) and the DSN name is then specified to open the connection.
+    </para>
+    <para>
+      For example, opening a connection to a PostgreSQL database named "mydb" one could use the following code:
+      <programlisting>
+GdaConnection *connection;
+connection = gda_connection_open_from_string ("PostgreSQL", "DB_NAME=mydb", 
+                                              "USERNAME=martin;PASSWORD=luther",
+                                              GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);
+if (!connection)
+    g_print ("CONNECTION FAILED\n");
+else {
+    /* use the opened connection */
+
+    /* close the connection (assuming it's not used by another object) */
+    g_object_unref (connection);
+}
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1>
+    <title>Define a data source (DSN)</title>
+    <para>
+      The <link linkend="libgda-40-Configuration">Configuration section</link> details how to manage data sources
+      definitions. To define a data source, one needs to create and fill a 
+      <link linkend="GdaDataSourceInfo">GdaDataSourceInfo</link> structure, and use the
+      <link linkend="gda-config-define-dsn">gda_config_define_dsn()</link> function.
+    </para>
+    <para>
+      For example the following code defines a data source:
+      <programlisting>
+GError *error = NULL;
+GdaDataSourceInfo dsn_info = {
+    "My DSN",
+    "MySQL",
+    "Sample MySQL data source",
+    "HOST=myserver;DB_NAME=testdb"
+    NULL,
+    FALSE
+};
+if (!gda_config_define_dsn (&amp;dsn_info, &amp;error)) {
+    /* an error occurred, the details are in error */
+    g_error_free (error);
+}
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1 id="howto-exec-select">
+    <title>Execute a SELECT command</title>
+    <para>
+      Any SQL command in &LIBGDA; must be converted to a <link linkend="GdaStatement">GdaStatement</link> object
+      which is then executed; if the statement defines some variable (place holders), then variables binding
+      (assigning values to variables) is done at execution time. Each database provider (database 
+      <emphasis>driver</emphasis> or <emphasis>adaptator</emphasis>) can implement its own parser to 
+      be able to parse its specific SQL dialect. Except for binding variables, and for reusability, the
+      <link linkend="GdaStatement">GdaStatement</link> object guarantees that it contains only one SQL statement
+      (it's not possible for it to contain several ones separated by a semicolon).
+    </para>
+    <para>
+      Parsing an SQL statement into a <link linkend="GdaStatement">GdaStatement</link> object is the job of an
+      <link linkend="SqlParser">SqlParser</link>.
+    </para>
+    <para>
+      However when one only wants to run a simple SQL statement, &LIBGDA; provides the
+      <link linkend="gda-execute-select-command">gda_execute_select_command()</link> function which does
+      everything in one step. The following codes illustrates parsing and executing a SELECT statement:
+    </para>
+    <para>
+      <programlisting>
+GdaSqlParser *parser;
+GdaStatament *stmt;
+GError *error = NULL;
+
+parser = gda_connection_create_parser (cnc);
+if (!parser) {
+    /* the database provider used by cnc does not implement its own parser,
+     * let's use a generic one */
+    parser = gda_sql_parser_new ();
+stmt = gda_sql_parser_parse_string (parser, "SELECT * FROM customers", NULL, &amp;error);
+g_object_unref (parser);
+if (!stmt) {
+    /* there was an error while parsing */
+}
+else {
+    GdaDataModel *model;
+    model = gda_connection_statement_execute_select (cnc, stmt, NULL, &amp;error);
+    if (model) {
+        /* dump to results to STDOUT */
+        gda_data_model_dump (model, stdout);
+        g_object_unref (model);
+    }
+    else {
+        /* there was an error while executing the statement */
+    }
+}
+g_object_unref (stmt);
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1 id="howto-exec-non-select">
+    <title>Execute an INSERT, UPDATE or DELETE command</title>
+    <para>
+      INSERT, UPDATE or DELETE are treated in the same way as the SELECT command (see the 
+      <link linkend="howto-exec-select">HOWTO</link> about executing
+      a SELECT command), except that the function to execute the command is different because the retuned value
+      is not a data model but a success/failure value.
+    </para>
+    <para>
+      The following codes illustrates parsing and executing an UPDATE statement:
+      <programlisting>
+GdaSqlParser *parser;
+GdaStatament *stmt;
+GError *error = NULL;
+
+parser = gda_connection_create_parser (cnc);
+if (!parser) {
+    /* the database provider used by cnc does not implement its own parser,
+     * let's use a generic one */
+    parser = gda_sql_parser_new ();
+stmt = gda_sql_parser_parse_string (parser, "UPDATE customers set name='Joe' WHERE id=123", NULL, &amp;error);
+g_object_unref (parser);
+if (!stmt) {
+    /* there was an error while parsing */
+}
+else {
+    gint res;
+    res = gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, &amp;error);
+    if (res == -1) {
+        /* there was an error while executing the statement */
+    }
+    else if (res >= 0) 
+        g_print ("Command Ok, %d row(s) impacted\n", res);
+    else
+        g_print ("Command Ok, number of rows impacted is not reported\n");
+}
+g_object_unref (stmt);
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1>
+    <title>Get the last inserted row</title>
+    <para>
+      &LIBGDA; allows one to get the last inserted row right after an INSERT statement has been
+      executed. &LIBGDA; returns a new <link linkend="GdaSet">GdaSet</link> object (which the caller
+      must unref when not needed anymore) which contains named values, one for each column of the
+      table in which data has been inserted. To get that object, pass a place holder to the
+      <parameter>last_insert_row</parameter> of
+      <link linkend="gda-connection-statement-execute-non-select">gda_connection_statement_execute_non_select()</link>
+    </para>
+    <para>
+      The following code example show how to use the returned <link linkend="GdaSet">GdaSet</link> object:
+      <programlisting>
+GdaSet *last_row;
+GdaStatement *stmt;
+stmt = gda_sql_parser_parse_string (parser, "INSERT INTO mytable (name) VALUES ('joe')", NULL, NULL);
+if (gda_connection_statement_execute_non_select (connection, stmt, NULL, &amp;last_row, &amp;error) == -1) {
+    g_warning ("Can't execute INSERT: %s\n",
+              error &amp;&amp; error->message ? error->message : "???");
+    if (error)
+        g_error_free (error);
+}
+else {
+    if (!last_row)
+        g_print ("Last row not reported\n");
+    else {
+        GSList *list;
+        for (list = last_row->holders; list; list = list->next) {
+            GdaHolder *h = GDA_HOLDER (list->data);
+            gchar *str;
+            str = gda_value_stringify (gda_holder_get_value (h));
+            g_print ("\t%s => %s\n", gda_holder_get_id (h), str);
+            g_free (str);
+        }
+    g_object_unref (last_row);
+}
+g_object_unref (stmt);
+      </programlisting>
+      Which gives the following output (considering that in the example the "mytable" table has two columns: an Id
+      and a Name columns):
+      <programlisting>
+        +0 => 1
+        +1 => joe
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1>
+    <title>Execute a DDL command</title>
+    <para>
+      DDL commands (commands to modify the database schema such as create tables and views, change
+      users' access rights, etc) are treated in the same way as non select commands (refer to 
+      the <link linkend="howto-exec-select">HOWTO</link> about executing non SELECT commands). However
+      &LIBGDA; offers a better and more portable way of executing such commands: using a
+      <link linkend="GdaServerOperation">GdaServerOperation</link> object.
+    </para>
+    <para>
+      Executing a DDL command involves the following steps:
+      <orderedlist>
+	<listitem><para>Request a new <link linkend="GdaServerOperation">GdaServerOperation</link> object from
+	    the database provider for a specific operation using the 
+	    <link linkend="gda-server-provider-create-operation">gda_server_provider_create_operation()</link> function.
+	</para></listitem>
+	<listitem><para>Specify the <link linkend="GdaServerOperation">GdaServerOperation</link> object's 
+	    behaviour by setting some pre-defined parameters; for example when creating a table, the parameters
+	    to be set include the tables name, the names of the columns, the constraints, etc. The list
+	    of parameters to set is listed by the <link linkend="libgda-list-server-op">gda-list-server-op</link>
+	    program.
+	</para></listitem>
+	<listitem><para>Ask the server provider to execute the operation based on the 
+	    <link linkend="GdaServerOperation">GdaServerOperation</link> object
+	</para></listitem>
+      </orderedlist>
+    </para>
+    <para>
+      The following code in part illustrates how to create a view:
+      <programlisting>
+GdaServerOperation *op;
+
+op = gda_connection_create_operation (cnc, GDA_SERVER_OPERATION_CREATE_VIEW, NULL, &amp;error);
+if (!op)
+    /* there was an error while creating the GdaServerOperation object */
+else {
+    /* define the view to create */
+    if (!gda_server_operation_set_value_at (op, "myview", &amp;error, 
+                                            "/VIEW_DEF_P/VIEW_NAME") ||
+        !gda_server_operation_set_value_at (op, "SELECT * FROM customers", &amp;error, 
+                                            "/VIEW_DEF_P/VIEW_DEF"))
+        /* there was an error */
+    else {
+        if (! gda_connection_perform_operation (cnc, op, &amp;error))
+            g_print ("Error\n");
+        else
+            g_print ("View created\n");
+    }
+    g_object_unref (op);
+}
+      </programlisting>
+    </para>
+    <para>
+      Please also note that &LIBGDA; provides some convenient functions to wrap this process, see the
+      <link linkend="libgda-40-Convenient-functions">Convenient functions</link> section for more
+      information.
+    </para>
+  </sect1>
+
+  <sect1 id="howto-meta1">
+    <title>Get information about a table's columns</title>
+    <para>
+      &LIBGDA; supports reporting meta data about a database (for which there is an opened connection). The
+      meta data are stored in a database (usually an in-memory database) which structure is close to the 
+      information schema SQL standard (ISO/IEC 9075), and adapted (form information, this database is managed by a
+      <link linkend="GdaMetaStore">GdaMetaStore</link> object). As databases don't notify the changes made to
+      their objects, it is necessary to update (or synchronize) the meta data if the database's schema has been
+      changed, or if the meta data has not yet been extracted: call
+      <link linkend="gda-connection-update-meta-store">gda_connection_update_meta_store()</link> for this purpose.
+    </para>
+    <para>
+      One then needs to find the data requested among the (rather large) quantity of meta data available, there are
+      two possibilities:
+      <itemizedlist>
+	<listitem><para>Knowing the information schema's structure (tables and views) used, 
+	    run some SELECT commands on the 
+	    <link linkend="GdaMetaStore">GdaMetaStore</link> object's internal connection: this solution is the most
+	    powerfull but requires some knowledge of the information schema's structure, and the data
+	    is not easy to use.</para></listitem>
+	<listitem><para>Use a <link linkend="GdaMetaStruct">GdaMetaStruct</link> object which 
+	    <emphasis>exports</emphasis> the meta data as a dynamic tree of pre-defined data structures, 
+	    easy to use.</para></listitem>
+      </itemizedlist>
+    </para>
+    <para>
+      The following code shows how to list all the colums of the "customers" table of a connection (the connection
+      is assumed to already be opened):
+      <programlisting>
+GdaMetaStruct *mstruct;
+GdaMetaDbObject *dbo;
+GValue *table_name;
+if (!gda_connection_update_meta_store (cnc, NULL, &amp;error)) {
+    /* there was an error */
+    return;
+}
+mstruct = gda_meta_struct_new (GDA_META_STRUCT_FEATURE_NONE);
+table_name = gda_value_new (G_TYPE_STRING);
+g_value_set_string (value, "customers");
+dbo = gda_meta_struct_complement (mstruct, gda_connection_get_meta_store (cnc),
+                                  GDA_META_DB_TABLE, NULL, NULL, 
+                                  table_name, &amp;error);
+gda_value_free (table_name);
+if (dbo) {
+    /* the "customers" table has been found, its details are in dbo */
+    GdaMetaTable *table = GDA_META_DB_OBJECT_GET_TABLE (dbo);
+    GSList *list;
+    for (list = table->columns: list; list = list->next) 
+        g_print ("Column: %s\n", ((GdaMetaTableColumn*) list->data)->column_name);
+}
+else
+    g_print ("Table not found\n");
+g_object_unref (mstruct);
+      </programlisting>
+    </para>
+  </sect1>
+
+  <sect1>
+    <title>Validate a DML statement</title>
+    <para>
+      &LIBGDA; supports validating DML (SELECT, INSERT, UPDATE and DELETE) statements: making sure every
+      database object referenced in the statement actually exists in the database being accessed through
+      a connection.
+      The validation process involves the meta data associated to a connection, son one must make that
+      meta data is up to date with the database being accessed, using 
+      <link linkend="gda-connection-update-meta-store">gda_connection_update_meta_store()</link> (also see the
+      <link linkend="howto-meta1">Get information about a table's columns</link> section).
+    </para>
+    <para>
+      The following code shows how to validate a statement (assuming the statement already exists):
+      <programlisting>
+if (!gda_statement_check_validity (stmt, cnc, &amp;error))
+    g_print ("Invalid statement: %s\n",
+             error &amp;&amp; error->message ? error->message : "No detail");
+else
+    g_print ("Statement is valid\n");
+      </programlisting>
+    </para>
+  </sect1>
+
+</chapter>
+

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	Wed Apr  2 20:22:59 2008
@@ -55,8 +55,6 @@
 <!ENTITY libgda-GdaDataProxy SYSTEM "xml/gda-data-proxy.xml">
 <!ENTITY libgda-GdaConnection-event SYSTEM "xml/gda-connection-event.xml">
 <!ENTITY libgda-GdaColumn SYSTEM "xml/gda-column.xml">
-<!ENTITY libgda-GdaColumnIndex SYSTEM "xml/gda-column-index.xml">
-<!ENTITY libgda-GdaDataModelIndex SYSTEM "xml/gda-data-model-index.xml">
 <!ENTITY libgda-init SYSTEM "xml/libgda.xml">
 <!ENTITY libgda-log SYSTEM "xml/gda-log.xml">
 <!ENTITY libgda-quark-list SYSTEM "xml/gda-quark-list.xml">
@@ -102,9 +100,11 @@
 <!ENTITY libgda-PStmt SYSTEM "xml/gda-pstmt.xml">
 <!ENTITY libgda-Enums SYSTEM "xml/gda-enums.xml">
 <!ENTITY libgda-convenient SYSTEM "xml/gda-convenient.xml">
+<!ENTITY libgda-GdaDataComparator SYSTEM "xml/gda-data-comparator.xml">
 <!ENTITY provider-writing SYSTEM "prov-writing.xml">
 <!ENTITY provider-support SYSTEM "xml/provider-support.xml">
 <!ENTITY fdl-appendix SYSTEM "fdl-appendix.sgml">
+<!ENTITY howto SYSTEM "howto.xml">
 <!ENTITY libgda-TreeIndex SYSTEM "xml/tree_index.sgml">
 ]>
 
@@ -412,6 +412,7 @@
       The following sections describe the API available for &GDA; applications.
     </para>
 
+    &howto;
     <chapter>
       <title>Object Hierarchy</title>
       &libgda-TreeIndex;
@@ -567,10 +568,9 @@
       &libgda-GdaDataModelArray;
       &libgda-GdaDataModelBdb;
       &libgda-GdaDataModelDir;
-      &libgda-GdaDataModelQuery;
       &libgda-GdaDataProxy;
-      &libgda-GdaDataModelIndex;
-      &libgda-GdaColumnIndex;
+      &libgda-GdaDataComparator;
+      &libgda-GdaDataModelQuery;
       <para>
 	The following UML diagram shows the various implementations of the GdaDataModel interface and their usage:
 	<mediaobject>

Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt	(original)
+++ trunk/doc/C/libgda-4.0-sections.txt	Wed Apr  2 20:22:59 2008
@@ -1,32 +1,4 @@
 <SECTION>
-<FILE>gda-column-index</FILE>
-<TITLE>GdaColumnIndex</TITLE>
-GdaColumnIndex
-GdaSorting
-GdaColumnIndex
-GdaColumnIndexClass
-GdaColumnIndexPrivate
-gda_column_index_new
-gda_column_index_copy
-gda_column_index_equal
-gda_column_index_get_column_name
-gda_column_index_set_column_name
-gda_column_index_get_defined_size
-gda_column_index_set_defined_size
-gda_column_index_get_sorting
-gda_column_index_set_sorting
-gda_column_index_get_references
-gda_column_index_set_references
-<SUBSECTION Standard>
-GDA_COLUMN_INDEX
-GDA_COLUMN_INDEX_CLASS
-GDA_IS_COLUMN_INDEX
-GDA_IS_COLUMN_INDEX_CLASS
-GDA_TYPE_COLUMN_INDEX
-gda_column_index_get_type
-</SECTION>
-
-<SECTION>
 <FILE>gda-column</FILE>
 <TITLE>GdaColumn</TITLE>
 GdaColumn
@@ -174,8 +146,12 @@
 gda_connection_get_events
 gda_connection_clear_events_list
 <SUBSECTION>
+gda_connection_create_operation
+gda_connection_perform_operation
+<SUBSECTION>
 GdaConnectionFeature
 gda_connection_supports_feature
+gda_connection_get_meta_store
 gda_connection_update_meta_store
 GdaConnectionMetaType
 gda_connection_get_meta_store_data
@@ -318,32 +294,6 @@
 gda_data_access_wrapper_get_type
 </SECTION>
 
-
-<SECTION>
-<FILE>gda-data-model-index</FILE>
-<TITLE>GdaDataModelIndex</TITLE>
-GdaDataModelIndex
-gda_data_model_index_new
-gda_data_model_index_copy
-gda_data_model_index_free
-gda_data_model_index_equal
-gda_data_model_index_get_name
-gda_data_model_index_set_name
-gda_data_model_index_get_table_name
-gda_data_model_index_set_table_name
-gda_data_model_index_get_primary_key
-gda_data_model_index_set_primary_key
-gda_data_model_index_get_unique_key
-gda_data_model_index_set_unique_key
-gda_data_model_index_get_references
-gda_data_model_index_set_references
-gda_data_model_index_get_column_index_list
-gda_data_model_index_set_column_index_list
-<SUBSECTION Standard>
-GDA_TYPE_DATA_MODEL_INDEX
-gda_data_model_index_get_type
-</SECTION>
-
 <SECTION>
 <FILE>gda-data-model</FILE>
 <TITLE>GdaDataModel</TITLE>
@@ -965,8 +915,10 @@
 gda_execute_select_command
 gda_execute_non_select_command
 <SUBSECTION>
-gda_create_table
-gda_drop_table
+gda_prepare_create_table
+gda_perform_create_table
+gda_prepare_drop_table
+gda_perform_drop_table
 <SUBSECTION>
 gda_insert_row_into_table
 gda_insert_row_into_table_from_string
@@ -1130,7 +1082,7 @@
 gda_statement_get_statement_type
 gda_statement_is_useless
 gda_statement_check_structure
-gda_statement_check_connection
+gda_statement_check_validity
 <SUBSECTION Standard>
 GDA_IS_STATEMENT
 GDA_STATEMENT
@@ -1190,7 +1142,7 @@
 gda_sql_statement_type_to_string
 gda_sql_statement_string_to_type
 gda_sql_statement_check_structure
-gda_sql_statement_check_connection
+gda_sql_statement_check_validity
 gda_sql_statement_check_clean
 <SUBSECTION>
 GdaSqlAnyPart
@@ -1474,3 +1426,25 @@
 <SUBSECTION>
 gda_connection_get_meta_store
 </SECTION>
+
+<SECTION>
+<FILE>gda-data-comparator</FILE>
+<TITLE>GdaDataComparator</TITLE>
+GdaDataComparator
+GdaDataComparatorClass
+GdaDataComparatorPrivate
+gda_data_comparator_new
+gda_data_comparator_set_key_columns
+GdaDiff
+GdaDiffType
+gda_data_comparator_compute_diff
+gda_data_comparator_get_n_diffs
+gda_data_comparator_get_diff
+<SUBSECTION Standard>
+GDA_DATA_COMPARATOR
+GDA_DATA_COMPARATOR_CLASS
+GDA_DATA_COMPARATOR_ERROR
+GDA_IS_DATA_COMPARATOR
+GDA_TYPE_DATA_COMPARATOR
+gda_data_comparator_get_type
+</SECTION>

Modified: trunk/doc/C/libgda-4.0.types.in
==============================================================================
--- trunk/doc/C/libgda-4.0.types.in	(original)
+++ trunk/doc/C/libgda-4.0.types.in	Wed Apr  2 20:22:59 2008
@@ -8,7 +8,6 @@
 #include <DocBook/gda-report-docbook-document.h>
 gda_blob_op_get_type
 gda_column_get_type
-gda_column_index_get_type
 gda_config_get_type
 gda_connection_get_type
 gda_connection_event_get_type
@@ -25,7 +24,6 @@
 gda_data_model_row_get_type
 gda_data_model_get_type
 gda_data_model_import_get_type
-gda_data_model_index_get_type
 gda_data_model_iter_get_type
 gda_data_model_query_get_type
 gda_data_access_wrapper_get_type

Modified: trunk/doc/C/tmpl/gda-connection.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-connection.sgml	(original)
+++ trunk/doc/C/tmpl/gda-connection.sgml	Wed Apr  2 20:22:59 2008
@@ -458,6 +458,29 @@
 @cnc: 
 
 
+<!-- ##### FUNCTION gda_connection_create_operation ##### -->
+<para>
+
+</para>
+
+ cnc: 
+ type: 
+ options: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_connection_perform_operation ##### -->
+<para>
+
+</para>
+
+ cnc: 
+ op: 
+ error: 
+ Returns: 
+
+
 <!-- ##### ENUM GdaConnectionFeature ##### -->
 <para>
 
@@ -490,6 +513,15 @@
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_connection_get_meta_store ##### -->
+<para>
+
+</para>
+
+ cnc: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_connection_update_meta_store ##### -->
 <para>
 

Modified: trunk/doc/C/tmpl/gda-convenient.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-convenient.sgml	(original)
+++ trunk/doc/C/tmpl/gda-convenient.sgml	Wed Apr  2 20:22:59 2008
@@ -81,7 +81,7 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_create_table ##### -->
+<!-- ##### FUNCTION gda_prepare_create_table ##### -->
 <para>
 
 </para>
@@ -93,7 +93,17 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_drop_table ##### -->
+<!-- ##### FUNCTION gda_perform_create_table ##### -->
+<para>
+
+</para>
+
+ op: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_prepare_drop_table ##### -->
 <para>
 
 </para>
@@ -104,6 +114,16 @@
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_perform_drop_table ##### -->
+<para>
+
+</para>
+
+ op: 
+ error: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_insert_row_into_table ##### -->
 <para>
 

Added: trunk/doc/C/tmpl/gda-data-comparator.sgml
==============================================================================
--- (empty file)
+++ trunk/doc/C/tmpl/gda-data-comparator.sgml	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,124 @@
+<!-- ##### SECTION Title ##### -->
+GdaDataComparator
+
+<!-- ##### SECTION Short_Description ##### -->
+Simple data model's contents comparison
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+  The #GdaDataComparator is a simple object which takes two #GdaDataModel objects and compare them.
+  Actual comparison is performed when the gda_data_comparator_compute_diff() is called; for each
+  difference found, the <link linkend="GdaDataComparator-diff-computed">diff-computed</link> signal
+  is emitted (any user installed signal handler which returns FALSE stops the computing process).
+</para>
+<para>
+  After the differences have been computed, they can each be accessed using gda_data_comparator_get_diff(),
+  the number of differences found being returned by gda_data_comparator_get_n_diffs().
+</para>
+<para>
+  There are some limitations to this object:
+  <itemizedlist>
+    <listitem><para>The data models compared must have the same number and type of columns</para></listitem>
+    <listitem><para>The comparison is done column-for-column: one cannot omit columns in the comparison, nor compare
+    columns with different positions</para></listitem>
+  </itemizedlist>
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### STRUCT GdaDataComparator ##### -->
+<para>
+
+</para>
+
+ object: 
+ priv: 
+
+<!-- ##### STRUCT GdaDataComparatorClass ##### -->
+<para>
+
+</para>
+
+ parent_class: 
+ diff_computed: 
+
+<!-- ##### STRUCT GdaDataComparatorPrivate ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### FUNCTION gda_data_comparator_new ##### -->
+<para>
+
+</para>
+
+ old_model: 
+ new_model: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_data_comparator_set_key_columns ##### -->
+<para>
+
+</para>
+
+ comp: 
+ col_numbers: 
+ nb_cols: 
+
+
+<!-- ##### STRUCT GdaDiff ##### -->
+<para>
+
+</para>
+
+ type: 
+ old_row: 
+ new_row: 
+ values: 
+
+<!-- ##### ENUM GdaDiffType ##### -->
+<para>
+
+</para>
+
+ GDA_DIFF_ADD_ROW: 
+ GDA_DIFF_REMOVE_ROW: 
+ GDA_DIFF_MODIFY_ROW: 
+
+<!-- ##### FUNCTION gda_data_comparator_compute_diff ##### -->
+<para>
+
+</para>
+
+ comp: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_data_comparator_get_n_diffs ##### -->
+<para>
+
+</para>
+
+ comp: 
+ Returns: 
+
+
+<!-- ##### FUNCTION gda_data_comparator_get_diff ##### -->
+<para>
+
+</para>
+
+ comp: 
+ pos: 
+ Returns: 
+
+

Modified: trunk/doc/C/tmpl/gda-sql-statement.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-sql-statement.sgml	(original)
+++ trunk/doc/C/tmpl/gda-sql-statement.sgml	Wed Apr  2 20:22:59 2008
@@ -43,6 +43,7 @@
 @stmt_type: the type of statement (SELECT, INSERT, etc) as a #GdaSqlStatementType enum
 @contents: the actual contents of the statement, depends on @stmt_type (can be a pointer to a
 #GdaSqlStatementSelect, #GdaSqlStatementInsert, etc)
+ validity_meta_struct: 
 
 <!-- ##### ENUM GdaSqlStatementType ##### -->
 <para>
@@ -126,7 +127,7 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_sql_statement_check_connection ##### -->
+<!-- ##### FUNCTION gda_sql_statement_check_validity ##### -->
 <para>
 
 </para>
@@ -688,7 +689,7 @@
 @is_param: 
 @nullok: 
 @g_type: 
- dict_type: 
+ validity_meta_dict: 
 
 <!-- ##### FUNCTION gda_sql_param_spec_new ##### -->
 <para>
@@ -768,6 +769,7 @@
 
 @any: inheritance structure
 @field_name: a table's field name, in the form [[[catalog.]schema.]table.]field_name
+ validity_meta_table_column: 
 
 <!-- ##### FUNCTION gda_sql_field_new ##### -->
 <para>
@@ -820,7 +822,7 @@
 
 @any: inheritance structure
 @table_name: a table's name, in the form [[catalog.]schema.]table
- full_table_name: 
+ validity_meta_object: 
 
 <!-- ##### FUNCTION gda_sql_table_new ##### -->
 <para>
@@ -874,7 +876,7 @@
 @any: inheritance structure
 @function_name: name of the function , in the form [[catalog.]schema.]function_name
 @args_list: list of #GdaSqlExpr expressions, one for each argument
- full_function_name: 
+ validity_meta_function: 
 
 <!-- ##### FUNCTION gda_sql_function_new ##### -->
 <para>
@@ -1084,7 +1086,8 @@
 @field_name: field name part of @expr if @expr represents a field
 @table_name: table name part of @expr if @expr represents a field
 @as: alias
- full_table_name: 
+ validity_meta_object: 
+ validity_meta_table_column: 
 
 <!-- ##### FUNCTION gda_sql_select_field_new ##### -->
 <para>
@@ -1158,7 +1161,7 @@
 @expr: expression
 @table_name: table name part of @expr if @expr represents a table
 @as: alias
- full_table_name: 
+ validity_meta_object: 
 
 <!-- ##### FUNCTION gda_sql_select_target_new ##### -->
 <para>

Modified: trunk/doc/C/tmpl/gda-statement.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-statement.sgml	(original)
+++ trunk/doc/C/tmpl/gda-statement.sgml	Wed Apr  2 20:22:59 2008
@@ -195,7 +195,7 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_statement_check_connection ##### -->
+<!-- ##### FUNCTION gda_statement_check_validity ##### -->
 <para>
 
 </para>

Modified: trunk/doc/C/tmpl/gda-util.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-util.sgml	(original)
+++ trunk/doc/C/tmpl/gda-util.sgml	Wed Apr  2 20:22:59 2008
@@ -17,15 +17,6 @@
 <!-- ##### SECTION Stability_Level ##### -->
 
 
-<!-- ##### FUNCTION gda_string_hash_to_list ##### -->
-<para>
-
-</para>
-
- hash_table: 
- Returns: 
-
-
 <!-- ##### FUNCTION gda_default_escape_string ##### -->
 <para>
 
@@ -35,26 +26,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_file_load ##### -->
-<para>
-
-</para>
-
- filename: 
- Returns: 
-
-
-<!-- ##### FUNCTION gda_file_save ##### -->
-<para>
-
-</para>
-
- filename: 
- buffer: 
- len: 
- Returns: 
-
-
 <!-- ##### FUNCTION gda_server_provider_get_schema_nb_columns ##### -->
 <para>
 

Modified: trunk/libgda/Makefile.am
==============================================================================
--- trunk/libgda/Makefile.am	(original)
+++ trunk/libgda/Makefile.am	Wed Apr  2 20:22:59 2008
@@ -28,11 +28,11 @@
 	gda-batch.h \
 	gda-blob-op.h \
 	gda-column.h \
-	gda-column-index.h \
 	gda-config.h \
 	gda-connection-event.h \
 	gda-connection.h \
 	gda-connection-private.h \
+	gda-data-comparator.h \
 	gda-data-handler.h \
 	gda-data-model-array.h \
 	gda-data-model-row.h \
@@ -41,7 +41,6 @@
 	gda-data-model-dir.h \
 	gda-data-model-extra.h \
 	gda-data-model-import.h \
-	gda-data-model-index.h \
 	gda-data-model-iter.h \
 	gda-data-model-private.h \
 	gda-data-model-query.h \
@@ -81,10 +80,10 @@
 	$(gda_headers) \
 	gda-blob-op.c \
 	gda-column.c \
-	gda-column-index.c \
 	gda-config.c \
 	gda-connection.c \
 	gda-connection-event.c \
+	gda-data-comparator.c \
 	gda-data-handler.c \
 	gda-data-model-array.c \
 	$(GDA_BDB_S) \
@@ -94,7 +93,6 @@
 	gda-data-model-dsn-list.c \
 	gda-data-model-dsn-list.h \
 	gda-data-model-import.c \
-	gda-data-model-index.c \
 	gda-data-model-iter.c \
 	gda-data-model-query.c \
 	gda-data-access-wrapper.c \

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Wed Apr  2 20:22:59 2008
@@ -1074,6 +1074,54 @@
 }
 
 /**
+ * gda_connection_create_operation
+ * @cnc: a #GdaConnection object
+ * @type: the type of operation requested
+ * @options: an optional list of parameters
+ * @error: a place to store an error, or %NULL
+ *
+ * Creates a new #GdaServerOperation object which can be modified in order 
+ * to perform the type type of action. It is a wrapper around the gda_server_provider_create_operation()
+ * method.
+ *
+ * Returns: a new #GdaServerOperation object, or %NULL in the connection's provider does not support the @type type
+ * of operation or if an error occurred
+ */
+GdaServerOperation*
+gda_connection_create_operation (GdaConnection *cnc, GdaServerOperationType type, 
+                                 GdaSet *options, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (cnc->priv, NULL);
+	g_return_val_if_fail (cnc->priv->provider_obj, NULL);
+
+	return gda_server_provider_create_operation (cnc->priv->provider_obj, cnc, type, options, error);
+}
+
+/**
+ * gda_connection_perform_operation
+ * @cnc: a #GdaConnection object
+ * @op: a #GdaServerOperation object
+ * @error: a place to store an error, or %NULL
+ *
+ * Performs the operation described by @op (which should have been created using
+ * gda_connection_create_operation()). It is a wrapper around the gda_server_provider_perform_operation()
+ * method.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_connection_perform_operation (GdaConnection *cnc, GdaServerOperation *op, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (cnc->priv, FALSE);
+	g_return_val_if_fail (cnc->priv->provider_obj, FALSE);
+	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
+
+	return gda_server_provider_perform_operation (cnc->priv->provider_obj, cnc, op, error);
+}
+
+/**
  * gda_connection_create_parser
  * @cnc: a #GdaConnection object
  *
@@ -1082,7 +1130,7 @@
  * then %NULL is returned, and a general SQL parser can be obtained
  * using gda_sql_parser_new().
  *
- * Returns: a new #GdaSqlParser object
+ * Returns: a new #GdaSqlParser object, or %NULL
  */
 GdaSqlParser *
 gda_connection_create_parser (GdaConnection *cnc)
@@ -2902,7 +2950,6 @@
 	static GHashTable *stmt_hash = NULL;
 	GdaStatement *stmt;
 	GdaSet *set = NULL;
-	va_list ap;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 	g_return_val_if_fail (cnc->priv->provider_obj, NULL);
@@ -2919,24 +2966,28 @@
 		stmt_hash = prepare_meta_statements_hash ();
 	key.meta_type = meta_type;
 	key.nb_filters = nb_filters;
-	key.filters = g_new (gchar *, nb_filters);
-	va_start (ap, nb_filters);
-	for (i = 0, fname = va_arg (ap, gchar*); fname && (i < nb_filters); fname = va_arg (ap, gchar*), i++) {
-		GdaHolder *h;
-		GValue *v;
-
-		v = va_arg (ap, GValue*);
-		if (!v || gda_value_is_null (v))
-			continue;
-		if (!set)
-			set = gda_set_new (NULL);
-		h = g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (v), "id", fname, NULL);
-		gda_holder_set_value (h, v);
-		gda_set_add_holder (set, h);
-		g_object_unref (h);
-		key.filters[i] = fname;
+	key.filters = NULL;
+	if (nb_filters > 0) {
+		va_list ap;
+		key.filters = g_new (gchar *, nb_filters);
+		va_start (ap, nb_filters);
+		for (i = 0, fname = va_arg (ap, gchar*); fname && (i < nb_filters); fname = va_arg (ap, gchar*), i++) {
+			GdaHolder *h;
+			GValue *v;
+			
+			v = va_arg (ap, GValue*);
+			if (!v || gda_value_is_null (v))
+				continue;
+			if (!set)
+				set = gda_set_new (NULL);
+			h = g_object_new (GDA_TYPE_HOLDER, "g-type", G_VALUE_TYPE (v), "id", fname, NULL);
+			gda_holder_set_value (h, v);
+			gda_set_add_holder (set, h);
+			g_object_unref (h);
+			key.filters[i] = fname;
+		}
+		va_end (ap);
 	}
-	va_end (ap);
 	stmt = g_hash_table_lookup (stmt_hash, &key);
 	g_free (key.filters);
 	if (!stmt) {

Modified: trunk/libgda/gda-connection.h
==============================================================================
--- trunk/libgda/gda-connection.h	(original)
+++ trunk/libgda/gda-connection.h	Wed Apr  2 20:22:59 2008
@@ -28,11 +28,11 @@
 
 #include "gda-decl.h"
 #include <libgda/gda-data-model.h>
-#include <libgda/gda-data-model-index.h>
 #include <libgda/gda-connection-event.h>
 #include <libgda/gda-transaction-status.h>
 #include <libgda/gda-statement.h>
 #include <libgda/gda-meta-store.h>
+#include <libgda/gda-server-operation.h>
 
 G_BEGIN_DECLS
 
@@ -145,6 +145,10 @@
 GdaServerProvider   *gda_connection_get_provider_obj     (GdaConnection *cnc);
 const gchar         *gda_connection_get_provider_name    (GdaConnection *cnc);
 
+GdaServerOperation  *gda_connection_create_operation     (GdaConnection *cnc, GdaServerOperationType type,
+                                                          GdaSet *options, GError **error);
+gboolean             gda_connection_perform_operation    (GdaConnection *cnc, GdaServerOperation *op, GError **error);
+                                                          
 const gchar         *gda_connection_get_dsn              (GdaConnection *cnc);
 gboolean             gda_connection_set_dsn              (GdaConnection *cnc, const gchar *datasource);
 const gchar         *gda_connection_get_cnc_string       (GdaConnection *cnc);

Added: trunk/libgda/gda-data-comparator.c
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-data-comparator.c	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,590 @@
+/* gda-data-comparator.c
+ *
+ * Copyright (C) 2008 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include "gda-data-comparator.h"
+#include "gda-marshal.h"
+#include "gda-data-model.h"
+
+/* 
+ * Main static functions 
+ */
+static void gda_data_comparator_class_init (GdaDataComparatorClass * class);
+static void gda_data_comparator_init (GdaDataComparator *srv);
+static void gda_data_comparator_dispose (GObject *object);
+static void gda_data_comparator_finalize (GObject *object);
+
+static void gda_data_comparator_set_property (GObject *object,
+					      guint param_id,
+					      const GValue *value,
+					      GParamSpec *pspec);
+static void gda_data_comparator_get_property (GObject *object,
+					      guint param_id,
+					      GValue *value,
+					      GParamSpec *pspec);
+/* get a pointer to the parents to be able to call their destructor */
+static GObjectClass  *parent_class = NULL;
+
+static void gda_diff_free (GdaDiff *diff);
+
+/* signals */
+enum
+{
+        DIFF_COMPUTED,
+        LAST_SIGNAL
+};
+
+static gint gda_data_comparator_signals[LAST_SIGNAL] = { 0 };
+
+
+/* properties */
+enum
+{
+	PROP_0,
+	PROP_OLD_MODEL,
+	PROP_NEW_MODEL
+};
+
+struct _GdaDataComparatorPrivate
+{
+	GdaDataModel      *old_model;
+	GdaDataModel      *new_model;
+	gint               nb_key_columns;
+	gint              *key_columns;
+	GArray            *diffs; /* array of GdaDiff pointers */
+};
+
+
+/* module error */
+GQuark gda_data_comparator_error_quark (void)
+{
+	static GQuark quark;
+	if (!quark)
+		quark = g_quark_from_static_string ("gda_data_comparator_error");
+	return quark;
+}
+
+GType
+gda_data_comparator_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (GdaDataComparatorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gda_data_comparator_class_init,
+			NULL,
+			NULL,
+			sizeof (GdaDataComparator),
+			0,
+			(GInstanceInitFunc) gda_data_comparator_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT, "GdaDataComparator", &info, 0);		
+	}
+	return type;
+}
+
+static gboolean
+diff_computed_accumulator (GSignalInvocationHint *ihint,
+			   GValue *return_accu,
+			   const GValue *handler_return,
+			   gpointer data)
+{
+        gboolean thisvalue;
+
+        thisvalue = g_value_get_boolean (handler_return);
+        g_value_set_boolean (return_accu, thisvalue);
+
+        return thisvalue; /* stop signal if 'thisvalue' is FALSE */
+}
+
+static gboolean
+m_diff_computed (GdaDataComparator *comparator, GdaDiff *diff)
+{
+        return TRUE; /* default is to allow differences computing to proceed */
+}
+
+static void
+gda_data_comparator_class_init (GdaDataComparatorClass *class)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (class);
+
+	parent_class = g_type_class_peek_parent (class);
+
+	/* signals */
+
+	gda_data_comparator_signals [DIFF_COMPUTED] =
+		g_signal_new ("diff_computed",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdaDataComparatorClass, diff_computed),
+                              diff_computed_accumulator, NULL,
+                              gda_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
+	class->diff_computed = m_diff_computed;
+
+	/* virtual functions */
+	object_class->dispose = gda_data_comparator_dispose;
+	object_class->finalize = gda_data_comparator_finalize;
+
+	/* Properties */
+	object_class->set_property = gda_data_comparator_set_property;
+	object_class->get_property = gda_data_comparator_get_property;
+
+	g_object_class_install_property (object_class, PROP_OLD_MODEL,
+					 g_param_spec_object ("old-model", _("Old data model"), NULL,
+                                                               GDA_TYPE_DATA_MODEL,
+							      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+	g_object_class_install_property (object_class, PROP_NEW_MODEL,
+					 g_param_spec_object ("new-model", _("New data model"), NULL,
+                                                               GDA_TYPE_DATA_MODEL,
+							      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+gda_data_comparator_init (GdaDataComparator *comparator)
+{
+	comparator->priv = g_new0 (GdaDataComparatorPrivate, 1);
+	comparator->priv->diffs = g_array_new (FALSE, FALSE, sizeof (GdaDiff *));
+}
+
+/**
+ * gda_data_comparator_new
+ * @old_model: Data model to which the modifications should be applied
+ * @new_model: Target data model.
+ *
+ * Creates a new comparator to compute the differences from @old_model to @new_model: if one applies
+ * all the computed differences (as #GdaDiff structures) to @old_model, the resulting data model
+ * should have the same contents as @new_model.
+ *
+ * Returns: a new #GdaDataComparator object
+ */
+GObject *
+gda_data_comparator_new (GdaDataModel *old_model, GdaDataModel *new_model)
+{
+	GObject *obj;
+
+	g_return_val_if_fail (GDA_IS_DATA_MODEL (old_model), NULL);
+	g_return_val_if_fail (GDA_IS_DATA_MODEL (new_model), NULL);
+
+	obj = g_object_new (GDA_TYPE_DATA_COMPARATOR, "old-model", old_model, "new-model", new_model, NULL);
+
+	return obj;
+}
+
+static void
+clean_diff (GdaDataComparator *comparator)
+{
+	if (comparator->priv->diffs) {
+		gint i;
+		for (i = 0; i < comparator->priv->diffs->len; i++) {
+			GdaDiff *diff = g_array_index (comparator->priv->diffs, GdaDiff *, i);
+			gda_diff_free (diff);
+		}
+		g_array_free (comparator->priv->diffs, TRUE);
+	}
+	comparator->priv->diffs = g_array_new (FALSE, FALSE, sizeof (GdaDiff *));
+}
+
+static void
+gda_data_comparator_dispose (GObject *object)
+{
+	GdaDataComparator *comparator;
+
+	g_return_if_fail (GDA_IS_DATA_COMPARATOR (object));
+
+	comparator = GDA_DATA_COMPARATOR (object);
+	if (comparator->priv) {
+		if (comparator->priv->old_model) {
+			g_object_unref (comparator->priv->old_model);
+			comparator->priv->old_model = NULL;
+		}
+		if (comparator->priv->new_model) {
+			g_object_unref (comparator->priv->new_model);
+			comparator->priv->new_model = NULL;
+		}
+		clean_diff (comparator);
+		g_free (comparator->priv->key_columns);
+		g_array_free (comparator->priv->diffs, TRUE);
+	}
+
+	/* parent class */
+	parent_class->dispose (object);
+}
+
+static void
+gda_data_comparator_finalize (GObject *object)
+{
+	GdaDataComparator *comparator;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GDA_IS_DATA_COMPARATOR (object));
+
+	comparator = GDA_DATA_COMPARATOR (object);
+	if (comparator->priv) {
+		g_free (comparator->priv);
+		comparator->priv = NULL;
+	}
+
+	/* parent class */
+	parent_class->finalize (object);
+}
+
+
+static void 
+gda_data_comparator_set_property (GObject *object,
+				  guint param_id,
+				  const GValue *value,
+				  GParamSpec *pspec)
+{
+	GdaDataComparator *comparator;
+	comparator = GDA_DATA_COMPARATOR (object);
+	if (comparator->priv) {
+		GdaDataModel *model;
+
+		switch (param_id) {
+		case PROP_OLD_MODEL:
+			model = (GdaDataModel*) g_value_get_object (value);
+			if (comparator->priv->old_model && (comparator->priv->old_model != model)) {
+				/* re-init */
+				clean_diff (comparator);
+				g_object_unref (comparator->priv->old_model);
+				g_free (comparator->priv->key_columns);
+				comparator->priv->key_columns = NULL;
+			}
+			comparator->priv->old_model = model; 
+			if (model)
+				g_object_ref (model);
+			break;
+		case PROP_NEW_MODEL:
+			model = (GdaDataModel*) g_value_get_object (value);
+			if (comparator->priv->new_model && (comparator->priv->new_model != model)) {
+				/* re-init */
+				clean_diff (comparator);
+				g_object_unref (comparator->priv->new_model);
+				g_free (comparator->priv->key_columns);
+				comparator->priv->key_columns = NULL;
+			}
+			comparator->priv->new_model = model; 
+			if (model)
+				g_object_ref (model);
+			break;
+		}
+	}
+}
+
+static void
+gda_data_comparator_get_property (GObject *object,
+				  guint param_id,
+				  GValue *value,
+				  GParamSpec *pspec)
+{
+	GdaDataComparator *comparator;
+
+	comparator = GDA_DATA_COMPARATOR (object);
+	if (comparator->priv) {
+		switch (param_id) {
+		case PROP_OLD_MODEL:
+			g_value_set_object (value, comparator->priv->old_model);
+			break;
+		case PROP_NEW_MODEL:
+			g_value_set_object (value, comparator->priv->new_model);
+			break;
+		}
+	}
+}
+
+static void
+gda_diff_free (GdaDiff *diff)
+{
+	g_hash_table_destroy (diff->values);
+	g_free (diff);
+}
+
+/**
+ * gda_data_comparator_set_key_columns
+ * @comp: a #GdaDataComparator object
+ * @nb_cols: the size of the @col_numbers array
+ * @col_numbers: a array of @nb_cols values
+ *
+ * Defines the columns which will be used as a key when searching data. This is not mandatory but
+ * will speed things up as less data will be processed.
+ */
+void
+gda_data_comparator_set_key_columns (GdaDataComparator *comp, gint *col_numbers, gint nb_cols)
+{
+	g_return_if_fail (GDA_IS_DATA_COMPARATOR (comp));
+	g_return_if_fail (comp->priv);
+
+	g_free (comp->priv->key_columns);
+	comp->priv->key_columns = NULL;
+	if (nb_cols > 0) {
+		comp->priv->nb_key_columns = nb_cols;
+		comp->priv->key_columns = g_new0 (gint, nb_cols);
+		memcpy (comp->priv->key_columns, col_numbers, sizeof (gint) * nb_cols);
+	}
+}
+
+/*
+ * Find the row in @comp->priv->old_model from the values of @comp->priv->new_model at line @row
+ * It is assumed that both data model have the same number of columns and of "compatible" types.
+ *
+ * Returns: 
+ *          -1 if not found, 
+ *          >=0 if found (if changes need to be made, then @out_has_changed is set to TRUE).
+ */
+static gint
+find_row_in_model (GdaDataComparator *comp, gint row, gboolean *out_has_changed, GError **error)
+{
+	gint i, erow;
+	gint ncols;
+	GSList *values = NULL;
+	
+	*out_has_changed = FALSE;
+	ncols = gda_data_model_get_n_columns (comp->priv->old_model);
+	if (!comp->priv->key_columns) {
+		comp->priv->nb_key_columns = ncols;
+		comp->priv->key_columns = g_new (gint, ncols);
+		for (i = 0; i < ncols; i++)
+			comp->priv->key_columns [i] = i;
+	}
+
+	for (i = 0; i < comp->priv->nb_key_columns; i++) 
+		values = g_slist_append (values, 
+					 (gpointer) gda_data_model_get_value_at (comp->priv->new_model, 
+										 comp->priv->key_columns[i], row));
+
+	erow = gda_data_model_get_row_from_values (comp->priv->old_model, values, comp->priv->key_columns);
+	g_slist_free (values);
+	
+	if (erow >= 0) {
+		gboolean changed = FALSE;
+		for (i = 0; i < ncols; i++) {
+			const GValue *v1, *v2;
+			v1 = gda_data_model_get_value_at (comp->priv->old_model, i, erow);
+			v2 = gda_data_model_get_value_at (comp->priv->new_model, i, row);
+			if (gda_value_compare_ext (v1, v2)) {
+				changed = TRUE;
+				break;
+			}
+		}
+		*out_has_changed = changed;
+	}
+	
+	return erow;
+}
+
+/**
+ * gda_data_comparator_compute_diff
+ * @comp: a #GdaDataComparator object
+ * @error: a place to store errors, or %NULL
+ *
+ * Actually computes the differences bewteen the data models for which @comp is defined. 
+ *
+ * For each difference computed, stored in a #GdaDiff structure, the "diff-computed" signal is emitted.
+ * If one connects to this signal and returns FALSE in the signal handler, then computing differences will be
+ * stopped and an error will be returned.
+ *
+ * Returns: TRUE if all the differences have been sucessfully computed
+ */
+gboolean
+gda_data_comparator_compute_diff (GdaDataComparator *comp, GError **error)
+{
+	gint oncols, nncols, i;
+	gint onrows, nnrows;
+	gboolean *rows_to_del = NULL;
+
+	g_return_val_if_fail (GDA_IS_DATA_COMPARATOR (comp), FALSE);
+	g_return_val_if_fail (comp->priv, FALSE);
+
+	clean_diff (comp);
+
+	/* check setup */
+	if (!comp->priv->old_model) {
+		g_set_error (error, GDA_DATA_COMPARATOR_ERROR, GDA_DATA_COMPARATOR_MISSING_DATA_MODEL_ERROR,
+			     _("Missing original data model"));
+		return FALSE;
+	}
+	if (!comp->priv->new_model) {
+		g_set_error (error, GDA_DATA_COMPARATOR_ERROR, GDA_DATA_COMPARATOR_MISSING_DATA_MODEL_ERROR,
+			     _("Missing new data model"));
+		return FALSE;
+	}
+	if (! (gda_data_model_get_access_flags (comp->priv->old_model) & GDA_DATA_MODEL_ACCESS_RANDOM) ||
+	    ! (gda_data_model_get_access_flags (comp->priv->new_model) & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+		g_set_error (error, GDA_DATA_COMPARATOR_ERROR, GDA_DATA_COMPARATOR_MODEL_ACCESS_ERROR,
+			     _("Data models must support random access model"));
+		return FALSE;
+	}
+
+	/* compare columns */
+	oncols = gda_data_model_get_n_columns (comp->priv->old_model);
+	nncols = gda_data_model_get_n_columns (comp->priv->new_model);
+	if (oncols != nncols) {
+		g_set_error (error, GDA_DATA_COMPARATOR_ERROR, GDA_DATA_COMPARATOR_MISSING_DATA_MODEL_ERROR,
+			     _("Data models to compare don't have the same number of columns"));
+		return FALSE;
+	}
+
+	for (i = 0; i < oncols; i++) {
+		GdaColumn *ocol, *ncol;
+		ocol = gda_data_model_describe_column (comp->priv->old_model, i);
+		ncol = gda_data_model_describe_column (comp->priv->new_model, i);
+		if (gda_column_get_g_type (ocol) != gda_column_get_g_type (ncol)) {
+			g_set_error (error, GDA_DATA_COMPARATOR_ERROR, 
+				     GDA_DATA_COMPARATOR_COLUMN_TYPES_MISMATCH_ERROR,
+				     _("Type mismatch for column %d: '%s' and '%s'"), i,
+				     g_type_name (gda_column_get_g_type (ocol)),
+				     g_type_name (gda_column_get_g_type (ncol)));
+			return FALSE;
+		}
+	}
+
+	/* actual differences computations : rows to insert / update */
+	onrows = gda_data_model_get_n_rows (comp->priv->old_model);
+	nnrows = gda_data_model_get_n_rows (comp->priv->new_model);
+	rows_to_del = g_new (gboolean, onrows);
+	memset (rows_to_del, TRUE, sizeof (gboolean) * onrows);
+	for (i = 0; i < nnrows; i++) {
+		gint erow = -1;
+		gboolean has_changed = FALSE;
+		GdaDiff *diff = NULL;
+		gboolean stop;
+		
+		erow = find_row_in_model (comp, i,  &has_changed, error);
+		
+#ifdef DEBUG_STORE_MODIFY
+		g_print ("FIND row %d(/%d) returned row %d (%s)\n", i, new_n_rows - 1, erow, 
+			 has_changed ? "CHANGED" : "unchanged");
+#endif
+		if (erow < 0) {
+			gint j;
+			diff = g_new0 (GdaDiff, 1);
+			diff->type = GDA_DIFF_ADD_ROW;
+			diff->old_row = -1;
+			diff->new_row = i;
+			diff->values = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free,
+							      (GDestroyNotify) gda_value_free);
+			for (j = 0; j < oncols; j++) 
+				g_hash_table_insert (diff->values, g_strdup_printf ("+%d", j),
+						     gda_value_copy (gda_data_model_get_value_at (comp->priv->new_model,
+												  j, i)));
+		}
+		else if (has_changed) {
+			gint j;
+			diff = g_new0 (GdaDiff, 1);
+			diff->type = GDA_DIFF_MODIFY_ROW;
+			diff->old_row = erow;
+			diff->new_row = i;
+			diff->values = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free,
+							      (GDestroyNotify) gda_value_free);
+			for (j = 0; j < oncols; j++) {
+				g_hash_table_insert (diff->values, g_strdup_printf ("+%d", j),
+						     gda_value_copy (gda_data_model_get_value_at (comp->priv->new_model,
+												  j, i)));
+				g_hash_table_insert (diff->values, g_strdup_printf ("-%d", j),
+						     gda_value_copy (gda_data_model_get_value_at (comp->priv->old_model,
+												  j, i)));
+			}
+		}
+		rows_to_del [i] = FALSE;
+		if (diff) {
+			g_array_append_val (comp->priv->diffs, diff);
+			g_signal_emit (comp, gda_data_comparator_signals [DIFF_COMPUTED], 0, diff, &stop);
+			if (stop) {
+				g_set_error (error, GDA_DATA_COMPARATOR_ERROR,
+					     GDA_DATA_COMPARATOR_USER_CANCELLED_ERROR,
+					     _("Differences computation cancelled on signal handling"));
+				g_free (rows_to_del);
+				return FALSE;
+			}
+		}
+	}
+
+	/* actual differences computations : rows to delete */
+	for (i = 0; i < onrows; i++) {
+		GdaDiff *diff = NULL;
+		gboolean stop;
+		if (rows_to_del [i]) {
+			gint j;
+			diff = g_new0 (GdaDiff, 1);
+			diff->type = GDA_DIFF_ADD_ROW;
+			diff->old_row = i;
+			diff->new_row = -1;
+			diff->values = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free,
+							      (GDestroyNotify) gda_value_free);
+			for (j = 0; j < oncols; j++) 
+				g_hash_table_insert (diff->values, g_strdup_printf ("-%d", j),
+						     gda_value_copy (gda_data_model_get_value_at (comp->priv->old_model,
+												  j, i)));
+			g_array_append_val (comp->priv->diffs, diff);
+			g_signal_emit (comp, gda_data_comparator_signals [DIFF_COMPUTED], 0, diff, &stop);
+			if (stop) {
+				g_set_error (error, GDA_DATA_COMPARATOR_ERROR,
+					     GDA_DATA_COMPARATOR_USER_CANCELLED_ERROR,
+					     _("Differences computation cancelled on signal handling"));
+				g_free (rows_to_del);
+				return FALSE;
+			}
+		}
+	}
+
+	g_free (rows_to_del);
+	return TRUE;
+}
+
+/**
+ * gda_data_comparator_get_n_diffs
+ * @comp: a #GdaDataComparator object
+ *
+ * Get the number of differences as computed by the last time gda_data_comparator_compute_diff() was called.
+ *
+ * Returns: the number of computed differences
+ */
+gint
+gda_data_comparator_get_n_diffs  (GdaDataComparator *comp)
+{
+	g_return_val_if_fail (GDA_IS_DATA_COMPARATOR (comp), 0);
+	g_return_val_if_fail (comp->priv, 0);
+
+	return comp->priv->diffs->len;
+}
+
+/**
+ * gda_data_comparator_get_diff
+ * @comp: a #GdaDataComparator object
+ * @pos: the requested difference number (starting at 0)
+ *
+ * Get a pointer to the #GdaDiff structure representing the difference which number is @pos
+ *
+ * Returns: a pointer to a #GdaDiff, or %NULL if @pos is invalid
+ */
+const GdaDiff *
+gda_data_comparator_get_diff (GdaDataComparator *comp, gint pos)
+{
+	g_return_val_if_fail (GDA_IS_DATA_COMPARATOR (comp), NULL);
+	g_return_val_if_fail (comp->priv, NULL);
+
+	return g_array_index (comp->priv->diffs, GdaDiff*, pos);
+}

Added: trunk/libgda/gda-data-comparator.h
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-data-comparator.h	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,88 @@
+/* gda-data-comparator.h
+ *
+ * Copyright (C) 2008 Vivien Malerba
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GDA_DATA_COMPARATOR_H_
+#define __GDA_DATA_COMPARATOR_H_
+
+#include "gda-decl.h"
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_DATA_COMPARATOR          (gda_data_comparator_get_type())
+#define GDA_DATA_COMPARATOR(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, gda_data_comparator_get_type(), GdaDataComparator)
+#define GDA_DATA_COMPARATOR_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, gda_data_comparator_get_type (), GdaDataComparatorClass)
+#define GDA_IS_DATA_COMPARATOR(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, gda_data_comparator_get_type ())
+
+typedef struct _GdaDataComparator GdaDataComparator;
+typedef struct _GdaDataComparatorClass GdaDataComparatorClass;
+typedef struct _GdaDataComparatorPrivate GdaDataComparatorPrivate;
+
+/* error reporting */
+extern GQuark gda_data_comparator_error_quark (void);
+#define GDA_DATA_COMPARATOR_ERROR gda_data_comparator_error_quark ()
+
+typedef enum {
+	GDA_DATA_COMPARATOR_MISSING_DATA_MODEL_ERROR,
+	GDA_DATA_COMPARATOR_COLUMN_TYPES_MISMATCH_ERROR,
+	GDA_DATA_COMPARATOR_MODEL_ACCESS_ERROR,
+	GDA_DATA_COMPARATOR_USER_CANCELLED_ERROR
+} GdaDataComparatorError;
+
+/* differences reporting */
+typedef enum {
+	GDA_DIFF_ADD_ROW,
+	GDA_DIFF_REMOVE_ROW,
+	GDA_DIFF_MODIFY_ROW,
+} GdaDiffType;
+
+typedef struct {
+	GdaDiffType  type;
+	gint         old_row;
+	gint         new_row;
+	GHashTable  *values; /* key = ('+' or '-') and a column position starting at 0 (string)
+			      * value = a GValue pointer */
+} GdaDiff;
+
+/* struct for the object's data */
+struct _GdaDataComparator
+{
+	GObject                   object;
+	GdaDataComparatorPrivate *priv;
+};
+
+/* struct for the object's class */
+struct _GdaDataComparatorClass
+{
+	GObjectClass              parent_class;
+	gboolean               (* diff_computed)  (GdaDataComparator *comp, GdaDiff *diff);
+};
+
+GType             gda_data_comparator_get_type        (void) G_GNUC_CONST;
+GObject          *gda_data_comparator_new             (GdaDataModel *old_model, GdaDataModel *new_model);
+void              gda_data_comparator_set_key_columns (GdaDataComparator *comp, gint *col_numbers, gint nb_cols);
+gboolean          gda_data_comparator_compute_diff    (GdaDataComparator *comp, GError **error);
+gint              gda_data_comparator_get_n_diffs     (GdaDataComparator *comp);
+const GdaDiff    *gda_data_comparator_get_diff        (GdaDataComparator *comp, gint pos);
+
+G_END_DECLS
+
+#endif

Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c	(original)
+++ trunk/libgda/gda-data-model.c	Wed Apr  2 20:22:59 2008
@@ -1282,12 +1282,10 @@
 		}
 	}
 	
-	if (! gda_file_save (file, body, strlen (body))) {
-		g_set_error (error, 0, 0, _("Could not save file %s"), file);
+	if (! g_file_set_contents (file, body, -1, error)) {
 		g_free (body);
 		return FALSE;
 	}
-
 	g_free (body);
 	return TRUE;
 }

Modified: trunk/libgda/gda-easy.c
==============================================================================
--- trunk/libgda/gda-easy.c	(original)
+++ trunk/libgda/gda-easy.c	Wed Apr  2 20:22:59 2008
@@ -25,6 +25,15 @@
 
 static GdaSqlParser *internal_parser = NULL;
 
+/* module error */
+GQuark gda_easy_error_quark (void)
+{
+	static GQuark quark;
+	if (!quark)
+		quark = g_quark_from_static_string ("gda_easy_error");
+	return quark;
+}
+
 /**
  * gda_prepare_create_database
  * @provider: the database provider to use
@@ -228,128 +237,142 @@
 }
 
 /**
- * gda_create_table
+ * gda_prepare_create_table
  * @cnc: an opened connection
  * @table_name:
  * @num_columns
  * @error: a place to store errors, or %NULL
- * @...: pairs of column name and #GType, finish with NULL
+ * @...: group of three arguments for column's name, column's #GType 
+ * and a #GdaEasyCreateTableFlag flag, finished with NULL
  * 
- * Create a Table over an opened connection using a pair list of colum name and 
- * GType as arguments, you need to finish the list using NULL.
- *
- * This is just a convenient function to create tables quickly, 
- * using defaults for the provider and converting the #GType passed to the corresponding 
- * type in the provider; to use a custom type or more advanced characteristics in a 
- * specific provider use the #GdaServerOperation framework.
- *
- * Returns: TRUE if the table was created; FALSE and set @error otherwise
+ * Create a #GdaServerOperation object using an opened connection, taking three 
+ * arguments, a colum's name the column's GType and #GdaEasyCreateTableFlag 
+ * flag, you need to finish the list using NULL.
+ *
+ * You'll be able to modify the #GdaServerOperation object to add custom options
+ * to the operation. When finish call #gda_perform_create_table 
+ * or #gda_server_operation_perform_operation
+ * in order to execute the operation.
+ * 
+ * Returns: a #GdaServerOperation if no errors; NULL and set @error otherwise
  */
-gboolean
-gda_create_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...)
+GdaServerOperation*
+gda_prepare_create_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...)
 {
 	GdaServerOperation *op;
 	GdaServerProvider *server;
-	
-	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
-	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
+		
+	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);	
 	
 	server = gda_connection_get_provider_obj(cnc);
 	
-	op = gda_server_provider_create_operation (server, cnc, GDA_SERVER_OPERATION_CREATE_TABLE, NULL, error);
-	if (GDA_IS_SERVER_OPERATION (op)) {
+	if (!table_name) {
+		g_set_error (error, GDA_EASY_ERROR, GDA_EASY_OBJECT_NAME_ERROR, 
+			     _("Unspecified table name"));
+		return NULL;
+	}
+	
+	if (gda_server_provider_supports_operation (server, cnc, GDA_SERVER_OPERATION_CREATE_TABLE, NULL)) {	
 		va_list  args;
 		gchar   *arg;
 		GType    type;
 		gchar   *dbms_type;
-		xmlDocPtr parameters;
-		xmlNodePtr root;
-		xmlNodePtr table, op_data, array_data, array_row, array_value;
-		
-		if (table_name == NULL) {
-			g_set_error (error, GDA_GENERAL_ERROR, GDA_GENERAL_OBJECT_NAME_ERROR, 
-				     _("Couldn't create table with a NULL string"));
-			return FALSE;    
-		}
-		
+		GdaEasyCreateTableFlag flag;
+		gint i;
 	
-		/* Initation of the xmlDoc */
-		parameters = xmlNewDoc ((xmlChar*)"1.0");
-		
-		root = xmlNewDocNode (parameters, NULL, (xmlChar*)"serv_op_data", NULL);
-		xmlDocSetRootElement (parameters, root);
-		table = xmlNewChild (root, NULL, (xmlChar*)"op_data", (xmlChar*)table_name);
-		xmlSetProp(table, (xmlChar*)"path", (xmlChar*)"/TABLE_DEF_P/TABLE_NAME");
-
-		op_data = xmlNewChild (root, NULL, (xmlChar*)"op_data", NULL);
-		xmlSetProp(op_data, (xmlChar*)"path", (xmlChar*)"/FIELDS_A");
-		array_data = xmlNewChild (op_data, NULL, (xmlChar*)"gda_array_data", NULL);
-			
+		op = gda_server_provider_create_operation (server, cnc, 
+							   GDA_SERVER_OPERATION_CREATE_TABLE, NULL, error);
+		gda_server_operation_set_value_at (op, table_name, error, "/TABLE_DEF_P/TABLE_NAME");
+				
 		va_start (args, error);
 		type = 0;
 		arg = NULL;
+		i = 0;
 		
 		while ((arg = va_arg (args, gchar*))) {
+			/* First argument for Column's name */			
+			gda_server_operation_set_value_at (op, arg, error, "/FIELDS_A/@COLUMN_NAME/%d", i);
+		
+			/* Second to Define column's type */
 			type = va_arg (args, GType);
 			if (type == 0) {
-				g_set_error(error, GDA_GENERAL_ERROR, GDA_GENERAL_INCORRECT_VALUE_ERROR, 
-					    _("Error the number of arguments are incorrect; couldn't create the table"));
+				g_set_error (error, GDA_EASY_ERROR, GDA_EASY_INCORRECT_VALUE_ERROR, 
+					     _("Invalid type"));
 				g_object_unref (op);
-				xmlFreeDoc(parameters);
-				return FALSE;
+				return NULL;
 			}
-			
 			dbms_type = (gchar *) gda_server_provider_get_default_dbms_type (server, 
 											 cnc, type);
-			array_row = xmlNewChild (array_data, NULL, (xmlChar*)"gda_array_row", NULL);
-			array_value = xmlNewChild (array_row, NULL, (xmlChar*)"gda_array_value", (xmlChar*)arg);
-			xmlSetProp(array_value, (xmlChar*)"colid", (xmlChar*)"COLUMN_NAME");
+			gda_server_operation_set_value_at (op, dbms_type, error, "/FIELDS_A/@COLUMN_TYPE/%d", i);
 		
-			array_value = xmlNewChild(array_row, NULL, (xmlChar*)"gda_array_value", (xmlChar*)dbms_type);
-			xmlSetProp(array_value, (xmlChar*)"colid", (xmlChar*)"COLUMN_TYPE");
-			
+			/* Third for column's flags */
+			flag = va_arg (args, GdaEasyCreateTableFlag);
+			if (flag & GDA_EASY_CREATE_TABLE_PKEY_FLAG)
+				gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_PKEY/%d", i);
+			if (flag & GDA_EASY_CREATE_TABLE_NOT_NULL_FLAG)
+				gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_NNUL/%d", i);
+			if (flag & GDA_EASY_CREATE_TABLE_AUTOINC_FLAG)
+				gda_server_operation_set_value_at (op, "TRUE", error, "/FIELDS_A/@COLUMN_AUTOINC/%d", i);
+			i++;
 		}
+	
+		va_end (args);
 		
-		va_end(args);
-		
-		if (!gda_server_operation_load_data_from_xml (op, root, error)) {
-			g_set_error (error, GDA_GENERAL_ERROR, GDA_GENERAL_OPERATION_ERROR, 
-				     _("The XML operation doesn't exist or could't be loaded"));
-			g_object_unref (op);
-			xmlFreeDoc(parameters);
-			return FALSE;
-		}
-		else {
-			if (!gda_server_provider_perform_operation (server, cnc, op, error)) {
-				g_object_unref (op);
-				xmlFreeDoc(parameters);
-				return FALSE;
-			}
-		}
+		g_object_set_data (G_OBJECT (op), "_gda_connection", cnc);
+		g_object_set_data (G_OBJECT (op), "_gda_provider_obj", server);
 		
-		g_object_unref (op);
-		xmlFreeDoc(parameters);
+		return op;		
 	}
+	else {
+		g_set_error (error, GDA_EASY_ERROR, GDA_EASY_OBJECT_NAME_ERROR, 
+			     _("CREATE TABLE operation is not supported by the database server"));
+		return NULL;
+	}
+}
+
+/**
+ * gda_perform_create_table
+ * @op: a valid #GdaServerOperation
+ * @error: a place to store errors, or %NULL
+ * 
+ * Performs a prepared #GdaServerOperation to create a table. This could perform
+ * an operation created by #gda_prepare_create_table or any other using the
+ * the #GdaServerOperation API.
+ *
+ * Returns: TRUE if the table was created; FALSE and set @error otherwise
+ */
+gboolean
+gda_perform_create_table (GdaServerOperation *op, GError **error)
+{
+	GdaConnection *cnc;
+	GdaServerProvider *server;
 	
-	return TRUE;
+	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);	
+	g_return_val_if_fail (gda_server_operation_get_op_type (op) ==
+						  GDA_SERVER_OPERATION_CREATE_TABLE, FALSE);
+	
+	server = GDA_SERVER_PROVIDER (g_object_get_data (G_OBJECT (op), "_gda_provider_obj"));
+	cnc = GDA_CONNECTION (g_object_get_data (G_OBJECT (op), "_gda_connection"));
+	
+	return gda_server_provider_perform_operation (server, cnc, op, error);
 }
 
 /**
- * gda_drop_table
+ * gda_prepare_drop_table
  * @cnc: an opened connection
  * @table_name:
  * @error: a place to store errors, or %NULL
  * 
- * This is just a convenient function to drop a table in an opened connection.
+ * This is just a convenient function to create a #GdaServerOperation to drop a 
+ * table in an opened connection.
  *
- * Returns: TRUE if the table was dropped
+ * Returns: a new #GdaServerOperation or NULL if couldn't create the opereration.
  */
-gboolean
-gda_drop_table (GdaConnection *cnc, const gchar *table_name, GError **error)
+GdaServerOperation*
+gda_prepare_drop_table (GdaConnection *cnc, const gchar *table_name, GError **error)
 {
 	GdaServerOperation *op;
 	GdaServerProvider *server;
-	gboolean retval = TRUE;
 
 	server = gda_connection_get_provider_obj (cnc);
 	
@@ -357,37 +380,46 @@
 						   GDA_SERVER_OPERATION_DROP_TABLE, NULL, error);
 	
 	if (GDA_IS_SERVER_OPERATION (op)) {
-		xmlDocPtr parameters;
-		xmlNodePtr root;
-		xmlNodePtr table;
-		
 		g_return_val_if_fail (table_name != NULL 
 				      || GDA_IS_CONNECTION (cnc) 
-				      || !gda_connection_is_opened (cnc), FALSE);
-    
-		parameters = xmlNewDoc ((xmlChar*)"1.0");
-		root = xmlNewDocNode (parameters, NULL, (xmlChar*)"serv_op_data", NULL);
-		xmlDocSetRootElement (parameters, root);
-		table = xmlNewChild (root, NULL, (xmlChar*)"op_data", (xmlChar*)table_name);
-		xmlSetProp(table, (xmlChar*)"path", (xmlChar*)"/TABLE_DESC_P/TABLE_NAME");
-	    
-		if (!gda_server_operation_load_data_from_xml (op, root, error)) {
-			/* error */
-			retval = FALSE;
-		}
-		else if (!gda_server_provider_perform_operation (server, cnc, op, error)) {
-			/* error */
-			retval = FALSE;
+				      || !gda_connection_is_opened (cnc), NULL);
+		
+		if (gda_server_operation_set_value_at (op, table_name, 
+						       error, "/TABLE_DESC_P/TABLE_NAME")) {
+			g_object_set_data (G_OBJECT (op), "_gda_connection", cnc);
+			g_object_set_data (G_OBJECT (op), "_gda_provider_obj", server);
+			return op;
 		}
-		g_object_unref (op);
-		xmlFreeDoc(parameters);
+		else 
+			return NULL;
 	}
-	else {
-		g_message("The Server doesn't support the DROP TABLE operation!\n\n");
-		retval = FALSE;
-	}
-    
-	return retval;
+	else
+		return NULL;
+}
+
+/**
+ * gda_perform_drop_table
+ * @op: a #GdaServerOperation object
+ * @error: a place to store errors, or %NULL
+ * 
+ * This is just a convenient function to perform a drop a table operation.
+ *
+ * Returns: TRUE if the table was dropped
+ */
+gboolean
+gda_perform_drop_table (GdaServerOperation *op, GError **error)
+{
+	GdaConnection *cnc;
+	GdaServerProvider *server;
+
+	g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
+	g_return_val_if_fail (gda_server_operation_get_op_type (op) ==
+			      GDA_SERVER_OPERATION_DROP_TABLE, FALSE);
+	
+	server = GDA_SERVER_PROVIDER (g_object_get_data (G_OBJECT (op), "_gda_provider_obj"));
+	cnc = GDA_CONNECTION (g_object_get_data (G_OBJECT (op), "_gda_connection"));
+	
+	return gda_server_provider_perform_operation (server, cnc, op, error);
 }
 
 static guint
@@ -477,7 +509,7 @@
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
 	g_return_val_if_fail (table_name && *table_name, FALSE);
-
+	
 	TO_IMPLEMENT;
 	return FALSE;
 }

Modified: trunk/libgda/gda-easy.h
==============================================================================
--- trunk/libgda/gda-easy.h	(original)
+++ trunk/libgda/gda-easy.h	Wed Apr  2 20:22:59 2008
@@ -3,6 +3,7 @@
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
+ *      Daniel Espinosa <esodan gmail com>
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -34,14 +35,24 @@
 
 G_BEGIN_DECLS
 
-extern GQuark gda_general_error_quark (void);
-#define GDA_GENERAL_ERROR gda_general_error_quark ()
+extern GQuark gda_easy_error_quark (void);
+#define GDA_EASY_ERROR gda_easy_error_quark ()
 
 typedef enum  {
-    GDA_GENERAL_OBJECT_NAME_ERROR,
-    GDA_GENERAL_INCORRECT_VALUE_ERROR,
-    GDA_GENERAL_OPERATION_ERROR
-} GdaGeneralError;
+    GDA_EASY_OBJECT_NAME_ERROR,
+    GDA_EASY_INCORRECT_VALUE_ERROR,
+    GDA_EASY_OPERATION_ERROR
+} GdaEasyError;
+
+typedef enum
+{
+	GDA_EASY_CREATE_TABLE_PKEY_FLAG      = 1 << 0,
+	GDA_EASY_CREATE_TABLE_NOT_NULL_FLAG  = 1 << 1,
+	GDA_EASY_CREATE_TABLE_UNIQUE_FLAG    = 1 << 2,
+	GDA_EASY_CREATE_TABLE_AUTOINC_FLAG   = 1 << 3,
+	/* Flags combinations */
+	GDA_EASY_CREATE_TABLE_PKEY_AUTOINC_FLAG = GDA_EASY_CREATE_TABLE_PKEY_FLAG | GDA_EASY_CREATE_TABLE_AUTOINC_FLAG
+} GdaEasyCreateTableFlag;
 
 /* 
  * Convenient Functions
@@ -66,8 +77,10 @@
 /*
  * Tables creation and destruction
  */
-gboolean            gda_create_table                  (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
-gboolean            gda_drop_table                    (GdaConnection *cnc, const gchar *table_name, GError **error);
+GdaServerOperation *gda_prepare_create_table	       (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
+gboolean            gda_perform_create_table          (GdaServerOperation *op, GError **error);
+GdaServerOperation *gda_prepare_drop_table            (GdaConnection *cnc, const gchar *table_name, GError **error);
+gboolean            gda_perform_drop_table            (GdaServerOperation *op, GError **error);
 
 /*
  * Data in tables manipulation

Modified: trunk/libgda/gda-init.c
==============================================================================
--- trunk/libgda/gda-init.c	(original)
+++ trunk/libgda/gda-init.c	Wed Apr  2 20:22:59 2008
@@ -236,16 +236,3 @@
 	GDA_UNLOCK ();
 }
 
-/*
- * Convenient Functions
- */
-
-
-/* module error */
-GQuark gda_general_error_quark (void)
-{
-	static GQuark quark;
-	if (!quark)
-		quark = g_quark_from_static_string ("gda_general_error");
-	return quark;
-}

Modified: trunk/libgda/gda-marshal.list
==============================================================================
--- trunk/libgda/gda-marshal.list	(original)
+++ trunk/libgda/gda-marshal.list	Wed Apr  2 20:22:59 2008
@@ -41,3 +41,4 @@
 BOOLEAN:INT
 BOOLEAN:INT,INT
 POINTER:POINTER
+BOOLEAN:POINTER

Modified: trunk/libgda/gda-meta-store.c
==============================================================================
--- trunk/libgda/gda-meta-store.c	(original)
+++ trunk/libgda/gda-meta-store.c	Wed Apr  2 20:22:59 2008
@@ -2419,7 +2419,8 @@
  */
 static gint
 find_row_in_model (GdaDataModel *find_in, GdaDataModel *data, gint row, gint *pk_cols, gint pk_cols_nb, 
-		   gboolean *out_has_changed, GError **error) {
+		   gboolean *out_has_changed, GError **error)
+{
 	gint i, erow;
 	GSList *values = NULL;
 	

Modified: trunk/libgda/gda-meta-struct.c
==============================================================================
--- trunk/libgda/gda-meta-struct.c	(original)
+++ trunk/libgda/gda-meta-struct.c	Wed Apr  2 20:22:59 2008
@@ -123,8 +123,11 @@
 
 /**
  * gda_meta_struct_new
- * @features: the kind of information the new #GdaMetaStruct object will compute (the more features, the more time
- * it takes to run)
+ * @features: the kind of extra information the new #GdaMetaStruct object will compute
+ *
+ * Creates a new #GdaMetaStruct object. The @features specifies the extra features which will also be computed:
+ * the more features, the more time it takes to run. Features such as table's columns, each column's attributes, etc
+ * are not optional and will always be computed.
  *
  * Returns: the newly created #GdaMetaStruct object
  */
@@ -494,12 +497,13 @@
 			goto onerror;
 
 		nrows = gda_data_model_get_n_rows (model);
-		if (0 && nrows >= 1) {
+		if (nrows >= 1) {
 			GdaDataModel *pkmodel;
-			sql = "SELECT column_name FROM _key_column_usage WHERE constraint_catalog = ##cc::string AND constraint_schema = ##cs::string AND constraint_name = ##cname::string ORDER BY ordinal_position";
+			sql = "SELECT column_name FROM _key_column_usage WHERE table_catalog = ##cc::string AND table_schema = ##cs::string AND table_name = ##tname::string AND constraint_name = ##cname::string ORDER BY ordinal_position";
 			pkmodel = gda_meta_store_extract (store, sql, error, 
-							  "cc", gda_data_model_get_value_at (model, 0, 0),
-							  "cs", gda_data_model_get_value_at (model, 1, 0),
+							  "cc", catalog,
+							  "cs", schema,
+							  "tname", name,
 							  "cname", gda_data_model_get_value_at (model, 0, 0), NULL);
 			if (!pkmodel) {
 				g_object_unref (model);
@@ -511,11 +515,11 @@
 			for (i = 0; i < nrows; i++) {
 				GdaMetaTableColumn *tcol;
 				tcol = gda_meta_struct_get_table_column (mstruct, mt, 
-									 gda_data_model_get_value_at (model, 0, i));
+									 gda_data_model_get_value_at (pkmodel, 0, i));
 				if (!tcol) {
 					mt->pk_cols_array [i] = -1;
 					g_warning (_("Internal GdaMetaStore error: column %s not found"),
-						   g_value_get_string (gda_data_model_get_value_at (model, 0, i)));
+						   g_value_get_string (gda_data_model_get_value_at (pkmodel, 0, i)));
 				}
 				else {
 					mt->pk_cols_array [i] = g_slist_index (mt->columns, tcol);

Modified: trunk/libgda/gda-server-provider.c
==============================================================================
--- trunk/libgda/gda-server-provider.c	(original)
+++ trunk/libgda/gda-server-provider.c	Wed Apr  2 20:22:59 2008
@@ -404,8 +404,8 @@
  * Creates a new #GdaServerOperation object which can be modified in order to perform the @type type of
  * action. The @options can contain:
  * <itemizedlist>
- *  <listitem>parameters which ID is a path in the resulting GdaServerOperation object, to initialize some value</listitem>
- *  <listitem>parameters which may change the contents of the GdaServerOperation, see <link linkend="gda-server-op-information-std">this section</link> for more information</listitem>
+ *  <listitem>named values which ID is a path in the resulting GdaServerOperation object, to initialize some value</listitem>
+ *  <listitem>named values which may change the contents of the GdaServerOperation, see <link linkend="gda-server-op-information-std">this section</link> for more information</listitem>
  * </itemizedlist>
  *
  * Returns: a new #GdaServerOperation object, or %NULL in the provider does not support the @type type
@@ -524,7 +524,8 @@
  * @op: a #GdaServerOperation object
  * @error: a place to store an error, or %NULL
  *
- * Performs the operation described by @op.
+ * Performs the operation described by @op. Note that @op is not destroyed by this method
+ * and can be reused.
  *
  * Returns: TRUE if no error occurred
  */

Modified: trunk/libgda/gda-server-provider.h
==============================================================================
--- trunk/libgda/gda-server-provider.h	(original)
+++ trunk/libgda/gda-server-provider.h	Wed Apr  2 20:22:59 2008
@@ -28,7 +28,6 @@
 #include <libgda/gda-server-operation.h>
 #include <libgda/gda-connection.h>
 #include <libgda/gda-data-model.h>
-#include <libgda/gda-data-model-index.h>
 #include <libgda/gda-quark-list.h>
 #include <libgda/gda-statement.h>
 #include <libgda/gda-meta-store.h>

Modified: trunk/libgda/gda-statement.c
==============================================================================
--- trunk/libgda/gda-statement.c	(original)
+++ trunk/libgda/gda-statement.c	Wed Apr  2 20:22:59 2008
@@ -364,7 +364,7 @@
 }
 
 /**
- * gda_statement_check_connection
+ * gda_statement_check_validity
  * @stmt: a #GdaStatement object
  * @cnc: a #GdaConnection object, or %NULL
  * @error: a place to store errors, or %NULL
@@ -374,17 +374,19 @@
  *
  * If @cnc is %NULL, then cleans anything related to @cnc in @stmt.
  *
+ * See gda_sql_statement_check_validity() for more information.
+ *
  * Returns: TRUE if every object actually exists in @cnc's database
  */
 gboolean
-gda_statement_check_connection (GdaStatement *stmt, GdaConnection *cnc, GError **error)
+gda_statement_check_validity (GdaStatement *stmt, GdaConnection *cnc, GError **error)
 {
 	gboolean retval;
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 	g_return_val_if_fail (stmt->priv, FALSE);
 	g_return_val_if_fail (!cnc || GDA_IS_CONNECTION (cnc), FALSE);
 
-	retval = gda_sql_statement_check_connection (stmt->priv->internal_struct, cnc, error);
+	retval = gda_sql_statement_check_validity (stmt->priv->internal_struct, cnc, error);
 	g_signal_emit (stmt, gda_statement_signals [CHECKED], 0, cnc, retval);
 
 	return retval;

Modified: trunk/libgda/gda-statement.h
==============================================================================
--- trunk/libgda/gda-statement.h	(original)
+++ trunk/libgda/gda-statement.h	Wed Apr  2 20:22:59 2008
@@ -96,13 +96,7 @@
 GdaSqlStatementType gda_statement_get_statement_type     (GdaStatement *stmt);
 gboolean            gda_statement_is_useless             (GdaStatement *stmt);
 gboolean            gda_statement_check_structure        (GdaStatement *stmt, GError **error);
-gboolean            gda_statement_check_connection       (GdaStatement *stmt, GdaConnection *cnc, GError **error);
-
-/*
-GdaObject         *gda_statement_execute              (GdaStatement *stmt, GdaConnection  *cnc, 
-						       GdaParameterList *params,
-						       GdaStatementModelUsage usage, GError **error);
-*/
+gboolean            gda_statement_check_validity         (GdaStatement *stmt, GdaConnection *cnc, GError **error);
 
 G_END_DECLS
 

Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c	(original)
+++ trunk/libgda/gda-util.c	Wed Apr  2 20:22:59 2008
@@ -38,6 +38,7 @@
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
+#include <libgda/sql-parser/gda-sql-statement.h>
 
 #include <libgda/binreloc/gda-binreloc.h>
 
@@ -201,373 +202,6 @@
 	return retval;	
 }
 
-/* function called by g_hash_table_foreach to add items to a GList */
-static void
-add_string_key_to_list (gpointer key, gpointer value, gpointer user_data)
-{
-        GList **list = (GList **) user_data;
-
-        *list = g_list_append (*list, g_strdup (key));
-}
-
-/**
- * gda_string_hash_to_list
- * @hash_table: a hash table.
- *
- * Creates a new list of strings, which contains all keys of a given hash 
- * table. After using it, you should free this list by calling g_list_free.
- * 
- * Returns: a new GList.
- */
-GList *
-gda_string_hash_to_list (GHashTable *hash_table)
-{
-	GList *list = NULL;
-
-        g_return_val_if_fail (hash_table != NULL, NULL);
-
-        g_hash_table_foreach (hash_table, (GHFunc) add_string_key_to_list, &list);
-        return list;
-}
-
-/**
- * gda_file_load
- * @filename: path for the file to be loaded.
- *
- * Loads a file, specified by the given @uri, and returns the file
- * contents as a string.
- *
- * It is the caller's responsibility to free the returned value.
- *
- * Returns: the file contents as a newly-allocated string, or %NULL
- * if there is an error.
- */
-gchar *
-gda_file_load (const gchar *filename)
-{
-	gchar *retval = NULL;
-	gsize length = 0;
-	GError *error = NULL;
-
-	g_return_val_if_fail (filename != NULL, NULL);
-
-	if (g_file_get_contents (filename, &retval, &length, &error))
-		return retval;
-
-	gda_log_error (_("Error while reading %s: %s"), filename, error->message);
-	g_error_free (error);
-
-	return NULL;
-}
-
-/**
- * gda_file_save
- * @filename: path for the file to be saved.
- * @buffer: contents of the file.
- * @len: size of @buffer.
- *
- * Saves a chunk of data into a file.
- *
- * Returns: %TRUE if successful, %FALSE on error.
- */
-gboolean
-gda_file_save (const gchar *filename, const gchar *buffer, gint len)
-{
-	gint fd;
-	gint res;
-	
-	g_return_val_if_fail (filename != NULL, FALSE);
-
-	fd = open (filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
-	if (fd == -1) {
-		gda_log_error (_("Could not create file %s"), filename);
-		return FALSE;
-	}
-
-	res = write (fd, (const void *) buffer, len);
-	close (fd);
-
-	return res == -1 ? FALSE : TRUE;
-}
-
-/**
- * gda_utility_build_encoded_id
- *
- * Creates a BASE64 kind encoded string. It's not really a BASE64 because:
- * - the characters + and / of BASE64 are replaced with - and _
- * - no padding is done using the = character
- *
- * The created string is a valid NCName XML token.
- */
-static inline unsigned char
-to_uchar (gchar ch)
-{
-	return ch;
-}
-
-gchar *
-gda_utility_build_encoded_id (const gchar *prefix, const gchar *id)
-{
-	const gchar conv[64] = 
-		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-	gchar *str, *out;
-	const gchar *in;
-	int ln = 0, pln = 0;
-	int offset;
-
-	/* size computation */
-	if (prefix) {
-		pln = strlen (prefix);
-		ln = pln;
-	}
-
-	ln += (strlen (id) * 4 + 2) / 3 + 1;
-	str = g_new0 (char, ln);
-
-	/* copy prefix */
-	out = str;
-	if (prefix) {
-		strcpy (str, prefix);
-		out += pln;
-	}
-
-	/* create data */
-	offset = 4;
-	for (in = id; offset == 4; in += 3) {
-		offset = 0;
-		
-		if (in[0]) {
-			offset = 2;
-			
-			out[0] = conv [to_uchar (in[0]) >> 2];
-
-			if (in[1]) {
-				offset = 3;
-				out[1] = conv [((to_uchar (in[0]) << 4) + 
-						(to_uchar (in[1]) >> 4)) & 0x3f];
-
-				if (in[2]) {
-					offset = 4;
-
-					out[2] = conv [((to_uchar (in[1]) << 2) + 
-							(to_uchar (in[2]) >> 6)) & 0x3f];
-					out[3] = conv [to_uchar (in[2]) & 0x3f];
-				}
-				else
-					out[2] = conv [(to_uchar (in[1]) << 2) & 0x3f];
-			}
-			else
-				out[1] = conv [(to_uchar (in[0]) << 4) & 0x3f];
-
-
-			out += offset;
-		}
-        }
-
-	return str;
-}
-
-#define B64(x)	     \
-  ((x) == 'A' ? 0    \
-   : (x) == 'B' ? 1  \
-   : (x) == 'C' ? 2  \
-   : (x) == 'D' ? 3  \
-   : (x) == 'E' ? 4  \
-   : (x) == 'F' ? 5  \
-   : (x) == 'G' ? 6  \
-   : (x) == 'H' ? 7  \
-   : (x) == 'I' ? 8  \
-   : (x) == 'J' ? 9  \
-   : (x) == 'K' ? 10 \
-   : (x) == 'L' ? 11 \
-   : (x) == 'M' ? 12 \
-   : (x) == 'N' ? 13 \
-   : (x) == 'O' ? 14 \
-   : (x) == 'P' ? 15 \
-   : (x) == 'Q' ? 16 \
-   : (x) == 'R' ? 17 \
-   : (x) == 'S' ? 18 \
-   : (x) == 'T' ? 19 \
-   : (x) == 'U' ? 20 \
-   : (x) == 'V' ? 21 \
-   : (x) == 'W' ? 22 \
-   : (x) == 'X' ? 23 \
-   : (x) == 'Y' ? 24 \
-   : (x) == 'Z' ? 25 \
-   : (x) == 'a' ? 26 \
-   : (x) == 'b' ? 27 \
-   : (x) == 'c' ? 28 \
-   : (x) == 'd' ? 29 \
-   : (x) == 'e' ? 30 \
-   : (x) == 'f' ? 31 \
-   : (x) == 'g' ? 32 \
-   : (x) == 'h' ? 33 \
-   : (x) == 'i' ? 34 \
-   : (x) == 'j' ? 35 \
-   : (x) == 'k' ? 36 \
-   : (x) == 'l' ? 37 \
-   : (x) == 'm' ? 38 \
-   : (x) == 'n' ? 39 \
-   : (x) == 'o' ? 40 \
-   : (x) == 'p' ? 41 \
-   : (x) == 'q' ? 42 \
-   : (x) == 'r' ? 43 \
-   : (x) == 's' ? 44 \
-   : (x) == 't' ? 45 \
-   : (x) == 'u' ? 46 \
-   : (x) == 'v' ? 47 \
-   : (x) == 'w' ? 48 \
-   : (x) == 'x' ? 49 \
-   : (x) == 'y' ? 50 \
-   : (x) == 'z' ? 51 \
-   : (x) == '0' ? 52 \
-   : (x) == '1' ? 53 \
-   : (x) == '2' ? 54 \
-   : (x) == '3' ? 55 \
-   : (x) == '4' ? 56 \
-   : (x) == '5' ? 57 \
-   : (x) == '6' ? 58 \
-   : (x) == '7' ? 59 \
-   : (x) == '8' ? 60 \
-   : (x) == '9' ? 61 \
-   : (x) == '-' ? 62 \
-   : (x) == '_' ? 63 \
-   : -1)
-
-static const signed char b64[0x100] = {
-  B64 (0), B64 (1), B64 (2), B64 (3),
-  B64 (4), B64 (5), B64 (6), B64 (7),
-  B64 (8), B64 (9), B64 (10), B64 (11),
-  B64 (12), B64 (13), B64 (14), B64 (15),
-  B64 (16), B64 (17), B64 (18), B64 (19),
-  B64 (20), B64 (21), B64 (22), B64 (23),
-  B64 (24), B64 (25), B64 (26), B64 (27),
-  B64 (28), B64 (29), B64 (30), B64 (31),
-  B64 (32), B64 (33), B64 (34), B64 (35),
-  B64 (36), B64 (37), B64 (38), B64 (39),
-  B64 (40), B64 (41), B64 (42), B64 (43),
-  B64 (44), B64 (45), B64 (46), B64 (47),
-  B64 (48), B64 (49), B64 (50), B64 (51),
-  B64 (52), B64 (53), B64 (54), B64 (55),
-  B64 (56), B64 (57), B64 (58), B64 (59),
-  B64 (60), B64 (61), B64 (62), B64 (63),
-  B64 (64), B64 (65), B64 (66), B64 (67),
-  B64 (68), B64 (69), B64 (70), B64 (71),
-  B64 (72), B64 (73), B64 (74), B64 (75),
-  B64 (76), B64 (77), B64 (78), B64 (79),
-  B64 (80), B64 (81), B64 (82), B64 (83),
-  B64 (84), B64 (85), B64 (86), B64 (87),
-  B64 (88), B64 (89), B64 (90), B64 (91),
-  B64 (92), B64 (93), B64 (94), B64 (95),
-  B64 (96), B64 (97), B64 (98), B64 (99),
-  B64 (100), B64 (101), B64 (102), B64 (103),
-  B64 (104), B64 (105), B64 (106), B64 (107),
-  B64 (108), B64 (109), B64 (110), B64 (111),
-  B64 (112), B64 (113), B64 (114), B64 (115),
-  B64 (116), B64 (117), B64 (118), B64 (119),
-  B64 (120), B64 (121), B64 (122), B64 (123),
-  B64 (124), B64 (125), B64 (126), B64 (127),
-  B64 (128), B64 (129), B64 (130), B64 (131),
-  B64 (132), B64 (133), B64 (134), B64 (135),
-  B64 (136), B64 (137), B64 (138), B64 (139),
-  B64 (140), B64 (141), B64 (142), B64 (143),
-  B64 (144), B64 (145), B64 (146), B64 (147),
-  B64 (148), B64 (149), B64 (150), B64 (151),
-  B64 (152), B64 (153), B64 (154), B64 (155),
-  B64 (156), B64 (157), B64 (158), B64 (159),
-  B64 (160), B64 (161), B64 (162), B64 (163),
-  B64 (164), B64 (165), B64 (166), B64 (167),
-  B64 (168), B64 (169), B64 (170), B64 (171),
-  B64 (172), B64 (173), B64 (174), B64 (175),
-  B64 (176), B64 (177), B64 (178), B64 (179),
-  B64 (180), B64 (181), B64 (182), B64 (183),
-  B64 (184), B64 (185), B64 (186), B64 (187),
-  B64 (188), B64 (189), B64 (190), B64 (191),
-  B64 (192), B64 (193), B64 (194), B64 (195),
-  B64 (196), B64 (197), B64 (198), B64 (199),
-  B64 (200), B64 (201), B64 (202), B64 (203),
-  B64 (204), B64 (205), B64 (206), B64 (207),
-  B64 (208), B64 (209), B64 (210), B64 (211),
-  B64 (212), B64 (213), B64 (214), B64 (215),
-  B64 (216), B64 (217), B64 (218), B64 (219),
-  B64 (220), B64 (221), B64 (222), B64 (223),
-  B64 (224), B64 (225), B64 (226), B64 (227),
-  B64 (228), B64 (229), B64 (230), B64 (231),
-  B64 (232), B64 (233), B64 (234), B64 (235),
-  B64 (236), B64 (237), B64 (238), B64 (239),
-  B64 (240), B64 (241), B64 (242), B64 (243),
-  B64 (244), B64 (245), B64 (246), B64 (247),
-  B64 (248), B64 (249), B64 (250), B64 (251),
-  B64 (252), B64 (253), B64 (254), B64 (255)
-};
-
-/* This is the previous version:
- * It caused a warning: "comparison is always true due to limited range of data type"
- *
- * #define isbase64(x) ((to_uchar (x) <= 255) && (0 <= b64[to_uchar (x)]))
- *
- * murrayc
- */
-#define isbase64(x) ((0 <= b64[to_uchar (x)]))
-
-
-/**
- * gda_utility_build_decoded_id
- *
- * Reverse of gda_utility_build_encoded_id()
- */
-gchar *
-gda_utility_build_decoded_id (const gchar *prefix, const gchar *id)
-{
-	gchar *str;
-	gchar *out;
-	const gchar *in;
-	int ln = 0;
-	int offset;
-
-	/* go to the first byte of actual data */
-	in = id;
-	if (prefix) {
-		const gchar *tmp;
-		tmp = prefix;
-		while (*tmp) {
-			in++;
-			tmp++;
-		}
-	}
-
-	ln = (strlen (in) * 3) / 4 + 3;
-	str = g_new0 (char, ln);
-	out = str;
-
-	/* create data */
-	offset = 3;
-	for (; offset == 3; in += 4) {
-		offset = 0;
-		
-		if (isbase64 (in[0]) && isbase64 (in[1])) {
-			out[0] = ((b64 [to_uchar (in[0])] << 2) | 
-				  (b64 [to_uchar (in[1])] >> 4));
-			
-			if (isbase64 (in[2])) {
-				out[1] = (((b64 [to_uchar (in[1])] << 4) & 0xf0) |
-					  (b64 [to_uchar (in[2])] >> 2));
-
-				if (isbase64 (in[3])) {
-					out[2] = (((b64 [to_uchar (in[2])] << 6) & 0xc0) |
-						  b64 [to_uchar (in[3])]);
-					offset = 3;
-				}
-			}
-		}
-
-		out += offset;
-        }
-
-	return str;
-}
-
-
 /**
  * gda_utility_check_data_model
  * @model: a #GdaDataModel object
@@ -972,3 +606,203 @@
 	return text;
 }
 
+/*
+ * Statement computation from meta store 
+ * @select_stmt: must be a SELECT statement (compound statements not handled)
+ */
+gboolean
+gda_compute_dml_statements (GdaConnection *cnc, GdaStatement *select_stmt, gboolean require_pk, 
+			    GdaStatement **insert, GdaStatement **update, GdaStatement **delete, GError **error)
+{
+	GdaSqlStatement *sel_struct;
+	GdaSqlStatementSelect *stsel;
+	GdaStatement *ret_insert = NULL;
+	GdaStatement *ret_update = NULL;
+	GdaStatement *ret_delete = NULL;
+	GdaMetaStruct *mstruct;
+	gboolean retval = TRUE;
+	GdaMetaTable *mtable;
+
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+	g_return_val_if_fail (GDA_IS_STATEMENT (select_stmt), FALSE);
+	g_return_val_if_fail (gda_statement_get_statement_type (select_stmt) == GDA_SQL_STATEMENT_SELECT, FALSE);
+
+	/*g_print ("*** %s\n", gda_statement_serialize (select_stmt));*/
+
+	g_object_get (G_OBJECT (select_stmt), "structure", &sel_struct, NULL);
+	stsel = (GdaSqlStatementSelect*) sel_struct->contents;
+	if (!stsel->from) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+			     _("SELECT statement has no FROM part"));
+		retval = FALSE;
+		goto cleanup;
+	}
+	if (stsel->from->targets && stsel->from->targets->next) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+			     _("SELECT statement involves more than one table or expression"));
+		retval = FALSE;
+		goto cleanup;
+	}
+	GdaSqlSelectTarget *target;
+	target = (GdaSqlSelectTarget*) stsel->from->targets->data;
+	if (!target->table_name) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+			     _("SELECT statement involves more than one table or expression"));
+		retval = FALSE;
+		goto cleanup;
+	}
+	if (!gda_sql_statement_check_validity (sel_struct, cnc, error)) {
+		retval = FALSE;
+		goto cleanup;
+	}
+
+	mstruct = sel_struct->validity_meta_struct;
+	g_assert (mstruct); /* because gda_sql_statement_check_validity() returned TRUE */
+
+	/* check that we want to modify a table */
+	if (target->validity_meta_object->obj_type != GDA_META_DB_TABLE) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+			     _("Can only build modification statement for tables"));
+		retval = FALSE;
+		goto cleanup;
+	}
+
+	/* check that the condition will be buildable */
+	mtable = GDA_META_DB_OBJECT_GET_TABLE (target->validity_meta_object);
+	if ((update || delete) && require_pk) {
+		gint i;
+		if (mtable->pk_cols_nb == 0) {
+			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+			     _("Table does not have any primary key"));
+			retval = FALSE;
+			goto cleanup;
+		}
+		for (i = 0; i < mtable->pk_cols_nb; i++) {
+			TO_IMPLEMENT;
+		}
+	}
+	
+	/* actual statement structure's computation */
+	GdaSqlStatementInsert *ist;
+	GdaSqlStatementUpdate *ust;
+	GdaSqlStatementDelete *dst;	
+        
+	if (insert) {
+		ist = g_new0 (GdaSqlStatementInsert, 1);
+		GDA_SQL_ANY_PART (ist)->type = GDA_SQL_ANY_STMT_INSERT;
+		ist->table = gda_sql_table_new (GDA_SQL_ANY_PART (ist));
+		ist->table->table_name = g_strdup ((gchar *) target->table_name);
+	}
+	
+	if (update) {
+		ust = g_new0 (GdaSqlStatementUpdate, 1);
+		GDA_SQL_ANY_PART (ust)->type = GDA_SQL_ANY_STMT_UPDATE;
+		ust->table = gda_sql_table_new (GDA_SQL_ANY_PART (ust));
+		ust->table->table_name = g_strdup ((gchar *) target->table_name);
+	}
+        
+	if (delete) {
+		dst = g_new0 (GdaSqlStatementDelete, 1);
+		GDA_SQL_ANY_PART (dst)->type = GDA_SQL_ANY_STMT_DELETE;
+		dst->table = gda_sql_table_new (GDA_SQL_ANY_PART (dst));
+		dst->table->table_name = g_strdup ((gchar *) target->table_name);
+	}
+
+	GSList *expr_list;
+	gint colindex;
+	GSList *insert_values_list = NULL;
+	GHashTable *fields_hash; /* key = a table's field's name, value = we don't care */
+	fields_hash = g_hash_table_new (g_str_hash, g_str_equal);
+	for (expr_list = stsel->expr_list, colindex = 0; 
+	     expr_list;
+	     expr_list = expr_list->next, colindex++) {
+		GdaSqlSelectField *selfield = (GdaSqlSelectField *) expr_list->data;
+		if (selfield->expr && selfield->expr->value && 
+		    !strcmp (g_value_get_string (selfield->expr->value), "*")) {
+			TO_IMPLEMENT;
+			continue;
+		}
+		else {
+			if ((selfield->validity_meta_object != target->validity_meta_object) ||
+			    !selfield->validity_meta_table_column)
+				continue;
+		}
+
+		/* field to insert into */
+		if (g_hash_table_lookup (fields_hash, selfield->field_name))
+			continue;
+		g_hash_table_insert (fields_hash, selfield->field_name, GINT_TO_POINTER (1));
+		if (insert) {
+			GdaSqlField *field;
+			field = gda_sql_field_new (GDA_SQL_ANY_PART (ist));
+			field->field_name = g_strdup (selfield->field_name);
+			ist->fields_list = g_slist_append (ist->fields_list, field);
+		}
+		if (update) {
+			GdaSqlField *field;
+			field = gda_sql_field_new (GDA_SQL_ANY_PART (ust));
+			field->field_name = g_strdup (selfield->field_name);
+			ust->fields_list = g_slist_append (ust->fields_list, field);
+		}
+
+		/* parameter for the inserted value */
+		GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
+		GdaSqlExpr *expr;
+		GdaMetaTableColumn *tcol;
+
+		tcol = selfield->validity_meta_table_column;
+		if (insert) {
+			pspec->name = g_strdup_printf ("+%d", colindex);
+			pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
+			pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
+			pspec->nullok = tcol->nullok;
+			expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
+			expr->param_spec = pspec;
+			insert_values_list = g_slist_append (insert_values_list, expr);
+		}
+		if (update) {
+			pspec->name = g_strdup_printf ("+%d", colindex);
+			pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
+			pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
+			pspec->nullok = tcol->nullok;
+			expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ust));
+			expr->param_spec = pspec;
+			ust->expr_list = g_slist_append (ust->expr_list, expr);
+		}
+	}
+	g_hash_table_destroy (fields_hash);
+
+	/* finish the statements */
+	if (insert) {
+		GdaSqlStatement *st;
+		ist->values_list = g_slist_append (NULL, insert_values_list);
+		st = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
+		st->contents = ist;
+		ret_insert = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
+		gda_sql_statement_free (st);
+	}
+	if (update) {
+		GdaSqlStatement *st;
+		st = gda_sql_statement_new (GDA_SQL_STATEMENT_UPDATE);
+		st->contents = ust;
+		ret_update = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
+		gda_sql_statement_free (st);
+	}
+	if (delete) {
+		GdaSqlStatement *st;
+		st = gda_sql_statement_new (GDA_SQL_STATEMENT_DELETE);
+		st->contents = dst;
+		ret_delete = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
+		gda_sql_statement_free (st);
+	}
+	
+ cleanup:
+	gda_sql_statement_free (sel_struct);
+	if (insert)
+		*insert = ret_insert;
+	if (update)
+		*update = ret_update;
+	if (delete)
+		*delete = ret_delete;
+	return retval;
+}

Modified: trunk/libgda/gda-util.h
==============================================================================
--- trunk/libgda/gda-util.h	(original)
+++ trunk/libgda/gda-util.h	Wed Apr  2 20:22:59 2008
@@ -38,8 +38,6 @@
 const gchar *gda_g_type_to_string (GType type);
 GType        gda_g_type_from_string (const gchar *str);
 
-GList       *gda_string_hash_to_list (GHashTable *hash_table);
-
 /* 
  * SQL escaping
  */
@@ -47,30 +45,26 @@
 gchar       *gda_default_unescape_string (const gchar *string);
 
 /*
- * File management utility functions
- */
-gchar    *gda_file_load (const gchar *filename);
-gboolean  gda_file_save (const gchar *filename, const gchar *buffer, gint len);
-
-/*
- * XML Id encoding and decoding
- */
-gchar *gda_utility_build_encoded_id (const gchar *prefix, const gchar *id);
-gchar *gda_utility_build_decoded_id (const gchar *prefix, const gchar *id);
-
-/*
  * Param & model utilities
  */
-gboolean gda_utility_check_data_model (GdaDataModel *model, gint nbcols, ...);
-void     gda_utility_data_model_dump_data_to_xml (GdaDataModel *model, xmlNodePtr parent, 
+gboolean     gda_utility_check_data_model (GdaDataModel *model, gint nbcols, ...);
+void         gda_utility_data_model_dump_data_to_xml (GdaDataModel *model, xmlNodePtr parent, 
 					      const gint *cols, gint nb_cols, const gint *rows, gint nb_rows,
 					      gboolean use_col_ids);
-void     gda_utility_holder_load_attributes (GdaHolder *holder, xmlNodePtr node, GSList *sources);
+void         gda_utility_holder_load_attributes (GdaHolder *holder, xmlNodePtr node, GSList *sources);
 
-/* translate any text to an alphanumerical text */
-gchar *gda_text_to_alphanum (const gchar *text);
-gchar *gda_alphanum_to_text (gchar *text);
+/* 
+ * translate any text to an alphanumerical text 
+ */
+gchar       *gda_text_to_alphanum (const gchar *text);
+gchar       *gda_alphanum_to_text (gchar *text);
 
+/*
+ * Statement computation from meta store 
+ */
+gboolean     gda_compute_dml_statements (GdaConnection *cnc, GdaStatement *select_stmt, gboolean require_pk, 
+					 GdaStatement **insert, GdaStatement **update, GdaStatement **delete, 
+					 GError **error);
 G_END_DECLS
 
 #endif

Modified: trunk/libgda/libgda.h.in
==============================================================================
--- trunk/libgda/libgda.h.in	(original)
+++ trunk/libgda/libgda.h.in	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* GDA library
- * Copyright (C) 1998 - 2006 The GNOME Foundation.
+ * Copyright (C) 1998 - 2008 The GNOME Foundation.
  *
  * AUTHORS:
  *      Michael Lausch <michael lausch at>
@@ -27,15 +27,14 @@
 #define __LIBGDA_H__
 
 #include <libgda/gda-easy.h>
-#include <libgda/gda-column-index.h>
 #include <libgda/gda-column.h>
 #include <libgda/gda-config.h>
 #include <libgda/gda-connection-event.h>
 #include <libgda/gda-connection.h>
 #include <libgda/gda-connection-private.h>
+#include <libgda/gda-data-comparator.h>
 #include <libgda/gda-data-model-array.h>
 @LIBGDA_BDB_INC@
-#include <libgda/gda-data-model-index.h>
 #include <libgda/gda-data-model-query.h>
 #include <libgda/gda-data-model.h>
 #include <libgda/gda-data-model-iter.h>

Modified: trunk/libgda/sql-parser/gda-statement-struct-compound.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-compound.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-compound.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -24,8 +24,8 @@
 #include <string.h>
 #include <glib/gi18n-lib.h>
 
-static gpointer  gda_sql_statement_compound_new (void);
-static gboolean  gda_sql_statement_compound_check_structure (GdaSqlAnyPart *stmt, gpointer data, GError **error);
+static gpointer gda_sql_statement_compound_new (void);
+static gboolean gda_sql_statement_compound_check_structure (GdaSqlAnyPart *stmt, gpointer data, GError **error);
 
 GdaSqlStatementContentsInfo compound_infos = {
 	GDA_SQL_STATEMENT_COMPOUND,
@@ -35,7 +35,8 @@
 	gda_sql_statement_compound_copy,
 	gda_sql_statement_compound_serialize,
 
-	gda_sql_statement_compound_check_structure
+	gda_sql_statement_compound_check_structure,
+	NULL
 };
 
 GdaSqlStatementContentsInfo *

Modified: trunk/libgda/sql-parser/gda-statement-struct-decl.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-decl.h	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-decl.h	Wed Apr  2 20:22:59 2008
@@ -22,6 +22,9 @@
 
 #include <glib.h>
 #include <glib-object.h>
+#include <libgda/gda-decl.h>
+#include <libgda/gda-meta-store.h>
+#include <libgda/gda-meta-struct.h>
 
 /* error reporting */
 extern GQuark gda_sql_error_quark (void);
@@ -30,7 +33,7 @@
 typedef enum {
 	GDA_SQL_STRUCTURE_CONTENTS_ERROR,
 	GDA_SQL_MALFORMED_IDENTIFIER_ERROR,
-	GDA_SQL_DICT_ELEMENT_MISSING_ERROR
+	GDA_SQL_VALIDATION_ERROR
 } GdaSqlErrorType;
 
 /*
@@ -137,6 +140,17 @@
 
 	/* augmenting information precision using a dictionary */
 	GdaSqlForeachFunc     check_structure_func;
+	GdaSqlForeachFunc     check_validity_func;
 } GdaSqlStatementContentsInfo;
 
+/*
+ * Validation against a dictionary
+ */
+
+typedef struct {
+	GdaConnection *cnc;
+	GdaMetaStore  *store;
+	GdaMetaStruct *mstruct;
+} GdaSqlStatementCheckValidityData;
+
 #endif

Modified: trunk/libgda/sql-parser/gda-statement-struct-delete.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-delete.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-delete.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -39,7 +39,8 @@
 	gda_sql_statement_delete_copy,
 	gda_sql_statement_delete_serialize,
 
-	gda_sql_statement_delete_check_structure
+	gda_sql_statement_delete_check_structure,
+	NULL
 };
 
 GdaSqlStatementContentsInfo *

Modified: trunk/libgda/sql-parser/gda-statement-struct-insert.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-insert.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-insert.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -40,7 +40,8 @@
 	gda_sql_statement_insert_copy,
 	gda_sql_statement_insert_serialize,
 
-	gda_sql_statement_insert_check_structure
+	gda_sql_statement_insert_check_structure,
+	NULL
 };
 
 GdaSqlStatementContentsInfo *

Modified: trunk/libgda/sql-parser/gda-statement-struct-parts.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-parts.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-parts.c	Wed Apr  2 20:22:59 2008
@@ -219,6 +219,7 @@
 	copy = gda_sql_field_new (NULL);
 	if (field->field_name)
 		copy->field_name = g_strdup (field->field_name);
+	copy->validity_meta_table_column = field->validity_meta_table_column;
 
 	return copy;
 }
@@ -274,6 +275,7 @@
 	copy = gda_sql_table_new (NULL);
 	if (table->table_name)
 		copy->table_name = g_strdup (table->table_name);
+	copy->validity_meta_object = table->validity_meta_object;
 
 	return copy;
 }
@@ -345,7 +347,8 @@
 			gda_sql_any_part_set_parent (copy->args_list->data, copy);
 		}
 		copy->args_list = g_slist_reverse (copy->args_list);
-	}	
+	}
+	copy->validity_meta_function = function->validity_meta_function;
 
 	return copy;
 }
@@ -782,6 +785,10 @@
 		copy->table_name = g_strdup (field->table_name);
 	if (field->as)
 		copy->as = g_strdup (field->as);
+
+	copy->validity_meta_object = field->validity_meta_object;
+	copy->validity_meta_table_column = field->validity_meta_table_column;
+
 	return copy;
 }
 
@@ -913,6 +920,9 @@
 		copy->table_name = g_strdup (target->table_name);
 	if (target->as)
 		copy->as = g_strdup (target->as);
+
+	copy->validity_meta_object = target->validity_meta_object;
+
 	return copy;
 }
 

Modified: trunk/libgda/sql-parser/gda-statement-struct-parts.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-parts.h	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-parts.h	Wed Apr  2 20:22:59 2008
@@ -25,6 +25,7 @@
 #include <sql-parser/gda-statement-struct.h>
 #include <sql-parser/gda-statement-struct-pspec.h>
 #include <sql-parser/gda-statement-struct-decl.h>
+#include <libgda/gda-meta-struct.h>
 
 typedef struct _GdaSqlExpr      GdaSqlExpr;
 typedef struct _GdaSqlField     GdaSqlField;
@@ -66,6 +67,9 @@
 struct _GdaSqlField {
 	GdaSqlAnyPart       any;
 	gchar              *field_name;
+
+	/* validity check with a connection */
+	GdaMetaTableColumn *validity_meta_table_column;
 };
 GdaSqlField     *gda_sql_field_new            (GdaSqlAnyPart *parent);
 void             gda_sql_field_free           (GdaSqlField *field);
@@ -83,8 +87,8 @@
 	GdaSqlAnyPart       any;
 	gchar              *table_name;
 
-	/* GdaMetaStore check */
-	gpointer            full_table_name;
+	/* validity check with a connection */
+	GdaMetaDbObject    *validity_meta_object;
 };
 
 GdaSqlTable     *gda_sql_table_new            (GdaSqlAnyPart *parent);
@@ -103,8 +107,8 @@
 	gchar              *function_name;
 	GSList             *args_list;
 
-	/* GdaDict check */
-	gchar              *full_function_name;
+	/* validity check with a connection */
+	gpointer            validity_meta_function; /* to be replaced with a pointer to a structure representing a DBMS data type in GdaMetaStruct */
 };
 
 GdaSqlFunction  *gda_sql_function_new            (GdaSqlAnyPart *parent);
@@ -195,8 +199,9 @@
 	gchar              *table_name; /* may be NULL if expr does not refer to a table.field */
 	gchar              *as; 
 
-	/* GdaDict check */
-	gchar              *full_table_name;
+	/* validity check with a connection */
+	GdaMetaDbObject    *validity_meta_object;
+	GdaMetaTableColumn *validity_meta_table_column;
 };
 
 GdaSqlSelectField *gda_sql_select_field_new            (GdaSqlAnyPart *parent);
@@ -219,8 +224,8 @@
 	gchar              *table_name; /* may be NULL if expr does not refer to a table */
 	gchar              *as; 
 
-	/* GdaDict check */
-	gchar              *full_table_name;
+	/* validity check with a connection */
+	GdaMetaDbObject    *validity_meta_object;
 };
 
 GdaSqlSelectTarget *gda_sql_select_target_new            (GdaSqlAnyPart *parent);

Modified: trunk/libgda/sql-parser/gda-statement-struct-pspec.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-pspec.h	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-pspec.h	Wed Apr  2 20:22:59 2008
@@ -37,7 +37,7 @@
 	gboolean  nullok;
 
 	GType     g_type;
-	gpointer  dict_type;
+	gpointer  validity_meta_dict; /* to be replaced with a pointer to a structure representing a DBMS data type in GdaMetaStruct */
 };
 GdaSqlParamSpec *gda_sql_param_spec_new        (GValue *simple_spec);
 GdaSqlParamSpec *gda_sql_param_spec_copy       (GdaSqlParamSpec *pspec);

Modified: trunk/libgda/sql-parser/gda-statement-struct-select.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-select.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-select.c	Wed Apr  2 20:22:59 2008
@@ -25,6 +25,7 @@
 #include <glib/gi18n-lib.h>
 
 static gboolean gda_sql_statement_select_check_structure (GdaSqlAnyPart *stmt, gpointer data, GError **error);
+static gboolean gda_sql_statement_select_check_validity (GdaSqlAnyPart *stmt, gpointer data, GError **error);
 
 GdaSqlStatementContentsInfo select_infos = {
 	GDA_SQL_STATEMENT_SELECT,
@@ -34,7 +35,8 @@
 	gda_sql_statement_select_copy,
 	gda_sql_statement_select_serialize,
 
-	gda_sql_statement_select_check_structure
+	gda_sql_statement_select_check_structure,
+	gda_sql_statement_select_check_validity
 };
 
 GdaSqlStatementContentsInfo *
@@ -335,3 +337,48 @@
 	}
 	return TRUE;
 }
+
+static gboolean
+gda_sql_statement_select_check_validity (GdaSqlAnyPart *stmt, gpointer data, GError **error)
+{
+	GdaSqlStatementSelect *select = (GdaSqlStatementSelect *) stmt;
+	GdaSqlStatementCheckValidityData *ddata = (GdaSqlStatementCheckValidityData*) data;
+	gboolean retval = TRUE;
+
+	/* validate target's names and aliases:
+	 * - there can't be 2 targets with the same alias
+	 * - each target name or alias can only reference at most one target
+	 */
+	if (select->from && select->from->targets) {
+		GHashTable *hash; /* key = target name or alias, value = GdaSqlSelectTarget pointer */
+		GSList *list;
+		hash = g_hash_table_new (g_str_hash, g_str_equal);
+		for (list = select->from->targets; list; list = list->next) {
+			GdaSqlSelectTarget *t = (GdaSqlSelectTarget*) list->data;
+			if (t->table_name) {
+				if (g_hash_table_lookup (hash, t->table_name)) {
+					g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+						     _("Multiple targets named or aliased '%s'"), t->table_name);
+					retval = FALSE;
+					break;
+				}
+				g_hash_table_insert (hash, t->table_name, t);
+			}
+			if (t->as) {
+				if (g_hash_table_lookup (hash, t->as)) {
+					g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+						     _("Multiple targets named or aliased '%s'"), t->as);
+					retval = FALSE;
+					break;
+				}
+				g_hash_table_insert (hash, t->as, t);
+			}
+
+			if (!retval)
+				break;
+		}
+		g_hash_table_destroy (hash);
+	}
+
+	return retval;
+}

Modified: trunk/libgda/sql-parser/gda-statement-struct-trans.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-trans.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-trans.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -36,6 +36,7 @@
 	gda_sql_statement_trans_free,
 	gda_sql_statement_trans_copy,
 	gda_sql_statement_trans_serialize,
+	NULL,
 	NULL
 };
 

Modified: trunk/libgda/sql-parser/gda-statement-struct-unknown.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-unknown.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-unknown.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -34,6 +34,7 @@
 	gda_sql_statement_unknown_free,
 	gda_sql_statement_unknown_copy,
 	gda_sql_statement_unknown_serialize,
+	NULL,
 	NULL
 };
 

Modified: trunk/libgda/sql-parser/gda-statement-struct-update.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-update.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-update.c	Wed Apr  2 20:22:59 2008
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 Vivien Malerba
+ * Copyright (C) 2007 - 2008 Vivien Malerba
  *
  * This Library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public License as
@@ -39,7 +39,8 @@
 	gda_sql_statement_update_copy,
 	gda_sql_statement_update_serialize,
 
-	gda_sql_statement_update_check_structure
+	gda_sql_statement_update_check_structure,
+	NULL
 };
 
 GdaSqlStatementContentsInfo *

Modified: trunk/libgda/sql-parser/gda-statement-struct.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct.c	Wed Apr  2 20:22:59 2008
@@ -22,6 +22,7 @@
 #include <libgda/sql-parser/gda-statement-struct.h>
 #include <libgda/gda-debug-macros.h>
 #include <libgda/gda-connection.h>
+#include <libgda/gda-meta-struct.h>
 #include <libgda/sql-parser/gda-statement-struct-util.h>
 #include <libgda/sql-parser/gda-statement-struct-unknown.h>
 #include <libgda/sql-parser/gda-statement-struct-trans.h>
@@ -111,6 +112,10 @@
 	}
 	else 
 		TO_IMPLEMENT;
+	if (stmt->validity_meta_struct) {
+		copy->validity_meta_struct = stmt->validity_meta_struct;
+		g_object_ref (copy->validity_meta_struct);
+	}
 
 	return copy;
 }
@@ -129,6 +134,9 @@
 		else 
 			TO_IMPLEMENT;
 	}
+	if (stmt->validity_meta_struct)
+		g_object_unref (stmt->validity_meta_struct);
+
 	g_free (stmt);
 }
 
@@ -223,64 +231,99 @@
 /*
  * Check with dict data structure
  */
-typedef struct {
-	GdaConnection *cnc;
-} DictCheckData;
-
-static gboolean foreach_check_cnc (GdaSqlAnyPart *node, DictCheckData *data, GError **error);
-static gboolean gda_sql_expr_check_cnc (GdaSqlExpr *expr, DictCheckData *data, GError **error);
-static gboolean gda_sql_field_check_cnc (GdaSqlField *field, DictCheckData *data, GError **error);
-static gboolean gda_sql_table_check_cnc (GdaSqlTable *table, DictCheckData *data, GError **error);
-static gboolean gda_sql_function_check_cnc (GdaSqlFunction *function, DictCheckData *data, GError **error);
-static gboolean gda_sql_select_field_check_cnc (GdaSqlSelectField *field, DictCheckData *data, GError **error);
-static gboolean gda_sql_select_target_check_cnc (GdaSqlSelectTarget *target, DictCheckData *data, GError **error);
+static gboolean foreach_check_clean (GdaSqlAnyPart *node, gpointer data, GError **error);
+
+static gboolean foreach_check_validity (GdaSqlAnyPart *node, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_expr_check_validity (GdaSqlExpr *expr, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_field_check_validity (GdaSqlField *field, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_table_check_validity (GdaSqlTable *table, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_function_check_validity (GdaSqlFunction *function, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_select_field_check_validity (GdaSqlSelectField *field, GdaSqlStatementCheckValidityData *data, GError **error);
+static gboolean gda_sql_select_target_check_validity (GdaSqlSelectTarget *target, GdaSqlStatementCheckValidityData *data, GError **error);
 
 /**
- * gda_sql_statement_check_connection
+ * gda_sql_statement_check_validity
  * @stmt: a #GdaSqlStatement pointer
  * @cnc: a #GdaConnection object, or %NULL
  * @error: a place to store errors, or %NULL
  *
  * If @cnc is not %NULL, then checks that all the database objects referenced in the statement actually
  * exist in the connection's database (for example the table being updated in a UPDATE statement must exist in the
- * connection's database for the check to succeed).
+ * connection's database for the check to succeed). This method fills the @stmt-&gt;validity_meta_struct attribute.
  *
- * If @cnc is %NULL, then remove any information from a previous call to this method stored in @stmt.
+ * If @cnc is %NULL, then remove any information from a previous call to this method stored in @stmt. In this case,
+ * the @stmt-&gt;validity_meta_struct attribute is cleared.
  *
  * Returns: TRUE if no error occurred
  */
 gboolean
-gda_sql_statement_check_connection (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error)
+gda_sql_statement_check_validity (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error)
 {
-	DictCheckData data;
-
 	g_return_val_if_fail (stmt, FALSE);
 	g_return_val_if_fail (!cnc || GDA_IS_CONNECTION (cnc), FALSE);
 
-	data.cnc = cnc;
+	/* check the structure first */
+	if (!gda_sql_statement_check_structure (stmt, error))
+		return FALSE;
 
-	return gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stmt->contents), 
-					 (GdaSqlForeachFunc) foreach_check_cnc, &data, error);
+	/* clear any previous setting */
+	gda_sql_statement_check_clean (stmt);
+
+	if (cnc) {
+		GdaSqlStatementCheckValidityData data;
+		gboolean retval;
+
+		/* prepare data */
+		data.cnc = cnc;
+		data.store = gda_connection_get_meta_store (cnc);
+		data.mstruct = gda_meta_struct_new (GDA_META_STRUCT_FEATURE_NONE);
+		
+		/* attach the GdaMetaStruct to @stmt */
+		stmt->validity_meta_struct = data.mstruct;
+		retval = gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stmt->contents), 
+						   (GdaSqlForeachFunc) foreach_check_validity, &data, error);
+		return retval;
+	}
+	else
+		return TRUE;
 }
 
 static gboolean
-foreach_check_cnc (GdaSqlAnyPart *node, DictCheckData *data, GError **error)
+foreach_check_validity (GdaSqlAnyPart *node, GdaSqlStatementCheckValidityData *data, GError **error)
 {
 	if (!node) return TRUE;
 
 	switch (node->type) {
+	case GDA_SQL_ANY_STMT_SELECT:
+        case GDA_SQL_ANY_STMT_INSERT:
+        case GDA_SQL_ANY_STMT_UPDATE:
+        case GDA_SQL_ANY_STMT_DELETE:
+        case GDA_SQL_ANY_STMT_COMPOUND:
+        case GDA_SQL_ANY_STMT_BEGIN:
+        case GDA_SQL_ANY_STMT_ROLLBACK:
+        case GDA_SQL_ANY_STMT_COMMIT:
+        case GDA_SQL_ANY_STMT_SAVEPOINT:
+        case GDA_SQL_ANY_STMT_ROLLBACK_SAVEPOINT:
+        case GDA_SQL_ANY_STMT_DELETE_SAVEPOINT:
+        case GDA_SQL_ANY_STMT_UNKNOWN: {
+		GdaSqlStatementContentsInfo *cinfo;
+		cinfo = gda_sql_statement_get_contents_infos (node->type);
+		if (cinfo->check_validity_func)
+			return cinfo->check_validity_func (node, data, error);
+		break;
+	}
 	case GDA_SQL_ANY_EXPR:
-		return gda_sql_expr_check_cnc ((GdaSqlExpr*) node, data, error);
+		return gda_sql_expr_check_validity ((GdaSqlExpr*) node, data, error);
 	case GDA_SQL_ANY_SQL_FIELD:
-		return gda_sql_field_check_cnc ((GdaSqlField*) node, data, error);
+		return gda_sql_field_check_validity ((GdaSqlField*) node, data, error);
 	case GDA_SQL_ANY_SQL_TABLE:
-		return gda_sql_table_check_cnc ((GdaSqlTable*) node, data, error);
+		return gda_sql_table_check_validity ((GdaSqlTable*) node, data, error);
 	case GDA_SQL_ANY_SQL_FUNCTION:
-		return gda_sql_function_check_cnc ((GdaSqlFunction*) node, data, error);
+		return gda_sql_function_check_validity ((GdaSqlFunction*) node, data, error);
 	case GDA_SQL_ANY_SQL_SELECT_FIELD:
-		return gda_sql_select_field_check_cnc ((GdaSqlSelectField*) node, data, error);
+		return gda_sql_select_field_check_validity ((GdaSqlSelectField*) node, data, error);
 	case GDA_SQL_ANY_SQL_SELECT_TARGET:
-		return gda_sql_select_target_check_cnc ((GdaSqlSelectTarget*) node, data, error);
+		return gda_sql_select_target_check_validity ((GdaSqlSelectTarget*) node, data, error);
 	default:
 		break;
 	}
@@ -288,23 +331,31 @@
 }
 
 static gboolean
-gda_sql_expr_check_cnc (GdaSqlExpr *expr, DictCheckData *data, GError **error)
+gda_sql_expr_check_validity (GdaSqlExpr *expr, GdaSqlStatementCheckValidityData *data, GError **error)
 {
 	if (!expr) return TRUE;
 	if (!expr->param_spec) return TRUE;
 	gda_sql_expr_check_clean (expr);
 
 	/* 2 checks to do here:
-	 *  - try to find the GdaDictType from expr->param_spec->type
+	 *  - try to find the data type from expr->param_spec->type using data->mstruct (not yet possible)
 	 *  - if expr->param_spec->type is NULL, then try to identify it (and expr->param_spec->g_type)
-	 *    using @expr context
+	 *    using @expr context, set expr->param_spec->validity_meta_dict.
 	 */
         TO_IMPLEMENT;
         return FALSE;
 }
 
+void
+gda_sql_expr_check_clean (GdaSqlExpr *expr)
+{
+	if (!expr) return;
+	if (expr->param_spec && expr->param_spec->validity_meta_dict)
+		TO_IMPLEMENT;
+}
+
 static gboolean
-gda_sql_field_check_cnc (GdaSqlField *field, DictCheckData *data, GError **error)
+gda_sql_field_check_validity (GdaSqlField *field, GdaSqlStatementCheckValidityData *data, GError **error)
 {
 	GdaSqlAnyPart *any;
 	GdaSqlTable *stable;
@@ -331,144 +382,308 @@
 		g_assert_not_reached ();
 
 	if (!stable) {
-		if (any->type == GDA_SQL_ANY_STMT_INSERT)
-			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
-				     _("Missing table in INSERT statement"));
-		else
-			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
-				     _("Missing table in UPDATE statement"));
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+			     _("Missing table in statement"));
 		return FALSE;
 	}
-	if (!stable->full_table_name) {
-		if (! gda_sql_table_check_cnc (stable, data, error))
-			return FALSE;
-		g_assert (stable->full_table_name);
-
-		GdaDataModel *model;
-		GValue *v1, *v2;
-		g_value_set_string (v1 = gda_value_new (G_TYPE_STRING), stable->table_name);
-		g_value_set_string (v2 = gda_value_new (G_TYPE_STRING), field->field_name);
-		model = gda_connection_get_meta_store_data (data->cnc, GDA_CONNECTION_META_FIELDS, error, 2,
-							    "name", v1, "field_name", v2);
-		if (!model)
-			return FALSE;
-		if (gda_data_model_get_n_rows (model) == 0) {
-			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_DICT_ELEMENT_MISSING_ERROR,
-				     _("Field %s.%s not found"), stable->table_name, field->field_name);
-			g_object_unref (model);
+	if (!stable->validity_meta_object) {
+		if (! gda_sql_table_check_validity (stable, data, error))
 			return FALSE;
-		}
-		g_object_unref (model);
-		return TRUE;
 	}
 
+	g_assert (stable->validity_meta_object);
+	GdaMetaTableColumn *tcol;
+	GValue value;
+	memset (&value, 0, sizeof (GValue));
+	g_value_set_string (g_value_init (&value, G_TYPE_STRING), field->field_name);
+	tcol = gda_meta_struct_get_table_column (data->mstruct, 
+						 GDA_META_DB_OBJECT_GET_TABLE (stable->validity_meta_object), 
+						 &value);
+	g_value_unset (&value);
+	field->validity_meta_table_column = tcol;
+	if (!tcol) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+			     _("Column '%s' not found"), field->field_name);
+		return FALSE;
+	}
+	
         return TRUE;
 }
 
-void
-gda_sql_expr_check_clean (GdaSqlExpr *expr)
-{
-	if (!expr) return;
-	if (expr->param_spec && expr->param_spec->dict_type)
-		TO_IMPLEMENT;
-}
-
 void 
 gda_sql_field_check_clean (GdaSqlField *field)
 {
 	if (!field) return;
-	/* nothing to do */
+	field->validity_meta_table_column = NULL;
+}
+
+/* For GdaSqlSelectTarget, GdaSqlSelectField and GdaSqlTable */
+static GdaMetaDbObject *
+find_table_or_view (GdaSqlAnyPart *part, GdaSqlStatementCheckValidityData *data, const gchar *name, GError **error)
+{
+	GdaMetaDbObject *dbo;
+	GValue value;
+	GError *lerror = NULL;
+	memset (&value, 0, sizeof (GValue));
+
+	/* use @name as the table or view's real name */
+	g_value_set_string (g_value_init (&value, G_TYPE_STRING), name);
+	dbo = gda_meta_struct_complement (data->mstruct, data->store, GDA_META_DB_UNKNOWN,
+					  NULL, NULL, &value, &lerror);
+	g_value_unset (&value);
+	if (!dbo) {
+		/* use @name as a table alias in the statement */
+		GdaSqlAnyPart *any;
+
+		for (any = part->parent; any && any->parent; any = any->parent);
+		if (!any) 
+			g_set_error (&lerror, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+				     _("GdaSqlSelectField is not part of a SELECT statement"));
+		else {
+			switch (any->type) {
+			case GDA_SQL_ANY_STMT_SELECT: {
+				GdaSqlStatementSelect *select = (GdaSqlStatementSelect*) any;
+				if (select->from) {
+					GSList *targets;
+					for (targets = select->from->targets; targets; targets = targets->next) {
+						GdaSqlSelectTarget *target = (GdaSqlSelectTarget*) targets->data;
+						if (!target->as)
+							continue;
+						g_value_set_string (g_value_init (&value, G_TYPE_STRING), 
+								    target->table_name);
+						dbo = gda_meta_struct_complement (data->mstruct, data->store, 
+										  GDA_META_DB_UNKNOWN,
+										  NULL, NULL, &value, NULL);
+						g_value_unset (&value);
+						if (dbo)
+							break;
+					}
+				}
+				break;
+			}
+			case GDA_SQL_ANY_STMT_INSERT:
+				TO_IMPLEMENT;
+				break;
+			case GDA_SQL_ANY_STMT_UPDATE:
+				TO_IMPLEMENT;
+				break;
+			case GDA_SQL_ANY_STMT_DELETE:
+				TO_IMPLEMENT;
+				break;
+			default:
+				g_assert_not_reached ();
+				break;
+			}
+		}
+	}
+	if (dbo) {
+		if (lerror)
+			g_error_free (lerror);
+	}
+	else {
+		if (lerror)
+			g_propagate_error (error, lerror);
+	}
+	return dbo;
 }
 
 static gboolean
-gda_sql_table_check_cnc (GdaSqlTable *table, DictCheckData *data, GError **error)
+gda_sql_table_check_validity (GdaSqlTable *table, GdaSqlStatementCheckValidityData *data, GError **error)
 {
+	GdaMetaDbObject *dbo;
+
 	if (!table) return TRUE;
+	gda_sql_table_check_clean (table);
 
-        TO_IMPLEMENT;
-        return FALSE;
+	if (!table->table_name) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+			     _("Missing table name in statement"));
+		return FALSE;
+	}
+
+	dbo = find_table_or_view ((GdaSqlAnyPart*) table, data, table->table_name, error);
+	if (dbo && ((dbo->obj_type != GDA_META_DB_TABLE) ||
+		    (dbo->obj_type != GDA_META_DB_VIEW))) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+			     _("Table '%s' not found"), table->table_name);
+		return FALSE;
+	}
+	table->validity_meta_object = dbo;
+
+	return table->validity_meta_object ? TRUE : FALSE;
 }
 
 void 
 gda_sql_table_check_clean (GdaSqlTable *table)
 {
 	if (!table) return;
-	if (table->full_table_name) {
-		g_free (table->full_table_name);
-		table->full_table_name = NULL;
-	}
+	table->validity_meta_object = NULL;
 }
 
 static gboolean
-gda_sql_function_check_cnc (GdaSqlFunction *function, DictCheckData *data, GError **error)
+gda_sql_function_check_validity (GdaSqlFunction *function, GdaSqlStatementCheckValidityData *data, GError **error)
 {
 	if (!function) return TRUE;
 
         TO_IMPLEMENT;
-        return FALSE;
+        return TRUE;
 }
 
 void
 gda_sql_function_check_clean (GdaSqlFunction *function)
 {
 	if (!function) return;
-	if (function->full_function_name) {
-		g_free (function->full_function_name);
-		function->full_function_name = NULL;
-	}
+	function->validity_meta_function = NULL;
 }
 
 static gboolean
-gda_sql_select_field_check_cnc (GdaSqlSelectField *field, DictCheckData *data, GError **error)
+gda_sql_select_field_check_validity (GdaSqlSelectField *field, GdaSqlStatementCheckValidityData *data, GError **error)
 {
+	GValue value;
+	GdaMetaDbObject *dbo = NULL;
+	gboolean starred_field = FALSE;
+
 	if (!field) return TRUE;
+	gda_sql_select_field_check_clean (field);
+
+	if (!field->field_name) 
+		/* field is not a table.field */
+		return TRUE;
+
+	memset (&value, 0, sizeof (GValue));
+	if (!strcmp (field->field_name, "*"))
+		starred_field = TRUE;
+
+	if (!field->table_name) {
+		/* go through all the SELECT's targets to see if there is a table with the corresponding field */
+		GdaSqlAnyPart *any;
+		GSList *targets;
+		GdaMetaTableColumn *tcol = NULL;
+
+		for (any = GDA_SQL_ANY_PART(field)->parent; 
+		     any && (any->type != GDA_SQL_ANY_STMT_SELECT);
+		     any = any->parent);
+		if (!any) {
+			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+				     _("GdaSqlSelectField is not part of a SELECT statement"));
+			return FALSE;
+		}
 
-	TO_IMPLEMENT;
-	return FALSE;
+		for (targets = ((GdaSqlStatementSelect *)any)->from->targets; targets; targets = targets->next) {
+			GdaSqlSelectTarget *target = (GdaSqlSelectTarget *) targets->data;
+			if (!target->validity_meta_object && 
+			    !gda_sql_select_target_check_validity (target, data, error))
+				return FALSE;
+
+			g_value_set_string (g_value_init (&value, G_TYPE_STRING), field->field_name);
+			tcol = gda_meta_struct_get_table_column (data->mstruct, 
+								 GDA_META_DB_OBJECT_GET_TABLE (target->validity_meta_object),
+								 &value);
+			g_value_unset (&value);
+			if (tcol) {
+				/* found a candidate */
+				if (dbo) {
+					g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+						     _("Could not identify table for field '%s'"), field->field_name);
+					return FALSE;
+				}
+				dbo = target->validity_meta_object;
+			}
+		}
+		if (!dbo) {
+			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+				     _("Could not identify table for field '%s'"), field->field_name);
+			return FALSE;
+		}
+		field->validity_meta_object = dbo;
+		field->validity_meta_table_column = tcol;
+	}
+	else {
+		/* table part */
+		dbo = find_table_or_view ((GdaSqlAnyPart*) field, data, field->table_name, error);
+		if (dbo && (dbo->obj_type != GDA_META_DB_TABLE) &&
+		    (dbo->obj_type != GDA_META_DB_VIEW)) {
+			g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+				     _("Table '%s' not found"), field->table_name);
+			return FALSE;
+		}
+		field->validity_meta_object = dbo;
+		if (!field->validity_meta_object)
+			return FALSE;
+		
+		/* field part */
+		if (strcmp (field->field_name, "*")) {
+			GdaMetaTableColumn *tcol;
+			g_value_set_string (g_value_init (&value, G_TYPE_STRING), field->field_name);
+			tcol = gda_meta_struct_get_table_column (data->mstruct, 
+								 GDA_META_DB_OBJECT_GET_TABLE (field->validity_meta_object), 
+								 &value);
+			g_value_unset (&value);
+			field->validity_meta_table_column = tcol;
+			if (!tcol) {
+				g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+					     _("Column '%s' not found"), field->field_name);
+				return FALSE;
+			}
+		}
+	}
+	return TRUE;
 }
 
 void 
 gda_sql_select_field_check_clean (GdaSqlSelectField *field)
 {
 	if (!field) return;
-	if (field->full_table_name) {
-		g_free (field->full_table_name);
-		field->full_table_name = NULL;
-	}
+	field->validity_meta_object = NULL;
+	field->validity_meta_table_column = NULL;
 }
 
 static gboolean
-gda_sql_select_target_check_cnc (GdaSqlSelectTarget *target, DictCheckData *data, GError **error)
+gda_sql_select_target_check_validity (GdaSqlSelectTarget *target, GdaSqlStatementCheckValidityData *data, GError **error)
 {
+	GdaMetaDbObject *dbo;
+
 	if (!target) return TRUE;
+	if (!target->table_name)
+		return TRUE;
 
-	TO_IMPLEMENT;
-	return FALSE;
+	dbo = find_table_or_view ((GdaSqlAnyPart*) target, data, target->table_name, error);
+	if (dbo && (dbo->obj_type != GDA_META_DB_TABLE) &&
+	    (dbo->obj_type != GDA_META_DB_VIEW)) {
+		g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+			     _("Table '%s' not found"), target->table_name);
+		return FALSE;
+	}
+	target->validity_meta_object = dbo;
+	return target->validity_meta_object ? TRUE : FALSE;
 }
 
 void 
 gda_sql_select_target_check_clean (GdaSqlSelectTarget *target)
 {
 	if (!target) return;
-	if (target->full_table_name) {
-		g_free (target->full_table_name);
-		target->full_table_name = NULL;
-	}
+	target->validity_meta_object = NULL;
 }
 
 
 
-static gboolean foreach_check_clean (GdaSqlAnyPart *node, gpointer data, GError **error);
 /**
  * gda_sql_statement_check_clean
+ * @stmt: a pinter to a #GdaSqlStatement structure
+ *
+ * Cleans any data set by a previous call to gda_sql_statement_check_validity().
  */
 void
 gda_sql_statement_check_clean (GdaSqlStatement *stmt)
 {
 	g_return_if_fail (stmt);
 
-	gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stmt->contents), 
-				  (GdaSqlForeachFunc) foreach_check_clean, NULL, NULL);
+	if (stmt->validity_meta_struct) {
+		gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stmt->contents), 
+					  (GdaSqlForeachFunc) foreach_check_clean, NULL, NULL);
+		g_object_unref (stmt->validity_meta_struct);
+		stmt->validity_meta_struct = NULL;
+	}
 }
 
 static gboolean

Modified: trunk/libgda/sql-parser/gda-statement-struct.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct.h	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct.h	Wed Apr  2 20:22:59 2008
@@ -29,6 +29,7 @@
 	gchar               *sql;
 	GdaSqlStatementType  stmt_type;
 	gpointer             contents; /* depends on stmt_type */
+	GdaMetaStruct       *validity_meta_struct; /* set when gda_sql_statement_check_validity() was last called */
 };
 
 GdaSqlStatement             *gda_sql_statement_new       (GdaSqlStatementType type);
@@ -40,7 +41,7 @@
 GdaSqlStatementType          gda_sql_statement_string_to_type (const gchar *type);
 
 gboolean                     gda_sql_statement_check_structure (GdaSqlStatement *stmt, GError **error);
-gboolean                     gda_sql_statement_check_connection(GdaSqlStatement *stmt, GdaConnection *cnc, GError **error);
+gboolean                     gda_sql_statement_check_validity  (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error);
 void                         gda_sql_statement_check_clean     (GdaSqlStatement *stmt);
 
 GdaSqlStatementContentsInfo *gda_sql_statement_get_contents_infos (GdaSqlStatementType type) ;

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Wed Apr  2 20:22:59 2008
@@ -610,7 +610,7 @@
 #endif
 
 	if (filename)
-		cdata->file = g_strdup (filename);
+		cdata->file = filename;
 
 	errmsg = sqlite3_open (filename, &cdata->connection);
 

Modified: trunk/providers/postgres/gda-postgres-meta.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-meta.c	(original)
+++ trunk/providers/postgres/gda-postgres-meta.c	Wed Apr  2 20:22:59 2008
@@ -33,8 +33,6 @@
 #include <libgda/gda-data-model-array.h>
 #include <libgda/gda-set.h>
 
-static gboolean append_a_row (GdaDataModel *to_model, GError **error, gint nb, ...);
-
 /*
  * predefined statements' IDs
  */

Modified: trunk/tests/parser/Makefile.am
==============================================================================
--- trunk/tests/parser/Makefile.am	(original)
+++ trunk/tests/parser/Makefile.am	Wed Apr  2 20:22:59 2008
@@ -5,12 +5,17 @@
 	$(LIBGDA_CFLAGS) \
 	-DROOT_DIR=\""$(top_srcdir)"\" 
 
-TESTS = check_parser
-check_PROGRAMS = check_parser
+TESTS = check_parser check_validation
+check_PROGRAMS = check_parser check_validation
 
 check_parser_SOURCES = check_parser.c
 check_parser_LDADD = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(LIBGDA_LIBS)
 
-EXTRA_DIST = testdata.xml
+check_validation_SOURCES = check_validation.c
+check_validation_LDADD = \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(LIBGDA_LIBS)
+
+EXTRA_DIST = testdata.xml testvalid.xml

Added: trunk/tests/parser/check_validation.c
==============================================================================
--- (empty file)
+++ trunk/tests/parser/check_validation.c	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <gmodule.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-util.h>
+#include <sql-parser/gda-sql-parser.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+GdaConnection *cnc;
+static gint do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected, 
+		     const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk);
+
+int 
+main (int argc, char** argv)
+{
+	xmlDocPtr doc;
+        xmlNodePtr root, node;
+	gint failures = 0;
+	gint ntests = 0;
+	gchar *fname;
+
+	gda_init ("Parser validation", ".1", argc, argv);
+
+	/* open connection */
+	gchar *cnc_string;
+	fname = g_build_filename (ROOT_DIR, "data", NULL);
+	cnc_string = g_strdup_printf ("DB_DIR=%s;DB_NAME=sales_test", fname);
+	g_free (fname);
+	cnc = gda_connection_open_from_string ("SQLite", cnc_string, NULL,
+					       GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);
+	if (!cnc) {
+		g_print ("Failed to open connection, cnc_string = %s\n", cnc_string);
+		exit (1);
+	}
+	if (!gda_connection_update_meta_store (cnc, NULL, NULL)) {
+		g_print ("Failed to update meta store, cnc_string = %s\n", cnc_string);
+		exit (1);
+	}
+	g_free (cnc_string);
+
+	/* load file */
+	fname = g_build_filename (ROOT_DIR, "tests", "parser", "testvalid.xml", NULL);
+	if (! g_file_test (fname, G_FILE_TEST_EXISTS)) {
+                g_print ("File '%s' does not exist\n", fname);
+                exit (1);
+        }
+
+	/* use test data */
+	doc = xmlParseFile (fname);
+	g_free (fname);
+	g_assert (doc);
+	root = xmlDocGetRootElement (doc);
+	g_assert (!strcmp ((gchar*) root->name, "testdata"));
+	for (node = root->children; node; node = node->next) {
+		if (strcmp ((gchar*) node->name, "test"))
+			continue;
+		xmlNodePtr snode;
+		xmlChar *sql = NULL;
+		xmlChar *id;
+		gboolean valid = FALSE;
+
+		id = xmlGetProp (node, BAD_CAST "id");
+		for (snode = node->children; snode; snode = snode->next) {
+			if (!strcmp ((gchar*) snode->name, "sql")) {
+				sql = xmlNodeGetContent (snode);
+				xmlChar *prop;
+				prop = xmlGetProp (snode, "valid");
+				if (prop) {
+					if ((*prop == 't') || (*prop == 'T') || (*prop == '1'))
+						valid = TRUE;
+					xmlFree (prop);
+				}
+			}
+			else if (!strcmp ((gchar*) snode->name, "insert") ||
+				 !strcmp ((gchar*) snode->name, "update") ||
+				 !strcmp ((gchar*) snode->name, "delete")) {
+				xmlChar *comp_exp;
+				xmlChar *prop;
+				gboolean require_pk = TRUE;
+				comp_exp = xmlNodeGetContent (snode);
+				prop = xmlGetProp (snode, "need_pk");
+				if (prop) {
+					if ((*prop == 'f') || (*prop == 'F') || (*prop == '0'))
+						require_pk = FALSE;
+					xmlFree (prop);
+				}
+				if (sql) {
+					if (!do_test (id, sql, valid, snode->name, comp_exp, require_pk))
+						failures++;
+					ntests++;
+				}
+			}
+		}
+		if (sql) {
+			if (!do_test (id, sql, valid, NULL, NULL, FALSE))
+				failures++;
+			ntests++;
+		}
+
+		/* mem free */
+		if (sql) xmlFree (sql);
+		if (id)	xmlFree (id);
+	}
+	xmlFreeDoc (doc);
+
+	g_print ("TESTS COUNT: %d\n", ntests);
+	g_print ("FAILURES: %d\n", failures);
+  
+	return failures != 0 ? 1 : 0;
+}
+
+/*
+ * Returns: the number of failures
+ */
+static gint
+do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected, 
+	 const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk) 
+{
+	static GdaSqlParser *parser = NULL;
+	GdaStatement *stmt;
+	gboolean is_valid;
+	GError *error = NULL;
+
+	if (!parser) {
+		parser = gda_connection_create_parser (cnc);
+		if (!parser)
+			parser = gda_sql_parser_new ();
+	}
+
+#ifdef GDA_DEBUG
+	if (computed_type)
+		g_print ("===== TEST %s COMPUTING %s (%s), SQL: @%s \n", id, computed_type, 
+			 require_pk ? "PK fields" : "All fields", sql);
+	else
+		g_print ("===== TEST %s SQL: @%s \n", id, sql);
+#endif
+
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+	if (!stmt) {
+		g_print ("ERROR for test '%s': could not parse statement\n", id);
+		return FALSE;
+	}
+	is_valid = gda_statement_check_validity (stmt, cnc, &error);
+	if (is_valid && !valid_expected) {
+		g_print ("ERROR for test '%s': statement is valid but test expected it invalid\n", id);
+		g_object_unref (stmt);
+		return FALSE;
+	}
+	if (!is_valid && valid_expected) {
+		g_print ("ERROR for test '%s': statement is invalid but test expected it valid: %s\n", id,
+			 error && error->message ? error->message : "No detail");
+		g_object_unref (stmt);
+		return FALSE;
+	}
+	/*g_print ("EXP %d, got %d\n", valid_expected, is_valid);*/
+	/*g_print ("PARSED: %s\n", gda_statement_serialize (stmt));*/
+
+	if (computed_exp) {
+		GdaStatement *cstmt = NULL;
+		switch (*computed_type) {
+		case 'i':
+		case 'I':
+			gda_compute_dml_statements (cnc, stmt, require_pk, &cstmt, NULL, NULL, &error);
+			break;
+		case 'u':
+		case 'U':
+			gda_compute_dml_statements (cnc, stmt, require_pk, NULL, &cstmt, NULL, &error);
+			break;
+		case 'd':
+		case 'D':
+			gda_compute_dml_statements (cnc, stmt, require_pk, NULL, NULL, &cstmt, &error);
+			break;
+		default:
+			TO_IMPLEMENT;
+		}
+
+		if (!*computed_exp && cstmt) {
+			g_object_unref (stmt);
+			gchar *serial, *rend;
+			serial = gda_statement_serialize (cstmt);
+			rend = gda_statement_to_sql (cstmt, NULL, NULL);
+			g_print ("ERROR for test '%s': %s statement created but none expected\n"
+				 "\tgot: %s\n\tSQL: %s\n", id, computed_type, serial, rend);
+			g_free (serial);
+			g_free (rend);
+			g_object_unref (cstmt);
+			return FALSE;
+		}
+		if (*computed_exp && !cstmt) {
+			g_print ("ERROR for test '%s': %s statement not created but expected: %s\n", id,
+				 computed_type,
+				 error && error->message ? error->message : "No detail");
+			g_object_unref (stmt);
+			return FALSE;
+		}
+		if (*computed_exp && cstmt) {
+			gchar *serial;
+			serial = gda_statement_serialize (cstmt);
+			if (strcmp (serial, computed_exp)) {
+				gchar *rend;
+				rend = gda_statement_to_sql (cstmt, NULL, NULL);
+				g_print ("ERROR for test '%s': computed %s statement is incorrect:\n"
+					 "\texp: %s\n\tgot: %s\n\tSQL: %s\n", id, computed_type, computed_exp, serial,
+					 rend);
+				g_free (rend);
+				g_object_unref (stmt);
+				g_object_unref (cstmt);
+				g_free (serial);
+				return FALSE;
+			}
+			g_free (serial);
+		}
+	}
+	g_object_unref (stmt);
+	return TRUE;
+}

Added: trunk/tests/parser/testvalid.xml
==============================================================================
--- (empty file)
+++ trunk/tests/parser/testvalid.xml	Wed Apr  2 20:22:59 2008
@@ -0,0 +1,82 @@
+<testdata>
+  <!-- table not found -->
+  <test id="1">
+    <sql valid="f">SELECT id, name FROM customer</sql>
+  </test>
+
+  <!-- column ambiguity -->
+  <test id="2">
+    <sql valid="f">SELECT id FROM customers INNER JOIN salesrep</sql>
+  </test>
+
+  <test id="3">
+    <sql valid="t">SELECT customers.id FROM customers INNER JOIN salesrep</sql>
+  </test>
+
+  <test id="3">
+    <sql valid="t">SELECT c.id FROM customers as c INNER JOIN salesrep</sql>
+  </test>
+
+  <!-- multiple targets with naming conflicts -->
+  <test id="10">
+    <sql valid="f">SELECT c.id FROM customers as c INNER JOIN salesrep as c</sql>
+  </test>
+  <test id="11">
+    <sql valid="f">SELECT c.id FROM customers INNER JOIN salesrep as customers</sql>
+  </test>
+  <test id="11">
+    <sql valid="f">SELECT version() FROM customers, customers</sql>
+  </test>
+  
+  <!-- computing DML commands from SELECT -->
+
+  <!-- no target table to modify -->
+  <test id="pre0">
+    <sql valid="t">SELECT 3</sql>
+    <insert/>
+    <update/>
+    <delete/>
+  </test>
+
+  <!-- more than 1 target table to modify -->
+  <test id="pre1">
+    <sql valid="t">SELECT version() FROM customers, salesrep</sql>
+    <insert/>
+    <update/>
+    <delete/>
+  </test>
+
+  <!-- target is not a table -->
+  <test id="pre2">
+    <sql valid="t">SELECT 123 FROM (SELECT name FROM customers) as c</sql>
+    <insert/>
+    <update/>
+    <delete/>
+  </test>
+
+  <!-- target has no primary key -->
+  <test id="pre3">
+    <sql valid="t">SELECT * FROM sales_orga</sql>
+    <insert>unknown</insert>
+    <update/>
+    <delete/>
+  </test>
+
+  <test id="0">
+    <sql valid="t">SELECT id, name FROM customers</sql>
+    <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["id","name"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gchararray","is_param":false,"nullok":false}}]]}}}</insert>
+    <update>unknown</update>
+    <delete>unknown</delete>
+  </test>
+
+  <test id="0.1">
+    <sql valid="t">SELECT id, 3.14, name FROM customers</sql>
+    <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["id","name"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+2","descr":null,"type":"gchararray","is_param":false,"nullok":false}}]]}}}</insert>
+  </test>
+
+  <test id="0.2">
+    <sql valid="t">SELECT id, 3.14, name, id FROM customers</sql>
+    <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["id","name"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+2","descr":null,"type":"gchararray","is_param":false,"nullok":false}}]]}}}</insert>
+  </test>
+
+</testdata>

Modified: trunk/tools/gda-list-server-op.c
==============================================================================
--- trunk/tools/gda-list-server-op.c	(original)
+++ trunk/tools/gda-list-server-op.c	Wed Apr  2 20:22:59 2008
@@ -35,7 +35,7 @@
         g_option_context_free (context);
 
 	gda_init ("list-server-op", PACKAGE_VERSION, argc, argv);
-	xml_dir = gda_gbr_get_file_path (GDA_DATA_DIR, "libgda-3.0", NULL);
+	xml_dir = gda_gbr_get_file_path (GDA_DATA_DIR, "libgda-4.0", NULL);
 	g_print (_("Using XML descriptions in %s\n"), xml_dir);
 	if (prov)
 		g_print ("For provider %s\n", prov);



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