libgda r3190 - in trunk: . doc/C doc/C/tmpl libgda libgda/providers-support libgda/sql-parser libgda/sqlite providers/mysql providers/postgres providers/skel-implementation/capi tests tests/data-models tests/meta-store



Author: vivien
Date: Fri Aug 15 19:41:51 2008
New Revision: 3190
URL: http://svn.gnome.org/viewvc/libgda?rev=3190&view=rev

Log:
2008-08-15  Vivien Malerba <malerba gnome-db org>

	* providers/mysql: updates to the MySQL provider (Carlos Savoretti)
	* libgda/gda-set.c:
	* libgda/gda-connection.c:
	* libgda/sql-parser/gda-statement-struct.c: fixes memory leaks
	for bug #545977
	* libgda/gda-statement.c:
	* libgda/gda-util.c:
	* libgda/sql-parser/gda-statement-struct-pspec.[ch]: removed the "type" attribute from
	the GdaSqlParamSpec structure as it was redundant with the "g_type" one
	* providers/postgres/gda-postgres-provider.c:
	* providers/mysql/gda-mysql-provider.c:
	* providers/skel-implementation/capi/gda-capi-provider.c:
	* libgda/sqlite/gda-sqlite-provider.c: check that parameters are valid when executing
	a statement
	* libgda/gda-data-model.h: added GdaDataModel error code
	* libgda/gda-connection-private.h:
	* libgda/gda-connection.c:
	* libgda/sqlite/gda-sqlite-provider.c:
	* providers/mysql/gda-mysql-provider.c:
	* providers/postgres/gda-postgres-provider.c:
	* providers/skel-implementation/capi/gda-capi-provider.c:
	explicitely declare prepared statements as GdaPStmt objects
	instead of generic gpointer
	* tests/test-cnc-utils.[ch]: added a function to load data
	from a file into a table
	* tests/meta-store.c: test corrections due to meta store internal's schema changes
	* libgda/providers-support/gda-pmodel.[ch]:
	* libgda/sqlite/gda-sqlite-recordset.[ch]:
	* providers/mysql/gda-mysql-recordset.[ch]:
	* providers/postgres/gda-postgres-recordset.[ch]:
	* providers/skel-implementation/capi/gda-capi-recordset.[ch]:
	* tests/data-models/Makefile.am:
	* tests/data-models/check_pmodel.c: new test for the GdaPModel
	data model (the base class for all the data models returned
	by providers after a SELECT) for the updatable property
	* doc/C:
	* misc files: doc updates


Added:
   trunk/tests/data-models/check_pmodel.c
Modified:
   trunk/ChangeLog
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/tmpl/gda-pmodel.sgml
   trunk/doc/C/tmpl/gda-pstmt.sgml
   trunk/libgda/gda-connection-private.h
   trunk/libgda/gda-connection.c
   trunk/libgda/gda-data-model.c
   trunk/libgda/gda-data-model.h
   trunk/libgda/gda-meta-store.c
   trunk/libgda/gda-set.c
   trunk/libgda/gda-statement.c
   trunk/libgda/gda-util.c
   trunk/libgda/gda-value.c
   trunk/libgda/providers-support/gda-pmodel.c
   trunk/libgda/providers-support/gda-pmodel.h
   trunk/libgda/providers-support/gda-pstmt.c
   trunk/libgda/providers-support/gda-pstmt.h
   trunk/libgda/sql-parser/gda-statement-struct-pspec.c
   trunk/libgda/sql-parser/gda-statement-struct-pspec.h
   trunk/libgda/sql-parser/gda-statement-struct.c
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/libgda/sqlite/gda-sqlite-recordset.c
   trunk/libgda/sqlite/gda-sqlite-recordset.h
   trunk/libgda/sqlite/utils.c
   trunk/providers/mysql/gda-mysql-provider.c
   trunk/providers/mysql/gda-mysql-recordset.c
   trunk/providers/mysql/gda-mysql-recordset.h
   trunk/providers/mysql/libmain.c
   trunk/providers/postgres/gda-postgres-provider.c
   trunk/providers/postgres/gda-postgres-recordset.c
   trunk/providers/postgres/gda-postgres-recordset.h
   trunk/providers/skel-implementation/capi/gda-capi-provider.c
   trunk/providers/skel-implementation/capi/gda-capi-recordset.c
   trunk/providers/skel-implementation/capi/gda-capi-recordset.h
   trunk/tests/data-models/   (props changed)
   trunk/tests/data-models/Makefile.am
   trunk/tests/meta-store/common.c
   trunk/tests/meta-store/data_routines.csv
   trunk/tests/test-cnc-utils.c
   trunk/tests/test-cnc-utils.h

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	Fri Aug 15 19:41:51 2008
@@ -1161,6 +1161,7 @@
 <SECTION>
 <FILE>gda-sql-statement</FILE>
 <TITLE>GdaSqlStatement</TITLE>
+<INCLUDE>sql-parser/gda-sql-statement.h</INCLUDE>
 GdaSqlStatement
 GdaSqlStatementType
 gda_sql_statement_new
@@ -1383,8 +1384,8 @@
 gda_pmodel_take_row
 gda_pmodel_get_stored_row
 gda_pmodel_get_connection
-gda_pmodel_set_modification_query
-gda_pmodel_compute_modification_queries
+gda_pmodel_set_modification_statement
+gda_pmodel_compute_modification_statements
 <SUBSECTION Standard>
 GDA_IS_PMODEL
 GDA_PMODEL
@@ -1399,6 +1400,7 @@
 <INCLUDE>providers-support/gda-pstmt.h</INCLUDE>
 GdaPStmt
 gda_pstmt_set_gda_statement
+gda_pstmt_get_gda_statement
 gda_pstmt_copy_contents
 <SUBSECTION Standard>
 GDA_IS_PSTMT

Modified: trunk/doc/C/tmpl/gda-pmodel.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-pmodel.sgml	(original)
+++ trunk/doc/C/tmpl/gda-pmodel.sgml	Fri Aug 15 19:41:51 2008
@@ -41,7 +41,6 @@
 @prep_stmt: SELECT prepared statement for which the execution gave this object
 @nb_stored_rows: 
 @advertized_nrows: initially set to -1, set to a value >= 0 when the number of rows in the data model is known
- cnc: 
 
 <!-- ##### STRUCT GdaPModelClass ##### -->
 <para>
@@ -85,7 +84,7 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_pmodel_set_modification_query ##### -->
+<!-- ##### FUNCTION gda_pmodel_set_modification_statement ##### -->
 <para>
 
 </para>
@@ -96,14 +95,13 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_pmodel_compute_modification_queries ##### -->
+<!-- ##### FUNCTION gda_pmodel_compute_modification_statements ##### -->
 <para>
 
 </para>
 
 @model: 
- target: 
- use_all_fields_if_no_pk: 
+ require_pk: 
 @error: 
 @Returns: 
 

Modified: trunk/doc/C/tmpl/gda-pstmt.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-pstmt.sgml	(original)
+++ trunk/doc/C/tmpl/gda-pstmt.sgml	Fri Aug 15 19:41:51 2008
@@ -2,16 +2,32 @@
 GdaPStmt
 
 <!-- ##### SECTION Short_Description ##### -->
-Prepared statement base class
+Prepared statement's base class
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-
+  The #GdaPStmt represents the association between a #GdaStatement statement and a <emphasis>prepared statement</emphasis>
+  which is database dependant and is an in-memory representation of a statement. Using prepared statement has the
+  following advantages:
+  <itemizedlist>
+    <listitem><para>the parsing of the SQL has to be done only once, which improves performances if the statement
+	has to be executed more than once</para></listitem>
+    <listitem><para>if a statement has been prepared, then it means it is syntaxically correct and has been
+	<emphasis>understood</emphasis> by the database's API</para></listitem>
+    <listitem><para>it is possible to use variables in prepared statement which eliminates the risk
+	of SQL code injection</para></listitem>
+  </itemizedlist>
+</para>
+<para>
+  The #GdaPStmt is not intended to be instanciated, but subclassed by database provider's implementation.
+  Once created, the database provider's implementation can decide to associate (for future lookup) to
+  a #GdaStatement object in a connection using 
+  <link linkend="gda-connection-add-prepared-statement">gda_connection_add_prepared_statement()</link>.
 </para>
 
 <!-- ##### SECTION See_Also ##### -->
 <para>
-
+  
 </para>
 
 <!-- ##### SECTION Stability_Level ##### -->
@@ -23,7 +39,7 @@
 </para>
 
 @object: base object
- stmt: 
+ priv: 
 @sql: actual SQL code used for this prepared statement, its memory is freed by the object itself
 @param_ids: list of parameters' IDs (as gchar *), the memory is freed by object itself
 @ncols: number of columns in the returned data model (if the prepared statement is a SELECT statement)
@@ -39,6 +55,15 @@
 @stmt: 
 
 
+<!-- ##### FUNCTION gda_pstmt_get_gda_statement ##### -->
+<para>
+
+</para>
+
+ pstmt: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_pstmt_copy_contents ##### -->
 <para>
 

Modified: trunk/libgda/gda-connection-private.h
==============================================================================
--- trunk/libgda/gda-connection-private.h	(original)
+++ trunk/libgda/gda-connection-private.h	Fri Aug 15 19:41:51 2008
@@ -24,6 +24,7 @@
 #define __GDA_CONNECTION_PRIVATE_H_
 
 #include <libgda/gda-meta-store.h>
+#include <providers-support/gda-pstmt.h>
 
 G_BEGIN_DECLS
 
@@ -52,9 +53,9 @@
 /* 
  * prepared statements support
  */
-void     gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, gpointer prepared_stmt); 
-void     gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt); 
-gpointer gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
+void      gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, GdaPStmt *prepared_stmt); 
+void      gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt); 
+GdaPStmt *gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt);
 
 G_END_DECLS
 

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Fri Aug 15 19:41:51 2008
@@ -264,6 +264,7 @@
 
 	if (cnc->priv->events_list) {
 		g_list_foreach (cnc->priv->events_list, (GFunc) g_object_unref, NULL);
+		g_slist_free (cnc->priv->events_list);
 		cnc->priv->events_list = NULL;
 	}
 
@@ -1269,6 +1270,7 @@
 	gda_connection_lock ((GdaLockable*) cnc);
 	if (cnc->priv->events_list != NULL) {
 		g_list_foreach (cnc->priv->events_list, (GFunc) g_object_unref, NULL);
+		g_list_free (cnc->priv->events_list);
 		cnc->priv->events_list =  NULL;
 	}
 	gda_connection_unlock ((GdaLockable*) cnc);
@@ -3694,11 +3696,25 @@
 	gda_connection_unlock ((GdaLockable*) cnc);
 }
 
+
+/**
+ * gda_connection_add_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ * @prepared_stmt: a prepared statement object (as a #GdaPStmt object, or more likely a descendant)
+ *
+ * Declares that @prepared_stmt is a prepared statement object associated to @gda_stmt within the connection
+ * (meaning the connection increments the reference counter of @prepared_stmt).
+ *
+ * If @gda_stmt changes or is destroyed, the the association will be lost and the connection will lose the
+ * reference it has on @prepared_stmt.
+ */
 void 
-gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, gpointer prepared_stmt)
+gda_connection_add_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt, GdaPStmt *prepared_stmt)
 {
 	g_return_if_fail (GDA_IS_CONNECTION (cnc));
 	g_return_if_fail (cnc->priv);
+	g_return_if_fail (GDA_IS_PSTMT (prepared_stmt));
 
 	gda_connection_lock ((GdaLockable*) cnc);
 
@@ -3714,10 +3730,20 @@
 	gda_connection_unlock ((GdaLockable*) cnc);
 }
 
-gpointer
+/**
+ * gda_connection_get_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ *
+ * Retreives a pointer to an object representing a prepared statement for @gda_stmt within @cnc. The
+ * association must have been done using gda_connection_add_prepared_statement().
+ *
+ * Returns: the prepared statement, or %NULL if no association exists
+ */
+GdaPStmt *
 gda_connection_get_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt)
 {
-	gpointer retval = NULL;
+	GdaPStmt *retval = NULL;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 	g_return_val_if_fail (cnc->priv, NULL);
@@ -3730,6 +3756,14 @@
 	return retval;
 }
 
+/**
+ * gda_connection_del_prepared_statement
+ * @cnc: a #GdaConnection object
+ * @gda_stmt: a #GdaStatement object
+ *
+ * Removes any prepared statement associated to @gda_stmt in @cnc: this undoes what
+ * gda_connection_add_prepared_statement() does.
+ */
 void
 gda_connection_del_prepared_statement (GdaConnection *cnc, GdaStatement *gda_stmt)
 {
@@ -3738,7 +3772,8 @@
 
 	gda_connection_lock ((GdaLockable*) cnc);
 	g_return_if_fail (GDA_IS_CONNECTION (cnc));
-	prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
+	if (gda_connection_get_prepared_statement (cnc, gda_stmt))
+		prepared_stms_stmt_destroyed_cb (gda_stmt, cnc);
 	gda_connection_unlock ((GdaLockable*) cnc);
 }
 

Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c	(original)
+++ trunk/libgda/gda-data-model.c	Fri Aug 15 19:41:51 2008
@@ -55,6 +55,15 @@
 
 static guint gda_data_model_signals[LAST_SIGNAL];
 
+/* module error */
+GQuark gda_data_model_error_quark (void)
+{
+        static GQuark quark;
+        if (!quark)
+                quark = g_quark_from_static_string ("gda_data_model_error");
+        return quark;
+}
+
 GType
 gda_data_model_get_type (void)
 {
@@ -552,7 +561,8 @@
  * occur if you do so). Also that value may become invalid as soon as any Libgda part is executed again,
  * which means if you want to keep the value, a copy must be made. 
  *
- * If you want to modify a value stored in a #GdaDataModel, use the gda_data_model_set_value() method.
+ * If you want to modify a value stored in a #GdaDataModel, use the gda_data_model_set_value_at() or 
+ * gda_data_model_set_values() methods.
  *
  * Returns: a #GValue containing the value stored in the given
  * position, or %NULL on error (out-of-bound position, etc).
@@ -939,7 +949,8 @@
  * @model: a #GdaDataModel object.
  * @error: a place to store errors, or %NULL
  * 
- * Appends a row to the data model. 
+ * Appends a row to the data model (the new row will possibliy have NULL values for all columns,
+ * or some other values depending on the data model implementation)
  *
  * Returns: the number of the added row, or -1 if an error occurred
  */

Modified: trunk/libgda/gda-data-model.h
==============================================================================
--- trunk/libgda/gda-data-model.h	(original)
+++ trunk/libgda/gda-data-model.h	Fri Aug 15 19:41:51 2008
@@ -39,6 +39,10 @@
 #define GDA_IS_DATA_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, GDA_TYPE_DATA_MODEL))
 #define GDA_DATA_MODEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GDA_TYPE_DATA_MODEL, GdaDataModelClass))
 
+/* error reporting */
+extern GQuark gda_data_model_error_quark (void);
+#define GDA_DATA_MODEL_ERROR gda_data_model_error_quark ()
+
 typedef enum {
 	GDA_DATA_MODEL_ACCESS_RANDOM = 1 << 0,
 	GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD = 1 << 1,

Modified: trunk/libgda/gda-meta-store.c
==============================================================================
--- trunk/libgda/gda-meta-store.c	(original)
+++ trunk/libgda/gda-meta-store.c	Fri Aug 15 19:41:51 2008
@@ -1316,7 +1316,6 @@
                         ptype = ctype ? gda_g_type_from_string ((gchar *) ctype) : G_TYPE_STRING;
                         pspec->name = g_strdup_printf ("+%d", colindex);
                         pspec->g_type = ptype;
-                        pspec->type = g_strdup (ctype ? (gchar *) ctype : "string");
                         pspec->nullok = nullok;
                         expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ist));
                         expr->param_spec = pspec;
@@ -1325,7 +1324,6 @@
                         pspec = g_new0 (GdaSqlParamSpec, 1);
                         pspec->name = g_strdup_printf ("+%d", colindex);
                         pspec->g_type = ptype;
-                        pspec->type = g_strdup (ctype ? (gchar *) ctype : "string");
                         pspec->nullok = nullok;
                         expr = gda_sql_expr_new (GDA_SQL_ANY_PART (ust));
                         expr->param_spec = pspec;
@@ -1589,7 +1587,6 @@
 	pspec = g_new0 (GdaSqlParamSpec, 1);
 	pspec->name = g_strdup_printf ("-%d", index);
 	pspec->g_type = ptype;
-	pspec->type = g_strdup (type ? (gchar *) type : "string");
 	pspec->nullok = nullok;
 	expr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
 	expr->param_spec = pspec;

Modified: trunk/libgda/gda-set.c
==============================================================================
--- trunk/libgda/gda-set.c	(original)
+++ trunk/libgda/gda-set.c	Fri Aug 15 19:41:51 2008
@@ -312,6 +312,7 @@
 
 	copy = g_object_new (GDA_TYPE_SET, "holders", holders, NULL);
 	g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
+	g_slist_free (holders);
 
 	return copy;
 }

Modified: trunk/libgda/gda-statement.c
==============================================================================
--- trunk/libgda/gda-statement.c	(original)
+++ trunk/libgda/gda-statement.c	Fri Aug 15 19:41:51 2008
@@ -477,8 +477,6 @@
 	    (pspec = ((GdaSqlExpr*) node)->param_spec)) {
 		GdaHolder *h;
 
-		if ((pspec->g_type == 0) && pspec->type)
-			pspec->g_type = gda_g_type_from_string (pspec->type);
 		if (pspec->g_type == 0) {
 			g_set_error (error, GDA_STATEMENT_ERROR, GDA_STATEMENT_PARAM_TYPE_ERROR,
 				     _("Could not determine GType for parameter '%s'"),
@@ -1246,13 +1244,13 @@
 			
 			g_string_append (string, " /* ");
 			g_string_append_printf (string, "name:%s", quoted_pname);
-			if (pspec->type) {
-				str = _add_quotes (pspec->type);
+			if (pspec->g_type) {
+				str = _add_quotes (gda_g_type_to_string (pspec->g_type));
 				g_string_append_printf (string, " type:%s", str);
 				g_free (str);
 			}
 			if (pspec->descr) {
-				str = _add_quotes (pspec->type);
+				str = _add_quotes (pspec->descr);
 				g_string_append_printf (string, " descr:%s", str);
 				g_free (str);
 			}
@@ -1264,9 +1262,9 @@
 		else {
 			g_string_append (string, "##");
 			g_string_append (string, pspec->name);
-			if (pspec->type) {
+			if (pspec->g_type != G_TYPE_INVALID) {
 				g_string_append (string, "::");
-				g_string_append (string, pspec->type);
+				g_string_append (string, gda_g_type_to_string (pspec->g_type));
 				if (pspec->nullok) 
 					g_string_append (string, "::NULL");
 			}

Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c	(original)
+++ trunk/libgda/gda-util.c	Fri Aug 15 19:41:51 2008
@@ -714,7 +714,6 @@
 				pspec = g_new0 (GdaSqlParamSpec, 1);
 				pspec->name = g_strdup_printf ("-%d", mtable->pk_cols_array[i]);
 				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;
 				opexpr->param_spec = pspec;
 				op->operands = g_slist_append (op->operands, opexpr);
@@ -851,7 +850,6 @@
 			GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
 			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;
@@ -861,7 +859,6 @@
 			GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
 			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;

Modified: trunk/libgda/gda-value.c
==============================================================================
--- trunk/libgda/gda-value.c	(original)
+++ trunk/libgda/gda-value.c	Fri Aug 15 19:41:51 2008
@@ -1834,7 +1834,7 @@
  * @value1: a #GValue to compare.
  * @value2: the other #GValue to be compared to @value1.
  *
- * Tells if two values are equal or not
+ * Tells if two values are equal or not, by comparing memory representations.
  *
  * Returns: 0 if @value1 and @value2 are equal, and something else otherwise
  */

Modified: trunk/libgda/providers-support/gda-pmodel.c
==============================================================================
--- trunk/libgda/providers-support/gda-pmodel.c	(original)
+++ trunk/libgda/providers-support/gda-pmodel.c	Fri Aug 15 19:41:51 2008
@@ -31,23 +31,26 @@
 #include <libgda/gda-statement.h>
 #include <libgda/gda-holder.h>
 #include <libgda/gda-connection.h>
+#include <libgda/gda-util.h>
+#include <sql-parser/gda-sql-statement.h>
 
 #define CLASS(x) (GDA_PMODEL_CLASS (G_OBJECT_GET_CLASS (x)))
 
-enum
+typedef enum
 {
 	FIRST_QUERY = 0,
         INS_QUERY  = 0,
         UPD_QUERY  = 1,
         DEL_QUERY  = 2,
 	NB_QUERIES = 3
-};
+} ModType;
 
 /*
  * Getting a GdaRow from a model row:
  * model row ==(model->index)==> model->rows index ==(model->rows)==> GdaRow
  */
 struct _GdaPModelPrivate {
+	GdaConnection          *cnc;
 	GSList                 *columns; /* list of GdaColumn objects */
 	GArray                 *rows; /* Array of GdaRow pointers */
 	GHashTable             *index; /* key = model row number + 1, value = index in @rows array + 1*/
@@ -58,7 +61,11 @@
 
 	GdaDataModelAccessFlags usage_flags;
 	
+	GdaSet                 *exec_set; /* owned by this object (copied) */
+	GdaSet                 *modif_set; /* owned by this object */
 	GdaStatement           *modif_stmts[NB_QUERIES];
+
+	GdaStatement          **upd_stmts; /* one stmt per data model column */
 };
 
 /* properties */
@@ -69,11 +76,21 @@
 	PROP_PREP_STMT,
 	PROP_FLAGS,
 	PROP_ALL_STORED,
+	PROP_PARAMS,
 	PROP_INS_QUERY,
 	PROP_UPD_QUERY,
 	PROP_DEL_QUERY
 };
 
+/* module error */
+GQuark gda_pmodel_error_quark (void)
+{
+        static GQuark quark;
+        if (!quark)
+                quark = g_quark_from_static_string ("gda_pmodel_error");
+        return quark;
+}
+
 static void gda_pmodel_class_init (GdaPModelClass *klass);
 static void gda_pmodel_init       (GdaPModel *model, GdaPModelClass *klass);
 static void gda_pmodel_dispose    (GObject *object);
@@ -102,6 +119,13 @@
 static gboolean             gda_pmodel_iter_prev       (GdaDataModel *model, GdaDataModelIter *iter);
 static gboolean             gda_pmodel_iter_at_row     (GdaDataModel *model, GdaDataModelIter *iter, gint row);
 
+static gboolean             gda_pmodel_set_value_at    (GdaDataModel *model, gint col, gint row, 
+							const GValue *value, GError **error);
+static gboolean             gda_pmodel_set_values      (GdaDataModel *model, gint row, GList *values,
+							GError **error);
+static gint                 gda_pmodel_append_values   (GdaDataModel *model, const GList *values, GError **error);
+static gboolean             gda_pmodel_remove_row      (GdaDataModel *model, gint row, GError **error);
+
 static GObjectClass *parent_class = NULL;
 
 /**
@@ -136,7 +160,7 @@
 
 		g_static_mutex_lock (&registering);
 		if (type == 0) {
-			type = g_type_register_static (G_TYPE_OBJECT, "GdaPModel", &info, 0);
+			type = g_type_register_static (G_TYPE_OBJECT, "GdaPModel", &info, G_TYPE_FLAG_ABSTRACT);
 			g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
 		}
 		g_static_mutex_unlock (&registering);
@@ -171,6 +195,11 @@
 					 g_param_spec_boolean ("store-all-rows", "Store all the rows",
 							       "Tells if model has analysed all the rows", FALSE,
 							       G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class, PROP_PARAMS,
+					 g_param_spec_object ("exec-params", NULL, 
+							      _("GdaSet used when the SELECT statement was executed"), 
+							      GDA_TYPE_SET,
+							      G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (object_class, PROP_INS_QUERY,
                                          g_param_spec_object ("insert_query", "INSERT query", 
 							      "INSERT Query to be executed to add data",
@@ -209,11 +238,11 @@
         iface->i_iter_next = gda_pmodel_iter_next;
         iface->i_iter_prev = gda_pmodel_iter_prev;
 
-	iface->i_set_value_at = NULL;
-	iface->i_set_values = NULL;
-        iface->i_append_values = NULL;
+	iface->i_set_value_at = gda_pmodel_set_value_at;
+	iface->i_set_values = gda_pmodel_set_values;
+        iface->i_append_values = gda_pmodel_append_values;
 	iface->i_append_row = NULL;
-	iface->i_remove_row = NULL;
+	iface->i_remove_row = gda_pmodel_remove_row;
 	iface->i_find_row = NULL;
 	
 	iface->i_set_notify = NULL;
@@ -226,7 +255,7 @@
 {
 	g_return_if_fail (GDA_IS_PMODEL (model));
 	model->priv = g_new0 (GdaPModelPrivate, 1);
-	model->cnc = NULL;
+	model->priv->cnc = NULL;
 	model->priv->rows = g_array_new (FALSE, FALSE, sizeof (GdaRow *));
 	model->priv->index = g_hash_table_new (g_direct_hash, g_direct_equal);
 	model->prep_stmt = NULL;
@@ -235,6 +264,10 @@
 
 	model->priv->iter_row = G_MININT;
         model->priv->iter = NULL;
+
+	model->priv->modif_set = NULL;
+	model->priv->exec_set = NULL;
+	model->priv->upd_stmts = NULL;
 }
 
 static void
@@ -247,9 +280,10 @@
 	/* free memory */
 	if (model->priv) {
 		gint i;
-		if (model->cnc) {
-			g_object_unref (model->cnc);
-			model->cnc = NULL;
+
+		if (model->priv->cnc) {
+			g_object_unref (model->priv->cnc);
+			model->priv->cnc = NULL;
 		}
 		if (model->prep_stmt) {
 			g_object_unref (model->prep_stmt);
@@ -274,12 +308,30 @@
 			model->priv->columns = NULL;
 		}
 
+		if (model->priv->modif_set) {
+			g_object_unref (model->priv->modif_set);
+			model->priv->modif_set = NULL;
+		}
+
+		if (model->priv->exec_set) {
+			g_object_unref (model->priv->exec_set);
+			model->priv->exec_set = NULL;
+		}
+
 		for (i = FIRST_QUERY; i < NB_QUERIES; i++) {
 			if (model->priv->modif_stmts [i]) {
 				g_object_unref (model->priv->modif_stmts [i]);
 				model->priv->modif_stmts [i] = NULL;
 			}
 		}
+		if (model->priv->upd_stmts) {
+			gint j, ncols;
+			ncols = gda_pmodel_get_n_columns ((GdaDataModel *) model);
+			for (j = 0; j < ncols; j++)
+				if (model->priv->upd_stmts [j])
+					g_object_unref (model->priv->upd_stmts [j]);
+			g_free (model->priv->upd_stmts);
+		}
 	}
 
 	/* chain to parent class */
@@ -346,9 +398,9 @@
 	if (model->priv) {
 		switch (param_id) {
 		case PROP_CNC:
-			model->cnc = g_value_get_object (value);
-			if (model->cnc)
-				g_object_ref (model->cnc);
+			model->priv->cnc = g_value_get_object (value);
+			if (model->priv->cnc)
+				g_object_ref (model->priv->cnc);
 			break;
 		case PROP_PREP_STMT:
 			if (model->prep_stmt)
@@ -375,6 +427,13 @@
 					CLASS (model)->store_all (model, NULL);
 			}
 			break;
+		case PROP_PARAMS: {
+			GdaSet *set;
+			set = g_value_get_object (value);
+			if (set) 
+				model->priv->exec_set = gda_set_copy (set);
+			break;
+		}
 		case PROP_INS_QUERY:
 			if (model->priv->modif_stmts [INS_QUERY])
 				g_object_unref (model->priv->modif_stmts [INS_QUERY]);
@@ -412,7 +471,7 @@
 	if (model->priv) {
 		switch (param_id) {
 		case PROP_CNC:
-			g_value_set_object (value, model->cnc);
+			g_value_set_object (value, model->priv->cnc);
 			break;
 		case PROP_PREP_STMT:
 			g_value_set_object (value, model->prep_stmt);
@@ -429,6 +488,9 @@
 				g_value_set_boolean (value, model->nb_stored_rows == model->advertized_nrows);
 			}
 			break;
+		case PROP_PARAMS:
+			g_value_set_object (value, model->priv->exec_set);
+			break;
 		case PROP_INS_QUERY:
 			g_value_set_object (value, model->priv->modif_stmts [INS_QUERY]);
 			break;
@@ -505,7 +567,78 @@
 	g_return_val_if_fail (GDA_IS_PMODEL (model), NULL);
 	g_return_val_if_fail (model->priv, NULL);
 
-	return model->cnc;
+	return model->priv->cnc;
+}
+
+/*
+ * Add the +/-<col num> holders to model->priv->modif_set
+ */
+static gboolean
+compute_modif_set (GdaPModel *model, GError **error)
+{
+	gint i;
+	
+	if (model->priv->modif_set)
+		g_object_unref (model->priv->modif_set);
+	if (model->priv->exec_set)
+		model->priv->modif_set = gda_set_copy (model->priv->exec_set);
+	else
+		model->priv->modif_set = gda_set_new (NULL);
+
+	for (i = 0; i < NB_QUERIES; i++) {
+		GdaSet *set;
+		if (! model->priv->modif_stmts [i])
+			continue;
+		if (! gda_statement_get_parameters (model->priv->modif_stmts [i], &set, error)) {
+			g_object_unref (model->priv->modif_set);
+			model->priv->modif_set = NULL;
+			return FALSE;
+		}
+
+		gda_set_merge_with_set (model->priv->modif_set, set);
+		g_object_unref (set);
+	}
+
+#ifdef GDA_DEBUG_NO
+	GSList *list;
+	g_print ("-------\n");
+	for (list = model->priv->modif_set->holders; list; list = list->next) {
+		GdaHolder *h = GDA_HOLDER (list->data);
+		g_print ("=> holder '%s'\n", gda_holder_get_id (h));
+	}
+#endif
+
+	return TRUE;
+}
+
+/*
+ * converts "[+-]<num>" to <num> and returns FALSE if @pname is not like
+ * "[+-]<num>". <num> is stored in @result, and the +/- signed is stored in @old_val
+ * (@old_val is set to TRUE if there is a "-")
+ */
+static gboolean
+param_name_to_int (const gchar *pname, gint *result, gboolean *old_val)
+{
+	gint sum = 0;
+	const gchar *ptr;
+
+	if (!pname || ((*pname != '-') && (*pname != '+')))
+		return FALSE;
+	
+	ptr = pname + 1;
+	while (*ptr) {
+		if ((*ptr > '9') || (*ptr < '0'))
+			return FALSE;
+		sum = sum * 10 + *ptr - '0';
+		ptr++;
+	}
+	
+	if (result) 
+		*result = sum;
+	if (old_val)
+		*old_val = (*pname == '-') ? TRUE : FALSE;
+	
+	return TRUE;
 }
 
 /**
@@ -520,39 +653,158 @@
  * Returns: TRUE if no error occurred.
  */
 gboolean
-gda_pmodel_set_modification_query (GdaPModel *model, GdaStatement *mod_stmt, GError **error)
+gda_pmodel_set_modification_statement (GdaPModel *model, GdaStatement *mod_stmt, GError **error)
 {
+	ModType mtype = NB_QUERIES;
+
 	g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
 	g_return_val_if_fail (model->priv, FALSE);
 	g_return_val_if_fail (GDA_IS_STATEMENT (mod_stmt), FALSE);
 
-	TO_IMPLEMENT;
+	switch (gda_statement_get_statement_type (mod_stmt)) {
+	case GDA_SQL_STATEMENT_INSERT:
+		mtype = INS_QUERY;
+		break;
+	case GDA_SQL_STATEMENT_DELETE:
+		mtype = DEL_QUERY;
+		break;
+	case GDA_SQL_STATEMENT_UPDATE:
+		mtype = UPD_QUERY;
+		break;
+	default:
+		break;
+	}
+	
+	if (mtype != NB_QUERIES) {
+		if (! gda_sql_statement_check_structure (mod_stmt, error))
+			return FALSE;
 
-	return FALSE;
+		if (model->priv->modif_stmts[mtype]) {
+			g_object_unref (model->priv->modif_stmts[mtype]);
+			model->priv->modif_stmts[mtype] = NULL;
+		}
+
+		/* prepare model->priv->modif_set */
+		if (!compute_modif_set (model, error))
+			return FALSE;
+
+		/* check that all the parameters required to execute @mod_stmt are in model->priv->modif_set */
+		GdaSet *params;
+		GSList *list;
+		if (! gda_statement_get_parameters (mod_stmt, &params, error))
+			return FALSE;
+		for (list = params->holders; list; list = list->next) {
+			GdaHolder *holder = GDA_HOLDER (list->data);
+			GdaHolder *eholder;
+			eholder = gda_set_get_holder (model->priv->modif_set, gda_holder_get_id (holder));
+			if (!eholder) {
+				gint num;
+				gboolean is_old;
+
+				if (!param_name_to_int (gda_holder_get_id (holder), &num, &is_old)) {
+					g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+						     _("Modification statement uses an unknown '%s' parameter"),
+						     gda_holder_get_id (holder));
+					g_object_unref (params);
+					return FALSE;
+				}
+				if (num > gda_pmodel_get_n_columns ((GdaDataModel*) model)) {
+					g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+						     _("Column %d out of range (0-%d)"), num, 
+						     gda_pmodel_get_n_columns ((GdaDataModel*) model)-1);
+					g_object_unref (params);
+					return FALSE;
+				}
+				gda_set_add_holder (model->priv->modif_set, holder);
+			}
+			else if (gda_holder_get_g_type (holder) != gda_holder_get_g_type (eholder)) {
+				g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+					     _("Modification statement's  '%s' parameter is a %s when it should be a %s"),
+					     gda_holder_get_id (holder),
+					     gda_g_type_to_string (gda_holder_get_g_type (holder)),
+					     gda_g_type_to_string (gda_holder_get_g_type (eholder)));
+				g_object_unref (params);
+				return FALSE;
+			}
+		}
+		g_object_unref (params);
+
+		model->priv->modif_stmts[mtype] = mod_stmt;
+		g_object_ref (mod_stmt);
+	}
+	else {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+			     _("Modification statement must be an INSERT, UPDATE or DELETE statement"));
+		return FALSE;
+	}
+
+#ifdef GDA_DEBUG_NO
+	GSList *hlist;
+	g_print ("SET MODIF QUERY\n");
+	if (model->priv->modif_set) {
+		for (hlist = model->priv->modif_set->holders; hlist; hlist = hlist->next) {
+			GdaHolder *h = GDA_HOLDER (hlist->data);
+			g_print ("  %s type=> %s (%d)\n", gda_holder_get_id (h), g_type_name (gda_holder_get_g_type (h)),
+				 gda_holder_get_g_type (h));
+		}
+	}
+#endif
+
+	return TRUE;
 }
 
 /**
  * gda_pmodel_compute_modification_queries
  * @model: a #GdaPModel data model
- * @target: the name of the target to modify (a table name or alias)
- * @use_all_fields_if_no_pk: set to TRUE if all fields must be used in the WHERE condition when no primary key exists
+ * @require_pk: FALSE if all fields can be used in the WHERE condition when no primary key exists
  * @error: a place to store errors, or %NULL
  *
- * Makes @model try to compute INSERT, UPDATE and DELETE statements to be used when modifying @model's contents
+ * Makes @model try to compute INSERT, UPDATE and DELETE statements to be used when modifying @model's contents.
+ * Note: any modification statement set using gda_pmodel_set_modification_statement() will first be unset
  *
- * Returns: TRUE if no error occurred.
+ * Returns: TRUE if no error occurred. If FALSE is returned, then some modification statement may still have been
+ * computed
  */
 gboolean
-gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target, gboolean use_all_fields_if_no_pk, GError **error)
+gda_pmodel_compute_modification_statements (GdaPModel *model, gboolean require_pk, GError **error)
 {
+	GdaStatement *stmt;
+	ModType mtype;
 	g_return_val_if_fail (GDA_IS_PMODEL (model), FALSE);
 	g_return_val_if_fail (model->priv, FALSE);
 
-	TO_IMPLEMENT;
-
-	return FALSE;
+	stmt = gda_pstmt_get_gda_statement (model->prep_stmt);
+	if (!stmt) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("No SELECT statement to use"));
+		return FALSE;
+	}
+	if (!model->priv->cnc) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_CONNECTION_ERROR,
+			     _("No connection to use"));
+		return FALSE;
+	}
+	for (mtype = FIRST_QUERY; mtype < NB_QUERIES; mtype++)
+		if (model->priv->modif_stmts[mtype]) {
+			g_object_unref (model->priv->modif_stmts[mtype]);
+			model->priv->modif_stmts[mtype] = NULL;
+		}
+	return gda_compute_dml_statements (model->priv->cnc, stmt, require_pk,
+					   &(model->priv->modif_stmts[INS_QUERY]),
+					   &(model->priv->modif_stmts[UPD_QUERY]),
+					   &(model->priv->modif_stmts[DEL_QUERY]), error);
+	if (!compute_modif_set (model, error)) {
+		for (mtype = FIRST_QUERY; mtype < NB_QUERIES; mtype++)
+			if (model->priv->modif_stmts[mtype]) {
+				g_object_unref (model->priv->modif_stmts[mtype]);
+				model->priv->modif_stmts[mtype] = NULL;
+			}
+		return FALSE;
+	}
+	return TRUE;
 }
 
+
 /*
  * GdaDataModel interface implementation
  */
@@ -601,18 +853,29 @@
 gda_pmodel_get_access_flags (GdaDataModel *model)
 {
 	GdaPModel *imodel;
+	GdaDataModelAccessFlags flags = 0;
+
 	g_return_val_if_fail (GDA_IS_PMODEL (model), 0);
 	imodel = GDA_PMODEL (model);
 	g_return_val_if_fail (imodel->priv, 0);
 
 	if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
-		return GDA_DATA_MODEL_ACCESS_RANDOM;
+		flags = GDA_DATA_MODEL_ACCESS_RANDOM;
 	else if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD) {
 		if (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)
-			return GDA_DATA_MODEL_ACCESS_CURSOR;
-		return GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
+			flags = GDA_DATA_MODEL_ACCESS_CURSOR;
+		else
+			flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 	}
-	g_assert_not_reached ();
+
+	if (imodel->priv->modif_stmts [UPD_QUERY])
+		flags |= GDA_DATA_MODEL_ACCESS_UPDATE;
+	if (imodel->priv->modif_stmts [INS_QUERY])
+		flags |= GDA_DATA_MODEL_ACCESS_INSERT;
+	if (imodel->priv->modif_stmts [DEL_QUERY])
+		flags |= GDA_DATA_MODEL_ACCESS_DELETE;
+
+	return flags;
 }
 
 static const GValue *
@@ -660,8 +923,8 @@
 	imodel = (GdaPModel *) model;
 	g_return_val_if_fail (imodel->priv, 0);
 	
-	/*FIXME: depending on modification queries being set or not */
-	flags = GDA_VALUE_ATTR_NO_MODIF;
+	if (! imodel->priv->modif_stmts [UPD_QUERY])
+		flags = GDA_VALUE_ATTR_NO_MODIF;
 	
 	return flags;
 }
@@ -856,3 +1119,210 @@
 	if (update_model)
 		g_object_set (G_OBJECT (iter), "update_model", update_model, NULL);
 }
+
+/*
+ * creates a derivative of the model->priv->modif_stmts [UPD_QUERY] statement
+ * where only the column @col is updated.
+ *
+ * Returns: a new #GdaStatement, or %NULL
+ */
+static GdaStatement *
+compute_single_update_stmt (GdaPModel *model, gint col, GError **error)
+{
+	GdaSqlStatement *sqlst;
+	GdaSqlStatementUpdate *upd;
+	GdaStatement *updstmt = NULL;
+
+	/* get a copy of complete UPDATE stmt */
+	g_assert (model->priv->modif_stmts [UPD_QUERY]);
+	g_object_get (G_OBJECT (model->priv->modif_stmts [UPD_QUERY]), "structure", &sqlst, NULL);
+	g_assert (sqlst);
+	g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_UPDATE);
+	upd = (GdaSqlStatementUpdate*) sqlst->contents;
+	
+	/* remove non necessary fields to update */
+	GSList *elist, *flist;
+	gboolean field_found = FALSE;
+	for (elist = upd->expr_list, flist = upd->fields_list; elist && flist; ) {
+		GdaSqlExpr *expr = (GdaSqlExpr *) elist->data;
+		gint num;
+		gboolean old_val;
+		if (! expr->param_spec || !param_name_to_int (expr->param_spec->name, &num, &old_val) || old_val) {
+			/* ignore this field to be updated */
+			elist = elist->next;
+			flist = flist->next;
+			continue;
+		}
+		if (num == col) {
+			/* this field is the field to be updated */
+			field_found = TRUE;
+			elist = elist->next;
+			flist = flist->next;
+			continue;
+		}
+		/* remove that field */
+		GSList *nelist, *nflist;
+		nelist = elist->next;
+		nflist = flist->next;
+		gda_sql_expr_free (expr);
+		gda_sql_field_free ((GdaSqlField*) flist->data);
+
+		upd->expr_list = g_slist_delete_link (upd->expr_list, elist);
+		upd->fields_list = g_slist_delete_link (upd->fields_list, flist);
+		elist = nelist;
+		flist = nflist;
+	}
+
+	/* create a new GdaStatement */
+	if (field_found)
+		updstmt = (GdaStatement *) g_object_new (GDA_TYPE_STATEMENT, "structure", sqlst, NULL);
+	else 
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("Column %d can't be modified"), col);
+	gda_sql_statement_free (sqlst);
+
+#ifdef GDA_DEBUG_NO
+	if (updstmt) {
+		gchar *sql;
+		sql = gda_statement_serialize (updstmt);
+		g_print ("UPDATE for col %d => %s\n", col, sql);
+		g_free (sql);
+	}
+	else
+		g_print ("UPDATE for col %d: ERROR\n", col);
+#endif
+
+	return updstmt;
+}
+
+static gboolean
+gda_pmodel_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *value, GError **error)
+{
+	GdaPModel *imodel;
+	gint i, ncols;
+	GdaHolder *holder;
+	gchar *str;
+
+	imodel = (GdaPModel *) model;
+
+	g_return_val_if_fail (imodel->priv, FALSE);
+
+	if (! (imodel->priv->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)) {
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
+			     _("Data model does not support random access"));
+		return FALSE;
+	}
+	if (! imodel->priv->modif_stmts [UPD_QUERY]) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("No UPDATE statement provided"));
+		return FALSE;
+	}
+
+	if (row >= gda_pmodel_get_n_rows (model)) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("Row %d out of range (0-%d)"), row, gda_pmodel_get_n_rows (model)-1);
+		return FALSE;
+	}
+
+	ncols = gda_pmodel_get_n_columns (model);
+	if (col >= ncols) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("Column %d out of range (0-%d)"), col, ncols-1);
+		return FALSE;
+	}
+
+	if (! imodel->priv->upd_stmts)
+		imodel->priv->upd_stmts = g_new0 (GdaStatement *, ncols);
+	if (! imodel->priv->upd_stmts [col]) {
+		imodel->priv->upd_stmts [col] = compute_single_update_stmt (imodel, col, error);
+		if (!imodel->priv->upd_stmts [col])
+			return FALSE;
+	}
+	
+	str = g_strdup_printf ("+%d", col);
+	holder = gda_set_get_holder (imodel->priv->modif_set, str);
+	g_free (str);
+	if (! holder) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+			     _("Column %d can't be modified"), col);
+		return FALSE;
+	}
+	if (! gda_holder_set_value (holder, value)) {
+		g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_VALUE_ERROR,
+			     _("Wrong value type"));
+		return FALSE;
+	}
+
+	for (i = 0; i < ncols; i++) {
+		str = g_strdup_printf ("-%d", i);
+		holder = gda_set_get_holder (imodel->priv->modif_set, str);
+		g_free (str);
+		if (holder) {
+			if (! gda_holder_set_value (holder,
+						    gda_data_model_get_value_at (model, i, row))) {
+				g_set_error (error, GDA_PMODEL_ERROR, GDA_PMODEL_VALUE_ERROR,
+					     _("Wrong value type"));
+				return FALSE;
+			}
+		}
+	}
+
+#ifdef GDA_DEBUG_NO
+	gchar *sql;
+	GError *lerror = NULL;
+	sql = gda_statement_to_sql_extended (imodel->priv->upd_stmts [col],
+					     imodel->priv->cnc,
+					     imodel->priv->modif_set, 
+					     GDA_STATEMENT_SQL_PRETTY, NULL,
+					     &lerror);
+	g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
+	if (!sql)
+		g_print ("\tERR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+	g_free (sql);
+#endif
+
+	if (gda_connection_statement_execute_non_select (imodel->priv->cnc, 
+							 imodel->priv->upd_stmts [col],
+							 imodel->priv->modif_set, NULL, error) == -1)
+		return FALSE;
+	
+	TO_IMPLEMENT;
+
+	return TRUE;
+}
+
+static gboolean
+gda_pmodel_set_values (GdaDataModel *model, gint row, GList *values, GError **error)
+{
+	GdaPModel *imodel;
+	imodel = (GdaPModel *) model;
+
+	g_return_val_if_fail (imodel->priv, FALSE);
+	TO_IMPLEMENT;
+
+	return FALSE;
+}
+
+static gint
+gda_pmodel_append_values (GdaDataModel *model, const GList *values, GError **error)
+{
+	GdaPModel *imodel;
+	imodel = (GdaPModel *) model;
+
+	g_return_val_if_fail (imodel->priv, -1);
+	TO_IMPLEMENT;
+
+	return -1;
+}
+
+static gboolean
+gda_pmodel_remove_row (GdaDataModel *model, gint row, GError **error)
+{
+	GdaPModel *imodel;
+	imodel = (GdaPModel *) model;
+
+	g_return_val_if_fail (imodel->priv, FALSE);
+	TO_IMPLEMENT;
+
+	return FALSE;
+}

Modified: trunk/libgda/providers-support/gda-pmodel.h
==============================================================================
--- trunk/libgda/providers-support/gda-pmodel.h	(original)
+++ trunk/libgda/providers-support/gda-pmodel.h	Fri Aug 15 19:41:51 2008
@@ -39,14 +39,25 @@
 typedef struct _GdaPModelClass   GdaPModelClass;
 typedef struct _GdaPModelPrivate GdaPModelPrivate;
 
+/* error reporting */
+extern GQuark gda_pmodel_error_quark (void);
+#define GDA_PMODEL_ERROR gda_pmodel_error_quark ()
+
+enum {
+	GDA_PMODEL_MODIFICATION_STATEMENT_ERROR,
+	GDA_PMODEL_MISSING_MODIFICATION_STATEMENT_ERROR,
+	GDA_PMODEL_CONNECTION_ERROR,
+	GDA_PMODEL_VALUE_ERROR
+};
+
 struct _GdaPModel {
 	GObject           object;
 	GdaPModelPrivate *priv;
+
 	/* read only information */
 	GdaPStmt         *prep_stmt; /* use the "prepared-stmt" property to set this */
 	gint              nb_stored_rows; /* number of GdaRow objects currently stored */
 	gint              advertized_nrows; /* set when the number of rows becomes known */
-	GdaConnection    *cnc;
 };
 
 /*
@@ -80,9 +91,8 @@
 GdaRow        *gda_pmodel_get_stored_row               (GdaPModel *model, gint rownum);
 
 GdaConnection *gda_pmodel_get_connection               (GdaPModel *model);
-gboolean       gda_pmodel_set_modification_query       (GdaPModel *model, GdaStatement *mod_stmt, GError **error);
-gboolean       gda_pmodel_compute_modification_queries (GdaPModel *model, const gchar *target, 
-							gboolean use_all_fields_if_no_pk, GError **error);
+gboolean       gda_pmodel_set_modification_statements   (GdaPModel *model, GdaStatement *mod_stmt, GError **error);
+gboolean       gda_pmodel_compute_modification_statements (GdaPModel *model, gboolean require_pk, GError **error);
 
 G_END_DECLS
 

Modified: trunk/libgda/providers-support/gda-pstmt.c
==============================================================================
--- trunk/libgda/providers-support/gda-pstmt.c	(original)
+++ trunk/libgda/providers-support/gda-pstmt.c	Fri Aug 15 19:41:51 2008
@@ -32,6 +32,10 @@
 
 static GObjectClass *parent_class = NULL;
 
+struct _GdaPStmtPrivate {
+	GdaStatement *gda_stmt; /* GdaPStmt object holds a weak reference on this stmt object, may be NULL */
+};
+
 /**
  * gda_pstmt_get_type
  *
@@ -58,7 +62,7 @@
 
 		g_static_mutex_lock (&registering);
 		if (type == 0)
-			type = g_type_register_static (G_TYPE_OBJECT, "GdaPStmt", &info, 0);
+			type = g_type_register_static (G_TYPE_OBJECT, "GdaPStmt", &info, G_TYPE_FLAG_ABSTRACT);
 		g_static_mutex_unlock (&registering);
 	}
 	return type;
@@ -79,6 +83,8 @@
 gda_pstmt_init (GdaPStmt *pstmt, GdaPStmtClass *klass)
 {
 	g_return_if_fail (GDA_IS_PSTMT (pstmt));
+	pstmt->priv = g_new0 (GdaPStmtPrivate, 1);
+	pstmt->priv->gda_stmt = NULL;
 	pstmt->sql = NULL;
 	pstmt->param_ids = NULL;
 	pstmt->ncols = -1;
@@ -87,14 +93,21 @@
 }
 
 static void
+gda_stmt_reset_cb (GdaStatement *stmt, GdaPStmt *pstmt)
+{
+	g_signal_handlers_disconnect_by_func (G_OBJECT (stmt), 
+					      G_CALLBACK (gda_stmt_reset_cb), pstmt);
+	g_object_remove_weak_pointer ((GObject*) pstmt->priv->gda_stmt, (gpointer*) &(pstmt->priv->gda_stmt));
+	pstmt->priv->gda_stmt = NULL;
+}
+
+static void
 gda_pstmt_dispose (GObject *object)
 {
 	GdaPStmt *pstmt = (GdaPStmt *) object;
 
-	if (pstmt->stmt) {
-		g_object_remove_weak_pointer ((GObject*) pstmt->stmt, (gpointer*) &(pstmt->stmt));
-		pstmt->stmt = NULL;
-	}
+	if (pstmt->priv->gda_stmt) 
+		gda_stmt_reset_cb (pstmt->priv->gda_stmt, pstmt);
 
 	/* chain to parent class */
 	parent_class->dispose (object);
@@ -106,6 +119,8 @@
 	GdaPStmt *pstmt = (GdaPStmt *) object;
 
 	/* free memory */
+	g_free (pstmt->priv);
+
 	if (pstmt->sql) {
 		g_free (pstmt->sql);
 		pstmt->sql = NULL;
@@ -141,13 +156,16 @@
 	g_return_if_fail (GDA_IS_PSTMT (pstmt));
 	g_return_if_fail (!stmt || GDA_IS_STATEMENT (stmt));
 
-	if (pstmt->stmt == stmt)
+	if (pstmt->priv->gda_stmt == stmt)
 		return;
-	if (pstmt->stmt)
-		g_object_unref (pstmt->stmt);
-	pstmt->stmt = stmt;
-	if (stmt)
-		g_object_add_weak_pointer ((GObject*) stmt, (gpointer*) &(pstmt->stmt));
+	if (pstmt->priv->gda_stmt) 
+		gda_stmt_reset_cb (pstmt->priv->gda_stmt, pstmt);
+
+	pstmt->priv->gda_stmt = stmt;
+	if (stmt) {
+		g_object_add_weak_pointer ((GObject*) stmt, (gpointer*) &(pstmt->priv->gda_stmt));
+		g_signal_connect (G_OBJECT (stmt), "reset", G_CALLBACK (gda_stmt_reset_cb), pstmt);
+	}
 }
 
 /**
@@ -189,4 +207,21 @@
 			dest->tmpl_columns = g_slist_append (dest->tmpl_columns, 
 							     gda_column_copy (GDA_COLUMN (list->data)));
 	}
+	if (src->priv->gda_stmt)
+		gda_pstmt_set_gda_statement (dest, src->priv->gda_stmt);
+}
+
+/**
+ * gda_pstmt_get_gda_statement
+ * @pstmt: a #GdaPStmt object
+ *
+ * Get a pointer to the #GdaStatement which led to the creation of this prepared statement
+ *
+ * Returns: the #GdaStatement
+ */
+GdaStatement *
+gda_pstmt_get_gda_statement (GdaPStmt *pstmt)
+{
+	g_return_val_if_fail (GDA_IS_PSTMT (pstmt), NULL);
+	return pstmt->priv->gda_stmt;
 }

Modified: trunk/libgda/providers-support/gda-pstmt.h
==============================================================================
--- trunk/libgda/providers-support/gda-pstmt.h	(original)
+++ trunk/libgda/providers-support/gda-pstmt.h	Fri Aug 15 19:41:51 2008
@@ -35,12 +35,13 @@
 #define GDA_IS_PSTMT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GDA_TYPE_PSTMT))
 
 typedef struct _GdaPStmt        GdaPStmt;
+typedef struct _GdaPStmtPrivate GdaPStmtPrivate;
 typedef struct _GdaPStmtClass   GdaPStmtClass;
 
 struct _GdaPStmt {
 	GObject       object;
 
-	GdaStatement *stmt; /* GdaPStmt object holds a reference on this stmt object, may be NULL */
+	GdaPStmtPrivate *priv;
 	gchar        *sql; /* actual SQL code used for this prepared statement, mem freed by GdaPStmt */
         GSList       *param_ids; /* list of parameters' IDs (as gchar *), mem freed by GdaPStmt */
 
@@ -55,9 +56,10 @@
 	GObjectClass  parent_class;
 };
 
-GType gda_pstmt_get_type          (void) G_GNUC_CONST;
-void  gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt);
-void  gda_pstmt_copy_contents     (GdaPStmt *src, GdaPStmt *dest);
+GType         gda_pstmt_get_type          (void) G_GNUC_CONST;
+void          gda_pstmt_set_gda_statement (GdaPStmt *pstmt, GdaStatement *stmt);
+void          gda_pstmt_copy_contents     (GdaPStmt *src, GdaPStmt *dest);
+GdaStatement *gda_pstmt_get_gda_statement (GdaPStmt *pstmt);
 
 G_END_DECLS
 

Modified: trunk/libgda/sql-parser/gda-statement-struct-pspec.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-pspec.c	(original)
+++ trunk/libgda/sql-parser/gda-statement-struct-pspec.c	Fri Aug 15 19:41:51 2008
@@ -75,16 +75,15 @@
 void
 gda_sql_param_spec_take_type (GdaSqlParamSpec *pspec, GValue *value)
 {
-	if (pspec->type) {
-		g_free (pspec->type);
-		pspec->type = NULL;
-	}
+	pspec->g_type = 0;
 	if (value) {
-		pspec->type = _remove_quotes (g_value_dup_string (value));
+		gchar *tmp;
+		tmp = _remove_quotes (g_value_dup_string (value));
 		g_value_unset (value);
 		g_free (value);
 
-		pspec->g_type = gda_g_type_from_string (pspec->type);
+		pspec->g_type = gda_g_type_from_string (tmp);
+		g_free (tmp);
 	}
 }
 
@@ -96,7 +95,6 @@
 	pspec =g_new0 (GdaSqlParamSpec, 1);
 	pspec->is_param = TRUE;
 	pspec->nullok = FALSE;
-	pspec->type = NULL;
 
 	if (value) {
 		gchar *str = (gchar *) g_value_get_string (value);
@@ -114,8 +112,7 @@
 				pspec->name = g_strdup (str);
 				break;
 			case 1:
-				g_free (pspec->type);
-				pspec->type = g_strdup (str);
+				pspec->g_type = gda_g_type_from_string (str);
 				break;
 			case 2:
 				pspec->nullok = (*str == 'n') || (*str == 'N') ? TRUE : FALSE;
@@ -145,8 +142,7 @@
 		copy->name = g_strdup (pspec->name);
 	if (pspec->descr)
 		copy->descr = g_strdup (pspec->descr);
-	if (pspec->type)
-		copy->type = g_strdup (pspec->type);
+	copy->g_type = pspec->g_type;
 	copy->is_param = pspec->is_param;
 	copy->nullok = pspec->nullok;
 
@@ -160,7 +156,6 @@
 
 	g_free (pspec->name);
 	g_free (pspec->descr);
-	g_free (pspec->type);
 	g_free (pspec);
 }
 
@@ -183,9 +178,13 @@
 	g_string_append_printf (string, ",\"descr\":%s", str);
 	g_free (str);
 
-	str = _json_quote_string (pspec->type);
-	g_string_append_printf (string, ",\"type\":%s", str);
-	g_free (str);
+	if (pspec->g_type != G_TYPE_INVALID) {
+		str = _json_quote_string (gda_g_type_to_string (pspec->g_type));
+		g_string_append_printf (string, ",\"type\":%s", str);
+		g_free (str);
+	}
+	else
+		g_string_append_printf (string, ",\"type\":null");
 
 	g_string_append_printf (string, ",\"is_param\":%s", pspec->is_param ? "true" : "false");
 	g_string_append_printf (string, ",\"nullok\":%s", pspec->nullok ? "true" : "false");

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	Fri Aug 15 19:41:51 2008
@@ -32,7 +32,6 @@
 {
 	gchar    *name;
 	gchar    *descr;
-	gchar    *type;
 	gboolean  is_param;
 	gboolean  nullok;
 

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	Fri Aug 15 19:41:51 2008
@@ -109,14 +109,20 @@
 		return NULL;
 
 	infos = gda_sql_statement_get_contents_infos (stmt->stmt_type);
-	copy = gda_sql_statement_new (stmt->stmt_type);
+	copy = g_new0 (GdaSqlStatement, 1);
+	copy->stmt_type = stmt->stmt_type;
+
 	if (stmt->sql)
 		copy->sql = g_strdup (stmt->sql);
 	if (infos && infos->copy) {
 		copy->contents = infos->copy (stmt->contents);
 		GDA_SQL_ANY_PART (copy->contents)->type = GDA_SQL_ANY_PART (stmt->contents)->type;
 	}
-	else 
+	else if (infos && infos->construct) {
+		copy->contents = infos->construct ();
+		GDA_SQL_ANY_PART (copy->contents)->type = stmt->stmt_type;
+	}
+	else
 		TO_IMPLEMENT;
 	if (stmt->validity_meta_struct) {
 		copy->validity_meta_struct = stmt->validity_meta_struct;

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Fri Aug 15 19:41:51 2008
@@ -1576,7 +1576,7 @@
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 
 	/* fetch prepares stmt if already done */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (ps)
 		return TRUE;
 
@@ -1584,7 +1584,7 @@
 	if (!ps)
 		return FALSE;
 	else {
-		gda_connection_add_prepared_statement (cnc, stmt, ps);
+		gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
 		return TRUE;
 	}
 }
@@ -1821,7 +1821,7 @@
 	/* get SQLite's private data */
 	cdata = (SqliteConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
-		return FALSE;
+		return NULL;
 
 	/* HACK: force SQLite to reparse the schema and thus discover new tables if necessary */
         {
@@ -1835,7 +1835,7 @@
         }
 
 	/* get/create new prepared statement */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (!ps) {
 		if (!gda_sqlite_provider_statement_prepare (provider, cnc, stmt, NULL)) {
 			/* try to use the SQL when parameters are rendered with their values */
@@ -1867,7 +1867,7 @@
 			new_ps = TRUE;
 		}
 		else
-			ps = gda_connection_get_prepared_statement (cnc, stmt);
+			ps = (GdaSqlitePStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	}
 	else if (ps->stmt_used) {
 		/* Don't use @ps => prepare stmt again */
@@ -1950,7 +1950,16 @@
 			g_free (str);
 			break;
 		}
-
+		if (!gda_holder_is_valid (h)) {
+			gchar *str;
+			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+			gda_connection_event_set_description (event, str);
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+			g_free (str);
+			break;
+		}
 		/*g_print ("BINDING param '%s' to %p\n", pname, h);*/
 		
 		const GValue *value = gda_holder_get_value (h);
@@ -2063,7 +2072,7 @@
 		else
 			flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 
-                data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, flags, col_types);
+                data_model = (GObject *) gda_sqlite_recordset_new (cnc, ps, params, flags, col_types);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL);
 		if (new_ps)
 			g_object_unref (ps);

Modified: trunk/libgda/sqlite/gda-sqlite-recordset.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.c	Fri Aug 15 19:41:51 2008
@@ -179,7 +179,8 @@
  * this function
  */
 GdaDataModel *
-gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaDataModelAccessFlags flags, GType *col_types)
+gda_sqlite_recordset_new (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
+			  GdaDataModelAccessFlags flags, GType *col_types)
 {
 	GdaSqliteRecordset *model;
         SqliteConnectionData *cdata;
@@ -288,7 +289,8 @@
 
 	/* create data model */
         model = g_object_new (GDA_TYPE_SQLITE_RECORDSET, "connection", cnc, 
-			      "prepared-stmt", ps, "model-usage", rflags, NULL);
+			      "prepared-stmt", ps, "model-usage", rflags, 
+			      "exec-params", exec_params, NULL);
 
         /* fill the data model */
         read_rows_to_init_col_types (model);

Modified: trunk/libgda/sqlite/gda-sqlite-recordset.h
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-recordset.h	(original)
+++ trunk/libgda/sqlite/gda-sqlite-recordset.h	Fri Aug 15 19:41:51 2008
@@ -52,8 +52,8 @@
 };
 
 GType         gda_sqlite_recordset_get_type  (void) G_GNUC_CONST;
-GdaDataModel *gda_sqlite_recordset_new       (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaDataModelAccessFlags flags, 
-					      GType *col_types);
+GdaDataModel *gda_sqlite_recordset_new       (GdaConnection *cnc, GdaSqlitePStmt *ps, GdaSet *exec_params,
+					      GdaDataModelAccessFlags flags, GType *col_types);
 
 G_END_DECLS
 

Modified: trunk/libgda/sqlite/utils.c
==============================================================================
--- trunk/libgda/sqlite/utils.c	(original)
+++ trunk/libgda/sqlite/utils.c	Fri Aug 15 19:41:51 2008
@@ -57,6 +57,7 @@
 	}
 	
 	g_hash_table_insert (types, g_strdup ("integer"), GINT_TO_POINTER (G_TYPE_INT));
+	g_hash_table_insert (types, g_strdup ("int"), GINT_TO_POINTER (G_TYPE_INT));
 	g_hash_table_insert (types, g_strdup ("boolean"), GINT_TO_POINTER (G_TYPE_BOOLEAN));
 	g_hash_table_insert (types, g_strdup ("date"), GINT_TO_POINTER (G_TYPE_DATE));
 	g_hash_table_insert (types, g_strdup ("time"), GINT_TO_POINTER (GDA_TYPE_TIME));

Modified: trunk/providers/mysql/gda-mysql-provider.c
==============================================================================
--- trunk/providers/mysql/gda-mysql-provider.c	(original)
+++ trunk/providers/mysql/gda-mysql-provider.c	Fri Aug 15 19:41:51 2008
@@ -155,7 +155,7 @@
 								  GType                           *col_types,
 								  GdaSet                         **last_inserted_row, 
 								  guint                           *task_id,
-								  GdaServerProviderAsyncCallback   async_cb, 
+								  GdaServerProviderExecCallback    async_cb, 
 								  gpointer                         cb_data,
 								  GError                         **error);
 
@@ -313,6 +313,7 @@
 	}
 	else
 		provider_class->limiting_thread = NULL;
+	
 }
 
 static void
@@ -1166,7 +1167,7 @@
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 
 	/* fetch prepares stmt if already done */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaMysqlPStmt*) gda_connection_get_prepared_statement (cnc, stmt);
 	if (ps)
 		return TRUE;
 
@@ -1228,7 +1229,7 @@
 		_GDA_PSTMT(ps)->param_ids = param_ids;
 		_GDA_PSTMT(ps)->sql = sql;
 		
-		gda_connection_add_prepared_statement (cnc, stmt, ps);
+		gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt*) ps);
 		return TRUE;
 	}
 	
@@ -1288,7 +1289,7 @@
 				      GType                           *col_types,
 				      GdaSet                         **last_inserted_row, 
 				      guint                           *task_id, 
-				      GdaServerProviderAsyncCallback   async_cb,
+				      GdaServerProviderExecCallback    async_cb,
 				      gpointer                         cb_data,
 				      GError                         **error)
 {
@@ -1312,7 +1313,7 @@
 
 
 	/* get/create new prepared statement */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaMysqlPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (!ps) {
 		if (!gda_mysql_provider_statement_prepare (provider, cnc, stmt, NULL)) {
 			/* this case can appear for example if some variables are used in places
@@ -1327,21 +1328,19 @@
 			if (!sql)
 				return NULL;
 			ps = prepare_stmt_simple (cdata, sql, error);
+			g_free (sql);
 			if (!ps)
 				return NULL;
 			
 		}
 		else
-			ps = gda_connection_get_prepared_statement (cnc, stmt);
+			ps = (GdaMysqlPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	}
 	g_assert (ps);
 
 	/* optionnally reset the prepared statement if required by the API */
 	// TO_IMPLEMENT;
 	
-	
-	g_print ("   %s: SQL=%s\n", __func__, _GDA_PSTMT(ps)->sql);
-	
 	/* bind statement's parameters */
 	GSList *list;
 	GdaConnectionEvent *event = NULL;
@@ -1351,7 +1350,7 @@
 	char **param_values = g_new0 (char *, nb_params + 1);
         int *param_lengths = g_new0 (int, nb_params + 1);
         int *param_formats = g_new0 (int, nb_params + 1);
-	g_print ("NB=%d, SQL=%s\n", nb_params, _GDA_PSTMT(ps)->sql);
+	/* g_print ("NB=%d, SQL=%s\n", nb_params, _GDA_PSTMT(ps)->sql); */
 
 	MYSQL_BIND *mysql_bind_param = g_new0 (MYSQL_BIND, nb_params);
 	
@@ -1386,6 +1385,16 @@
 			g_free (str);
 			break;
 		}
+		if (!gda_holder_is_valid (h)) {
+			gchar *str;
+			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+			gda_connection_event_set_description (event, str);
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+			g_free (str);
+			break;
+		}
 
 		/* actual binding using the C API, for parameter at position @i */
 		const GValue *value = gda_holder_get_value (h);
@@ -1403,7 +1412,7 @@
 			param_values[i] = gda_handler_time_get_no_locale_str_from_value (handler_time,
 											 value);
 			
-			g_print ("--- TIME=%s\n", param_values[i]);
+			/* g_print ("--- TIME=%s\n", param_values[i]); */
 		} else {
 			GdaDataHandler *data_handler = gda_server_provider_get_data_handler_gtype
 				(provider, cnc, G_VALUE_TYPE(value));
@@ -1412,7 +1421,7 @@
 			else
 				param_values[i] = gda_data_handler_get_str_from_value (data_handler,
 										       value);
-			g_print ("--- PV=%s\n", param_values[i]);
+			/* g_print ("--- PV=%s\n", param_values[i]); */
 
 			mysql_bind_param[i].buffer_type = MYSQL_TYPE_STRING;
 			mysql_bind_param[i].buffer = g_strdup (param_values[i]);
@@ -1420,11 +1429,6 @@
 			mysql_bind_param[i].length = g_malloc0 (sizeof(unsigned long));
 
 		}
-		
-		gchar *str = gda_value_stringify (value);
-		g_print ("   %s: %s=%s\n", __func__, pname, str);
-		g_free (str);
-		
 
 	}
 		
@@ -1443,6 +1447,24 @@
 
 		return NULL;
 	}
+
+
+	/* use cursor when retrieving result */
+	if ((model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) == 0 &&
+	    gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) {
+#if MYSQL_VERSION_ID >= 50002
+		const unsigned long cursor_type = CURSOR_TYPE_READ_ONLY;
+		if (mysql_stmt_attr_set (cdata->mysql_stmt, STMT_ATTR_CURSOR_TYPE, (void *) &cursor_type)) {
+			_gda_mysql_make_error (cnc, NULL, cdata->mysql_stmt, NULL);
+			return NULL;
+		}
+#else
+		model_usage = GDA_STATEMENT_MODEL_RANDOM_ACCESS;
+		g_warning (_("Could not use CURSOR. Mysql version 5.0 at least is required. "
+			     "Using random access anyway."));
+#endif
+	}
+
 	
 	/* add a connection event for the execution */
 	event = gda_connection_event_new (GDA_CONNECTION_EVENT_COMMAND);
@@ -1471,7 +1493,7 @@
 				else
 					flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 
-				return_value = (GObject *) gda_mysql_recordset_new (cnc, ps, flags, col_types);
+				return_value = (GObject *) gda_mysql_recordset_new (cnc, ps, params, flags, col_types);
 				gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
 			}
 			

Modified: trunk/providers/mysql/gda-mysql-recordset.c
==============================================================================
--- trunk/providers/mysql/gda-mysql-recordset.c	(original)
+++ trunk/providers/mysql/gda-mysql-recordset.c	Fri Aug 15 19:41:51 2008
@@ -58,22 +58,22 @@
 gda_mysql_recordset_fetch_nb_rows (GdaPModel  *model);
 static gboolean
 gda_mysql_recordset_fetch_random (GdaPModel  *model,
-				  GdaRow    **prow,
+				  GdaRow    **row,
 				  gint        rownum,
 				  GError    **error);
 static gboolean
 gda_mysql_recordset_fetch_next (GdaPModel  *model,
-				GdaRow    **prow,
+				GdaRow    **row,
 				gint        rownum,
 				GError    **error);
 static gboolean
 gda_mysql_recordset_fetch_prev (GdaPModel  *model,
-				GdaRow    **prow,
+				GdaRow    **row,
 				gint        rownum,
 				GError    **error);
 static gboolean
 gda_mysql_recordset_fetch_at (GdaPModel  *model,
-			      GdaRow    **prow,
+			      GdaRow    **row,
 			      gint        rownum,
 			      GError    **error);
 
@@ -104,12 +104,52 @@
 	/* initialize specific information */
 	// TO_IMPLEMENT;
 	
-	recset->priv->chunk_size = 10;
+	recset->priv->chunk_size = 1;
 	recset->priv->chunks_read = 0;
 	
 }
 
 
+gint
+gda_mysql_recordset_get_chunk_size (GdaMysqlRecordset  *recset)
+{
+	g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+	return recset->priv->chunk_size;
+}
+
+void
+gda_mysql_recordset_set_chunk_size (GdaMysqlRecordset  *recset,
+				    gint                chunk_size)
+{
+	g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+
+	if (recset->priv->mysql_stmt == NULL)  // Creation is in progress so it's not set.
+		return;
+
+#if MYSQL_VERSION_ID >= 50002
+	const unsigned long prefetch_rows = chunk_size;
+	if (mysql_stmt_attr_set (recset->priv->mysql_stmt, STMT_ATTR_PREFETCH_ROWS,
+				 (void *) &prefetch_rows)) {
+		_gda_mysql_make_error (recset->priv->cnc, NULL, recset->priv->mysql_stmt, NULL);
+		return;
+	}
+	recset->priv->chunk_size = chunk_size;
+	g_object_notify (G_OBJECT(recset), "chunk-size");
+#else
+	g_warning (_("Could not use CURSOR. Mysql version 5.0 at least is required. "
+		     "Chunk size ignored."));
+#endif
+
+}
+
+gint
+gda_mysql_recordset_get_chunks_read (GdaMysqlRecordset  *recset)
+{
+	g_return_if_fail (GDA_IS_MYSQL_RECORDSET (recset));
+	return recset->priv->chunks_read;
+}
+
+
 static void
 gda_mysql_recordset_set_property (GObject       *object,
 				  guint          param_id,
@@ -126,7 +166,8 @@
 
 	switch (param_id) {
 	case PROP_CHUNK_SIZE:
-		record_set->priv->chunk_size = g_value_get_int (value);
+		gda_mysql_recordset_set_chunk_size (record_set,
+						    g_value_get_int (value));
 		break;
 	case PROP_CHUNKS_READ:
 		break;
@@ -187,7 +228,7 @@
 		 PROP_CHUNK_SIZE,
 		 g_param_spec_int ("chunk-size", _("Number of rows fetched at a time"),
 				   NULL,
-				   1, G_MAXINT - 1, 10,
+				   1, G_MAXINT - 1, 1,
 				   (G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE)));
 
 	g_object_class_install_property
@@ -259,6 +300,7 @@
 GdaDataModel *
 gda_mysql_recordset_new (GdaConnection            *cnc,
 			 GdaMysqlPStmt            *ps,
+			 GdaSet                   *exec_params,
 			 GdaDataModelAccessFlags   flags,
 			 GType                    *col_types)
 {
@@ -417,6 +459,7 @@
         model = g_object_new (GDA_TYPE_MYSQL_RECORDSET,
 			      "prepared-stmt", ps,
 			      "model-usage", rflags,
+			      "exec-params", exec_params, 
 			      NULL);
         model->priv->cnc = cnc;
 	g_object_ref (G_OBJECT(cnc));
@@ -459,18 +502,18 @@
 new_row_from_mysql_stmt (GdaMysqlRecordset  *imodel,
 			 gint                rownum)
 {
-	g_print ("*** %s -- %d -- %d\n", __func__, ((GdaPModel *) imodel)->prep_stmt->ncols, rownum);
+	/* g_print ("*** %s -- %d -- %d\n", __func__, ((GdaPModel *) imodel)->prep_stmt->ncols, rownum); */
 	
 	MYSQL_BIND *mysql_bind_result = ((GdaMysqlPStmt *) ((GdaPModel *) imodel)->prep_stmt)->mysql_bind_result;
 	g_assert (mysql_bind_result);
 	
-	GdaRow *prow = gda_row_new (((GdaPModel *) imodel)->prep_stmt->ncols);
+	GdaRow *row = gda_row_new (((GdaPModel *) imodel)->prep_stmt->ncols);
 	gint col;
 	for (col = 0; col < ((GdaPModel *) imodel)->prep_stmt->ncols; ++col) {
 		
 		gint i = col;
 		
-		GValue *value = gda_row_get_value (prow, i);
+		GValue *value = gda_row_get_value (row, i);
 		GType type = ((GdaPModel *) imodel)->prep_stmt->types[i];
 		gda_value_reset_with_type (value, type);
 		
@@ -607,20 +650,20 @@
 		g_free (strvalue);
 		
 		/* gchar *str = gda_value_stringify (value); */
-		/* g_print ("   V%d=%s\n", i, str); */
+		/* g_print ("***V%d=%s\n", i, str); */
 		/* g_free (str); */
-		//		
+
 	}
-	return prow;
+	return row;
 }
 
 
 /*
- * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
+ * Create a new filled #GdaRow object for the row at position @rownum, and put it into *row.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ *  -  If *row is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *row contains a #GdaRow object which has already been created 
  *     (through a call to this very function), and in this case it should not be modified
  *     but the function may return FALSE if an error occurred.
  *
@@ -632,7 +675,7 @@
  */
 static gboolean 
 gda_mysql_recordset_fetch_random (GdaPModel  *model,
-				  GdaRow    **prow,
+				  GdaRow    **row,
 				  gint        rownum,
 				  GError    **error)
 {
@@ -642,7 +685,7 @@
 
 	// TO_IMPLEMENT;
 	
-	if (*prow)
+	if (*row)
 		return TRUE;
 
 	if (imodel->priv->mysql_stmt == NULL) {
@@ -655,13 +698,14 @@
 	if (mysql_stmt_fetch (imodel->priv->mysql_stmt))
 		return FALSE;
 	
-	*prow = new_row_from_mysql_stmt (imodel, rownum);
-	gda_pmodel_take_row (model, *prow, rownum);
-	
-	/* if (model->nb_stored_rows == model->advertized_nrows) { */
-	/* 	g_print ("  All the row have been converted..."); */
-	/* } */
+	*row = new_row_from_mysql_stmt (imodel, rownum);
+	gda_pmodel_take_row (model, *row, rownum);
 	
+	if (model->nb_stored_rows == model->advertized_nrows) {
+		/* All the row have been converted.  We could free result now */
+		/* but it was done before provided no field-based API functions */
+		/* that process result set meta data was needed in the middle. */
+	}
 
 	return TRUE;
 }
@@ -670,7 +714,8 @@
  * Create and "give" filled #GdaRow object for all the rows in the model
  */
 static gboolean
-gda_mysql_recordset_store_all (GdaPModel *model, GError **error)
+gda_mysql_recordset_store_all (GdaPModel  *model,
+			       GError    **error)
 {
 	GdaMysqlRecordset *imodel;
 	gint i;
@@ -679,19 +724,19 @@
 
 	/* default implementation */
 	for (i = 0; i < model->advertized_nrows; i++) {
-		GdaRow *prow;
-		if (! gda_mysql_recordset_fetch_random (model, &prow, i, error))
+		GdaRow *row;
+		if (! gda_mysql_recordset_fetch_random (model, &row, i, error))
 			return FALSE;
 	}
 	return TRUE;
 }
 
 /*
- * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
+ * Create a new filled #GdaRow object for the next cursor row, and put it into *row.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ *  -  If *row is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *row contains a #GdaRow object which has already been created 
  *     (through a call to this very function), and in this case it should not be modified
  *     but the function may return FALSE if an error occurred.
  *
@@ -699,21 +744,26 @@
  * can use gda_pmodel_take_row().
  */
 static gboolean 
-gda_mysql_recordset_fetch_next (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_next (GdaPModel  *model,
+				GdaRow    **row,
+				gint        rownum,
+				GError    **error)
 {
 	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
 
-	TO_IMPLEMENT;
+	// TO_IMPLEMENT;
+	//
 
-	return TRUE;
+	// gda_pmodel_iter_next increments rownum
+	return /* TRUE */ gda_mysql_recordset_fetch_random (model, row, rownum, error);
 }
 
 /*
- * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
+ * Create a new filled #GdaRow object for the previous cursor row, and put it into *row.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ *  -  If *row is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *row contains a #GdaRow object which has already been created 
  *     (through a call to this very function), and in this case it should not be modified
  *     but the function may return FALSE if an error occurred.
  *
@@ -721,7 +771,10 @@
  * can use gda_pmodel_take_row().
  */
 static gboolean 
-gda_mysql_recordset_fetch_prev (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_prev (GdaPModel  *model,
+				GdaRow    **row,
+				gint        rownum,
+				GError    **error)
 {
 	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
 
@@ -731,11 +784,11 @@
 }
 
 /*
- * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
+ * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *row.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
+ * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
+ *  -  If *row is NULL then a new #GdaRow object has to be created, 
+ *  -  and otherwise *row contains a #GdaRow object which has already been created 
  *     (through a call to this very function), and in this case it should not be modified
  *     but the function may return FALSE if an error occurred.
  *
@@ -743,7 +796,10 @@
  * can use gda_pmodel_take_row().
  */
 static gboolean 
-gda_mysql_recordset_fetch_at (GdaPModel *model, GdaRow **prow, gint rownum, GError **error)
+gda_mysql_recordset_fetch_at (GdaPModel  *model,
+			      GdaRow    **row,
+			      gint        rownum,
+			      GError    **error)
 {
 	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
 	

Modified: trunk/providers/mysql/gda-mysql-recordset.h
==============================================================================
--- trunk/providers/mysql/gda-mysql-recordset.h	(original)
+++ trunk/providers/mysql/gda-mysql-recordset.h	Fri Aug 15 19:41:51 2008
@@ -53,9 +53,21 @@
 GdaDataModel *
 gda_mysql_recordset_new       (GdaConnection            *cnc,
 			       GdaMysqlPStmt            *ps,
+			       GdaSet                   *exec_params,
 			       GdaDataModelAccessFlags   flags, 
 			       GType                    *col_types);
 
+
+gint
+gda_mysql_recordset_get_chunk_size (GdaMysqlRecordset  *recset);
+
+void
+gda_mysql_recordset_set_chunk_size (GdaMysqlRecordset  *recset,
+				    gint                chunk_size);
+
+gint
+gda_mysql_recordset_get_chunks_read (GdaMysqlRecordset  *recset);
+
 G_END_DECLS
 
 #endif

Modified: trunk/providers/mysql/libmain.c
==============================================================================
--- trunk/providers/mysql/libmain.c	(original)
+++ trunk/providers/mysql/libmain.c	Fri Aug 15 19:41:51 2008
@@ -2,7 +2,7 @@
  * Copyright (C) 2008 The GNOME Foundation
  *
  * AUTHORS:
- *      Carlos Savoretti <csavoretti gmail com>
+ *      TO_ADD: your name and email
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public

Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c	(original)
+++ trunk/providers/postgres/gda-postgres-provider.c	Fri Aug 15 19:41:51 2008
@@ -1629,7 +1629,7 @@
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 
 	/* fetch prepares stmt if already done */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (ps)
 		return TRUE;
 
@@ -1694,7 +1694,7 @@
         _GDA_PSTMT (ps)->param_ids = param_ids;
         _GDA_PSTMT (ps)->sql = sql;
 	
-	gda_connection_add_prepared_statement (cnc, stmt, ps);
+	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
 	return TRUE;
 
  out_err:
@@ -1962,13 +1962,13 @@
 		PQclear (pg_res);
 		
 		/* create data model in CURSOR mode */
-		recset = gda_postgres_recordset_new_cursor (cnc, ps, cursor_name, col_types);
+		recset = gda_postgres_recordset_new_cursor (cnc, ps, params, cursor_name, col_types);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
 		return (GObject*) recset;
 	}
 
 	/* get/create new prepared statement */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (!ps) {
 		if (!gda_postgres_provider_statement_prepare (provider, cnc, stmt, NULL)) {
 			/* this case can appear for example if some variables are used in places
@@ -1987,7 +1987,7 @@
 				return NULL;
 		}
 		else
-			ps = gda_connection_get_prepared_statement (cnc, stmt);
+			ps = (GdaPostgresPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	}
 	g_assert (ps);
 
@@ -2037,6 +2037,16 @@
 			g_free (str);
 			break;
 		}
+		if (!gda_holder_is_valid (h)) {
+			gchar *str;
+			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+			gda_connection_event_set_description (event, str);
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+			g_free (str);
+			break;
+		}
 
 		/* actual binding using the C API, for parameter at position @i */
 		const GValue *value = gda_holder_get_value (h);
@@ -2144,7 +2154,7 @@
                                 PQclear (pg_res);
                         }
                         else if (status == PGRES_TUPLES_OK) 
-				retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, pg_res, col_types);
+				retval = (GObject*) gda_postgres_recordset_new_random (cnc, ps, params, pg_res, col_types);
                         else {
                                 PQclear (pg_res);
                                 retval = (GObject *) gda_data_model_array_new (0);

Modified: trunk/providers/postgres/gda-postgres-recordset.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-recordset.c	(original)
+++ trunk/providers/postgres/gda-postgres-recordset.c	Fri Aug 15 19:41:51 2008
@@ -319,7 +319,8 @@
 }
 
 GdaDataModel *
-gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types)
+gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params, 
+				   PGresult *pg_res, GType *col_types)
 {
 	GdaPostgresRecordset *model;
         PostgresConnectionData *cdata;
@@ -337,7 +338,8 @@
 	/* create data model */
         model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "connection", cnc, 
 			      "prepared-stmt", ps, 
-			      "model-usage", GDA_DATA_MODEL_ACCESS_RANDOM, NULL);
+			      "model-usage", GDA_DATA_MODEL_ACCESS_RANDOM, 
+			      "exec-params", exec_params, NULL);
 	model->priv->pg_res = pg_res;
 	((GdaPModel*) model)->advertized_nrows = PQntuples (model->priv->pg_res);
 
@@ -348,7 +350,8 @@
  * Takes ownership of @cursor_name
  */
 GdaDataModel *
-gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types)
+gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params,
+				   gchar *cursor_name, GType *col_types)
 {
 	GdaPostgresRecordset *model;
         PostgresConnectionData *cdata;
@@ -393,7 +396,8 @@
 	/* create model */
 	model = g_object_new (GDA_TYPE_POSTGRES_RECORDSET, "connection", cnc, 
 			      "prepared-stmt", ps, "model-usage", 
-			      GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD, NULL);
+			      GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD, 
+			      "exec-params", exec_params, NULL);
 	model->priv->pconn = cdata->pconn;
 	model->priv->cursor_name = cursor_name;
 	gboolean fetch_error;
@@ -727,7 +731,7 @@
 		if (thevalue && (*thevalue != '\0' ? FALSE : PQgetisnull (imodel->priv->pg_res, pg_res_rownum, col)))
 			gda_value_set_null (gda_row_get_value (prow, col));
 		else
-			set_value (((GdaPModel*) imodel)->cnc,
+			set_value (gda_pmodel_get_connection ((GdaPModel*) imodel),
 				   gda_row_get_value (prow, col), 
 				   ((GdaPModel*) imodel)->prep_stmt->types [col], 
 				   thevalue, 
@@ -771,7 +775,7 @@
         status = PQresultStatus (model->priv->pg_res);
 	model->priv->chunks_read ++;
         if (status != PGRES_TUPLES_OK) {
-		_gda_postgres_make_error (((GdaPModel*) model)->cnc, 
+		_gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model), 
 					  model->priv->pconn, model->priv->pg_res, error);
                 PQclear (model->priv->pg_res);
                 model->priv->pg_res = NULL;
@@ -863,7 +867,7 @@
         status = PQresultStatus (model->priv->pg_res);
 	model->priv->chunks_read ++;
         if (status != PGRES_TUPLES_OK) {
-		_gda_postgres_make_error (((GdaPModel*) model)->cnc,
+		_gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model),
 					  model->priv->pconn, model->priv->pg_res, error);
                 PQclear (model->priv->pg_res);
                 model->priv->pg_res = NULL;
@@ -937,7 +941,7 @@
         status = PQresultStatus (model->priv->pg_res);
         model->priv->chunks_read ++; /* Not really correct, because we are only fetching 1 row, not a whole chunk of rows. */
         if (status != PGRES_TUPLES_OK) {
-		_gda_postgres_make_error (((GdaPModel*) model)->cnc, 
+		_gda_postgres_make_error (gda_pmodel_get_connection ((GdaPModel*) model), 
 					  model->priv->pconn, model->priv->pg_res, error);
                 PQclear (model->priv->pg_res);
                 model->priv->pg_res = NULL;

Modified: trunk/providers/postgres/gda-postgres-recordset.h
==============================================================================
--- trunk/providers/postgres/gda-postgres-recordset.h	(original)
+++ trunk/providers/postgres/gda-postgres-recordset.h	Fri Aug 15 19:41:51 2008
@@ -49,8 +49,8 @@
 };
 
 GType         gda_postgres_recordset_get_type   (void) G_GNUC_CONST;
-GdaDataModel *gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, PGresult *pg_res, GType *col_types);
-GdaDataModel *gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, gchar *cursor_name, GType *col_types);
+GdaDataModel *gda_postgres_recordset_new_random (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params, PGresult *pg_res, GType *col_types);
+GdaDataModel *gda_postgres_recordset_new_cursor (GdaConnection *cnc, GdaPostgresPStmt *ps, GdaSet *exec_params, gchar *cursor_name, GType *col_types);
 
 
 G_END_DECLS

Modified: trunk/providers/skel-implementation/capi/gda-capi-provider.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-provider.c	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-provider.c	Fri Aug 15 19:41:51 2008
@@ -913,7 +913,7 @@
 	g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
 
 	/* fetch prepares stmt if already done */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (ps)
 		return TRUE;
 
@@ -922,7 +922,7 @@
 	if (!ps)
 		return FALSE;
 	else {
-		gda_connection_add_prepared_statement (cnc, stmt, ps);
+		gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
 		return TRUE;
 	}
 }
@@ -968,7 +968,7 @@
 
 
 	/* get/create new prepared statement */
-	ps = gda_connection_get_prepared_statement (cnc, stmt);
+	ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	if (!ps) {
 		if (!gda_capi_provider_statement_prepare (provider, cnc, stmt, NULL)) {
 			/* this case can appear for example if some variables are used in places
@@ -980,7 +980,7 @@
 			return NULL;
 		}
 		else
-			ps = gda_connection_get_prepared_statement (cnc, stmt);
+			ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
 	}
 	g_assert (ps);
 
@@ -1023,6 +1023,16 @@
 			g_free (str);
 			break;
 		}
+		if (!gda_holder_is_valid (h)) {
+			gchar *str;
+			str = g_strdup_printf (_("Parameter '%s' is invalid"), pname);
+			event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
+			gda_connection_event_set_description (event, str);
+			g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+				     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+			g_free (str);
+			break;
+		}
 
 		/* actual binding using the C API, for parameter at position @i */
 		const GValue *value = gda_holder_get_value (h);
@@ -1050,7 +1060,7 @@
 		else
 			flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 
-                data_model = (GObject *) gda_capi_recordset_new (cnc, ps, flags, col_types);
+                data_model = (GObject *) gda_capi_recordset_new (cnc, ps, params, flags, col_types);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
 		return data_model;
         }

Modified: trunk/providers/skel-implementation/capi/gda-capi-recordset.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-recordset.c	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-recordset.c	Fri Aug 15 19:41:51 2008
@@ -138,7 +138,8 @@
  * this function
  */
 GdaDataModel *
-gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaDataModelAccessFlags flags, GType *col_types)
+gda_capi_recordset_new (GdaConnection *cnc, GdaCapiPStmt *ps, GdaSet *exec_params,
+			GdaDataModelAccessFlags flags, GType *col_types)
 {
 	GdaCapiRecordset *model;
         CapiConnectionData *cdata;
@@ -202,7 +203,10 @@
 		rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 
 	/* create data model */
-        model = g_object_new (GDA_TYPE_CAPI_RECORDSET, "prepared-stmt", ps, "model-usage", rflags, NULL);
+        model = g_object_new (GDA_TYPE_CAPI_RECORDSET, 
+			      "prepared-stmt", ps, 
+			      "model-usage", rflags, 
+			      "exec-params", exec_params, NULL);
         model->priv->cnc = cnc;
 	g_object_ref (cnc);
 

Modified: trunk/providers/skel-implementation/capi/gda-capi-recordset.h
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-recordset.h	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-recordset.h	Fri Aug 15 19:41:51 2008
@@ -49,8 +49,8 @@
 };
 
 GType         gda_capi_recordset_get_type  (void) G_GNUC_CONST;
-GdaDataModel *gda_capi_recordset_new       (GdaConnection *cnc, GdaCapiPStmt *ps, GdaDataModelAccessFlags flags, 
-					    GType *col_types);
+GdaDataModel *gda_capi_recordset_new       (GdaConnection *cnc, GdaCapiPStmt *ps, GdaSet *exec_params,
+					    GdaDataModelAccessFlags flags, GType *col_types);
 
 G_END_DECLS
 

Modified: trunk/tests/data-models/Makefile.am
==============================================================================
--- trunk/tests/data-models/Makefile.am	(original)
+++ trunk/tests/data-models/Makefile.am	Fri Aug 15 19:41:51 2008
@@ -5,8 +5,8 @@
 	$(LIBGDA_CFLAGS) \
 	-DCHECK_FILES=\""$(top_srcdir)"\"
 
-check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy
-TESTS = check_model_import check_virtual check_data_proxy check_model_copy
+check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel
+TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel
 
 common_sources = 
 
@@ -33,6 +33,15 @@
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(LIBGDA_LIBS)
 
+check_pmodel_SOURCES = $(common_sources) check_pmodel.c
+check_pmodel_CFLAGS = \
+	-I$(top_srcdir)/libgda/sqlite \
+	-I$(top_srcdir)/libgda/sqlite/pmodel
+check_pmodel_LDADD = \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(top_builddir)/tests/libgda-test-4.0.la \
+	$(LIBGDA_LIBS)
+
 EXTRA_DIST = \
 	check_virtual.csv \
 	city.csv \

Added: trunk/tests/data-models/check_pmodel.c
==============================================================================
--- (empty file)
+++ trunk/tests/data-models/check_pmodel.c	Fri Aug 15 19:41:51 2008
@@ -0,0 +1,363 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <sql-parser/gda-sql-statement.h>
+#include <providers-support/gda-pmodel.h>
+#include <tests/gda-ddl-creator.h>
+#include <glib/gstdio.h>
+#include "../test-cnc-utils.h"
+
+#define fail(x) g_warning (x)
+#define fail_if(x,y) if (x) g_warning (y)
+#define fail_unless(x,y) if (!(x)) g_warning (y)
+
+#define CHECK_EXTRA_INFO
+
+static GdaConnection *setup_connection (void);
+static GdaStatement *stmt_from_string (const gchar *sql);
+static void load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *file);
+
+typedef gboolean (*TestFunc) (GdaConnection *);
+static gint test1 (GdaConnection *cnc);
+static gint test2 (GdaConnection *cnc);
+static gint test3 (GdaConnection *cnc);
+
+TestFunc tests[] = {
+        test1,
+        test2,
+        test3
+};
+
+int
+main (int argc, char **argv)
+{
+	gint i, ntests = 0, number_failed = 0;
+	GdaConnection *cnc;
+
+	gda_init ();
+
+	g_unlink ("pmodel.db");
+	cnc = setup_connection ();
+	
+	for (i = 0; i < sizeof (tests) / sizeof (TestFunc); i++) {
+		g_print ("---------- test %d ----------\n", i);
+		gint n = tests[i] (cnc);
+		number_failed += n;
+		if (n > 0) 
+			g_print ("Test %d failed\n", i);
+		ntests ++;
+	}
+
+	g_object_unref (cnc);
+	if (number_failed == 0)
+		g_print ("Ok.\n");
+	else
+		g_print ("%d failed\n", number_failed);
+
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static GdaConnection *
+setup_connection (void)
+{
+	GdaDDLCreator *ddl;
+        GError *error = NULL;
+        GdaConnection *cnc;
+        gchar *str;
+
+        /* open a connection */
+        cnc = gda_connection_open_from_string ("SQLite", "DB_DIR=.;DB_NAME=pmodel", NULL, GDA_CONNECTION_OPTIONS_NONE, &error);
+        if (!cnc) {
+                g_print ("Error opening connection: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+        }
+
+	/* Setup structure */
+        ddl = gda_ddl_creator_new ();
+        gda_ddl_creator_set_connection (ddl, cnc);
+	str = g_build_filename (CHECK_FILES, "tests", "data-models", "pmodel_dbstruct.xml", NULL);
+        if (!gda_ddl_creator_set_dest_from_file (ddl, str, &error)) {
+                g_print ("Error creating GdaDDLCreator: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+        }
+	g_free (str);
+
+#ifdef SHOW_SQL
+        str = gda_ddl_creator_get_sql (ddl, &error);
+        if (!str) {
+                g_print ("Error getting SQL: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+        }
+        g_print ("%s\n", str);
+        g_free (str);
+#endif
+
+        if (!gda_ddl_creator_execute (ddl, &error)) {
+                g_print ("Error creating database objects: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+        }
+
+	if (! gda_connection_update_meta_store (cnc, NULL, &error)) {
+		g_print ("Error fetching meta data: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+	}
+
+	/* load some data */
+	load_data_from_file (cnc, "locations", "pmodel_data_locations.xml");
+	load_data_from_file (cnc, "customers", "pmodel_data_customers.xml");
+
+        g_object_unref (ddl);
+	return cnc;
+}
+
+static void
+load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *file)
+{
+	gchar *str;
+	GError *error = NULL;
+
+	str = g_build_filename (CHECK_FILES, "tests", "data-models", file, NULL);
+	if (! test_cnc_load_data_from_file (cnc, table, str, &error)) {
+		g_print ("Error loading data into table '%s' from file '%s': %s\n", table, str,
+			 error && error->message ? error->message : "No detail");
+		exit (EXIT_FAILURE);
+	}
+	g_free (str);
+}
+
+static GdaStatement *
+stmt_from_string (const gchar *sql)
+{
+	GdaStatement *stmt;
+	GError *error = NULL;
+
+	static GdaSqlParser *parser = NULL;
+	if (!parser)
+		parser = gda_sql_parser_new ();
+
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, &error);
+	if (!stmt) {
+		g_print ("Cound not parse SQL: %s\nSQL was: %s\n",
+			 error && error->message ? error->message : "No detail",
+			 sql);
+		exit (EXIT_FAILURE);
+	}
+	return stmt;
+}
+
+/* Returns the number of failures */
+static gint
+test1 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt;
+	gint nfailed = 0;
+
+	/* create GdaPModel */
+	stmt = stmt_from_string ("SELECT * FROM customers");
+	model = gda_connection_statement_execute_select (cnc, stmt, NULL, &error);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not execute SELECT!\n");
+#endif
+		goto out;
+	}
+	if (!GDA_IS_PMODEL (model)) {
+		g_print ("Data model should be a GdaPModel!\n");
+		exit (EXIT_FAILURE);
+	}
+
+	/* test non INSERT, UPDATE or DELETE stmts */
+	GdaStatement *mod_stmt;
+	
+	if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+		goto out;
+	}
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+	g_error_free (error);
+	error = NULL;
+
+	mod_stmt = stmt_from_string ("BEGIN");
+	if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+		goto out;
+	}
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+	g_error_free (error);
+	error = NULL;
+	g_object_unref (mod_stmt);
+
+	/* test INSERT with undefined params */
+	mod_stmt = stmt_from_string ("INSERT INTO customers (name) VALUES (##aname::string)");
+	if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+		goto out;
+	}
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+	g_error_free (error);
+	error = NULL;
+
+ out:
+	g_object_unref (model);
+	g_object_unref (stmt);
+	
+	return nfailed;
+}
+
+/* Returns the number of failures */
+static gint
+test2 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt, *mod_stmt;
+	gint nfailed = 0;
+	GdaSet *params;
+
+	/* create GdaPModel */
+	stmt = stmt_from_string ("SELECT * FROM customers WHERE country = ##country::string");
+	if (!gda_statement_get_parameters (stmt, &params, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not get SELECT's parameters!\n");
+#endif
+		goto out;
+	}
+	gda_set_set_holder_value (params, "country", "SP");
+	model = gda_connection_statement_execute_select (cnc, stmt, params, &error);
+	g_object_unref (params);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not execute SELECT!\n");
+#endif
+		goto out;
+	}
+	if (!GDA_IS_PMODEL (model)) {
+		g_print ("Data model should be a GdaPModel!\n");
+		exit (EXIT_FAILURE);
+	}
+
+
+	/* test INSERT with params of the wrong type */
+	mod_stmt = stmt_from_string ("INSERT INTO customers (name, country) VALUES (##+1::string, ##country::date)");
+	if (gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have failed\n");
+#endif
+		goto out;
+	}
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+	g_error_free (error);
+	error = NULL;
+	g_object_unref (mod_stmt);
+
+	/* test correct INSERT */
+	mod_stmt = stmt_from_string ("INSERT INTO customers (name, country) VALUES (##+1::string, ##country::string)");
+	if (!gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have succedded, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+
+ out:	
+	g_object_unref (model);
+	g_object_unref (stmt);
+	
+	return nfailed;
+}
+
+/* Returns the number of failures */
+static gint
+test3 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt, *mod_stmt;
+	gint nfailed = 0;
+
+	/* create GdaPModel */
+	stmt = stmt_from_string ("SELECT * FROM customers");
+	model = gda_connection_statement_execute_select (cnc, stmt, NULL, &error);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not execute SELECT!\n");
+#endif
+		goto out;
+	}
+	if (!GDA_IS_PMODEL (model)) {
+		g_print ("Data model should be a GdaPModel!\n");
+		exit (EXIT_FAILURE);
+	}
+
+	/* test INSERT with undefined params */
+	mod_stmt = stmt_from_string ("UPDATE customers SET name = ##+1::string WHERE id = ##-0::gint");
+	if (!gda_pmodel_set_modification_statement (GDA_PMODEL (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_pmodel_set_modification_statement() should have succedded, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+	GValue *value;
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "Jack");
+	if (! gda_data_model_set_value_at (model, 1, 0, value, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_set_value_at failed: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+	if (gda_data_model_set_value_at (model, 0, 0, value, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_set_value_at should have failed\n");
+#endif
+		goto out;
+	}
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Got expected error: %s\n", error && error->message ? error->message : "No detail");
+#endif
+	g_error_free (error);
+	gda_value_free (value);
+
+ out:
+	g_object_unref (model);
+	g_object_unref (stmt);
+	
+	return nfailed;
+}

Modified: trunk/tests/meta-store/common.c
==============================================================================
--- trunk/tests/meta-store/common.c	(original)
+++ trunk/tests/meta-store/common.c	Fri Aug 15 19:41:51 2008
@@ -107,9 +107,9 @@
 
 /*
  * Loading a CSV file
- * ... is a (-1 terminated) list of triplets composed of:
+ * ... is a (-1 terminated) list of pairs composed of:
  *   - a column number (gint)
- *   - the column (gchar *)
+ *   - the column type (gchar *)
  */
 GdaDataModel *
 common_load_csv_file (const gchar *data_file, ...)
@@ -511,15 +511,15 @@
 	TEST_HEADER;
 
 	/* load CSV file */
-	import = common_load_csv_file ("data_routines.csv", 8, "boolean", 14, "boolean", 16, "boolean", -1);
+	import = common_load_csv_file ("data_routines.csv", 8, "boolean", 9, "gint", 15, "boolean", 17, "boolean", -1);
 	common_declare_expected_insertions_from_model (TNAME, import);
 	TEST_MODIFY (store, TNAME, import, NULL, &error, NULL);
 	TEST_END (import);
 
 	/* remove some lines */
-	DECL_CHANGE (TNAME, GDA_META_STORE_REMOVE, "-0", "meta", "-1", "information_schema", "-2", "_pg_numeric_precision_radix_10632", NULL);
+	DECL_CHANGE (TNAME, GDA_META_STORE_REMOVE, "-0", "meta", "-1", "information_schema", "-2", "_pg_numeric_precision_radix_11324", NULL);
 	TEST_MODIFY (store, TNAME, NULL, 
-		     "specific_name='_pg_numeric_precision_radix_10632'", &error, NULL);
+		     "specific_name='_pg_numeric_precision_radix_11324'", &error, NULL);
 	TEST_END (NULL);
 #undef TNAME
 }

Modified: trunk/tests/meta-store/data_routines.csv
==============================================================================
--- trunk/tests/meta-store/data_routines.csv	(original)
+++ trunk/tests/meta-store/data_routines.csv	Fri Aug 15 19:41:51 2008
@@ -1,6 +1,6 @@
-"meta","information_schema","_pg_truetypid_10627","meta","information_schema","_pg_truetypid","FUNCTION","pg_catalog.oid",FALSE,"SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypid","information_schema._pg_truetypid","postgres"
-"meta","information_schema","_pg_truetypmod_10628","meta","information_schema","_pg_truetypmod","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypmod","information_schema._pg_truetypmod","postgres"
-"meta","information_schema","_pg_char_max_length_10629","meta","information_schema","_pg_char_max_length","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_truetypid_11319","meta","information_schema","_pg_truetypid","FUNCTION","pg_catalog.oid",FALSE,"2","SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypid","information_schema._pg_truetypid","postgres"
+"meta","information_schema","_pg_truetypmod_11320","meta","information_schema","_pg_truetypmod","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_truetypmod","information_schema._pg_truetypmod","postgres"
+"meta","information_schema","_pg_char_max_length_11321","meta","information_schema","_pg_char_max_length","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
   CASE WHEN $2 = -1 /* default typmod */
        THEN null
        WHEN $1 IN (1042, 1043) /* char, varchar */
@@ -9,12 +9,12 @@
        THEN $2
        ELSE null
   END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_char_max_length","information_schema._pg_char_max_length","postgres"
-"meta","information_schema","_pg_char_octet_length_10630","meta","information_schema","_pg_char_octet_length","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_char_octet_length_11322","meta","information_schema","_pg_char_octet_length","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
   CASE WHEN $1 IN (25, 1042, 1043) /* text, char, varchar */
        THEN CAST(2^30 AS integer)
        ELSE null
   END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_char_octet_length","information_schema._pg_char_octet_length","postgres"
-"meta","information_schema","_pg_numeric_precision_10631","meta","information_schema","_pg_numeric_precision","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_numeric_precision_11323","meta","information_schema","_pg_numeric_precision","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
   CASE $1
          WHEN 21 /*int2*/ THEN 16
          WHEN 23 /*int4*/ THEN 32
@@ -28,7 +28,7 @@
          WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
          ELSE null
   END","NULL","SQL","GENERAL",TRUE,"MODIFIES",TRUE,"NULL","information_schema._pg_numeric_precision","information_schema._pg_numeric_precision","postgres"
-"meta","information_schema","_pg_numeric_precision_radix_10632","meta","information_schema","_pg_numeric_precision_radix","FUNCTION","pg_catalog.int4",FALSE,"SQL","SELECT
+"meta","information_schema","_pg_numeric_precision_radix_11324","meta","information_schema","_pg_numeric_precision_radix","FUNCTION","pg_catalog.int4",FALSE,"2","SQL","SELECT
   CASE WHEN $1 IN (21, 23, 20, 700, 701) THEN 2
        WHEN $1 IN (1700) THEN 10
        ELSE null

Modified: trunk/tests/test-cnc-utils.c
==============================================================================
--- trunk/tests/test-cnc-utils.c	(original)
+++ trunk/tests/test-cnc-utils.c	Fri Aug 15 19:41:51 2008
@@ -321,3 +321,129 @@
 	TO_IMPLEMENT;
 	return FALSE;
 }
+
+/*
+ * Load data from file @file into table @table
+ */
+gboolean
+test_cnc_load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *full_file, GError **error)
+{
+	GdaStatement *stmt;
+	GdaSet *params = NULL;
+	GdaDataModel *import;
+	gint nrows, ncols, i;
+	GdaMetaStruct *mstruct = NULL;
+	GSList *list;
+	gboolean retval = TRUE;
+
+	/* loading XML file */
+	import = gda_data_model_import_new_file (full_file, TRUE, NULL);
+	if (gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (import))) {
+		g_set_error (error, 0, 0, "Error loading '%s' file", full_file);
+		return FALSE;
+	}
+
+	/* retreiving meta data info */
+	GdaMetaDbObject *table_dbo;
+	GValue *name_value;
+	g_value_set_string ((name_value = gda_value_new (G_TYPE_STRING)), table);
+	mstruct = gda_meta_struct_new (gda_connection_get_meta_store (cnc), GDA_META_STRUCT_FEATURE_NONE);
+	table_dbo = gda_meta_struct_complement (mstruct, GDA_META_DB_TABLE,
+						NULL, NULL, name_value, error);
+	gda_value_free (name_value);
+	if (! table_dbo) {
+		retval = FALSE;
+		goto out;
+	}
+
+	/* creating INSERT statement */
+	GdaSqlStatement *st;
+	GdaSqlStatementInsert *ist;
+	GSList *insert_values_list = NULL;
+	
+	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 (table);
+
+	GdaMetaTable *mtable = GDA_META_TABLE (table_dbo);
+	for (list = mtable->columns; list; list = list->next) {
+		GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
+		GdaSqlField *field;
+
+		/* field */
+		field = gda_sql_field_new (GDA_SQL_ANY_PART (ist));
+		field->field_name = g_strdup (tcol->column_name);
+		ist->fields_list = g_slist_append (ist->fields_list, field);
+
+		/* value */
+		GdaSqlParamSpec *pspec = g_new0 (GdaSqlParamSpec, 1);
+		GdaSqlExpr *expr;
+		pspec->name = g_strdup (tcol->column_name);
+		pspec->g_type = tcol->gtype;
+		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);
+	}
+
+        ist->values_list = g_slist_append (NULL, insert_values_list);
+        st = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
+        st->contents = ist;
+	stmt = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
+        gda_sql_statement_free (st);
+	g_object_unref (mstruct);
+
+	if (! gda_statement_get_parameters (stmt, &params, error)) {
+		retval = FALSE;
+		goto out;
+	}
+
+	/* executing inserts */
+	nrows = gda_data_model_get_n_rows (import);
+	ncols = gda_data_model_get_n_columns (import);
+	if (!gda_connection_begin_transaction (cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN, error)) {
+		retval = FALSE;
+		goto out;
+	}
+	for (i = 0; i < nrows; i++) {
+		gint j;
+		GSList *list;
+		for (list = params->holders, j = 0; list && (j < ncols); list = list->next, j++) {
+			const GValue *cvalue = gda_data_model_get_value_at (import, j, i);
+			if (! gda_holder_set_value (GDA_HOLDER (list->data), cvalue)) {
+				g_set_error (error, 0, 0, "Error using value at col=>%d and row=>%d", j, i);
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
+				retval = FALSE;
+				goto out;
+			}
+		}
+
+		if (list || (j < ncols)) {
+			g_set_error (error, 0, 0,
+				     "Incoherent number of columns in table and imported data");
+			gda_connection_rollback_transaction (cnc, NULL, NULL);
+			retval = FALSE;
+			goto out;
+		}
+
+		if (gda_connection_statement_execute_non_select (cnc, stmt, params, NULL, error) == -1) {
+			gda_connection_rollback_transaction (cnc, NULL, NULL);
+			retval = FALSE;
+			goto out;
+		}
+	}
+
+	if (! gda_connection_commit_transaction (cnc, NULL, error))
+		retval = FALSE;
+
+ out:
+	if (import)
+		g_object_unref (import);
+	if (stmt)
+		g_object_unref (stmt);
+	if (params)
+		g_object_unref (params);
+
+	return retval;
+}

Modified: trunk/tests/test-cnc-utils.h
==============================================================================
--- trunk/tests/test-cnc-utils.h	(original)
+++ trunk/tests/test-cnc-utils.h	Fri Aug 15 19:41:51 2008
@@ -10,4 +10,6 @@
 gboolean       test_cnc_setup_db_contents (GdaConnection *cnc, const gchar *data_file, GError **error);
 gboolean       test_cnc_clean_connection (GdaConnection *cnc, GError **error);
 
+gboolean       test_cnc_load_data_from_file (GdaConnection *cnc, const gchar *table, const gchar *full_file, GError **error);
+
 #endif



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