libgda r3202 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite/virtual tests/data-models



Author: vivien
Date: Sun Sep  7 19:06:38 2008
New Revision: 3202
URL: http://svn.gnome.org/viewvc/libgda?rev=3202&view=rev

Log:
2008-09-07  Vivien Malerba <malerba gnome-db org>

	* libgda/gda-data-select.[ch]:
	* tests/data-models/check_pmodel.c: correctly handle the situation where the value
	of the primary key (or the column which composes the unique row condition) is modified,
	and added a "safety lock" feature which prevents further modification to the data model
	if the unique row condition is incorrect
	* doc/C: documentation improvements, and now the generated file when running "make PDF"
	is libgda-4.0-doc.pdf (for the V4 ABI).
	* libgda/sqlite/virtual/gda-vconnection-hub.c: no need anymore to use a GdaDataModelQuery
	object
	* libgda/Makefile.am:
	* libgda/gda-data-select-extra.h:
	* libgda/gda-data-select.[ch]: enable "exporting" all the statements and other information
	computed while the object was being used to be used by the new GdaDataModelQuery
	implementation
	* libgda/gda-data-model-query.[ch]: re-wrote the object to use the GdaDataSelect's features
	instead or re-implementing them in the object
	* libgda/tests/data-models/Makefile.am:
	* libgda/tests/data-models/check_model_query.c: new GdaDataModelQuery test
	* libgda/tests/data-models/check_pmodel.c: new tests


Added:
   trunk/doc/C/writable_data_model.dia   (contents, props changed)
   trunk/doc/C/writable_data_model.png   (contents, props changed)
   trunk/libgda/gda-data-select-extra.h
   trunk/tests/data-models/check_model_query.c
Modified:
   trunk/ChangeLog
   trunk/doc/C/Makefile.am
   trunk/doc/C/tmpl/gda-data-model-query.sgml
   trunk/doc/C/tmpl/gda-data-select.sgml
   trunk/libgda/Makefile.am
   trunk/libgda/gda-data-model-iter.c
   trunk/libgda/gda-data-model-query.c
   trunk/libgda/gda-data-model-query.h
   trunk/libgda/gda-data-model.c
   trunk/libgda/gda-data-select.c
   trunk/libgda/gda-data-select.h
   trunk/libgda/sqlite/virtual/gda-vconnection-hub.c
   trunk/tests/data-models/   (props changed)
   trunk/tests/data-models/Makefile.am
   trunk/tests/data-models/check_pmodel.c

Modified: trunk/doc/C/Makefile.am
==============================================================================
--- trunk/doc/C/Makefile.am	(original)
+++ trunk/doc/C/Makefile.am	Sun Sep  7 19:06:38 2008
@@ -49,7 +49,8 @@
 HTML_IMAGES = DataModels.png \
 	architecture.png parts.png stmt-unknown.png stmt-select.png stmt-insert1.png stmt-insert2.png \
 	stmt-update.png stmt-compound.png information_schema.png \
-	MetaStore1.png MetaStore2.png i_s_data_types.png
+	MetaStore1.png MetaStore2.png i_s_data_types.png \
+	writable_data_model.png
 
 # Extra options to supply to gtkdoc-fixref
 FIXXREF_OPTIONS=
@@ -67,7 +68,7 @@
 # Files not to distribute
 # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
 # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
-DISTCLEANFILES = $(DOC_MODULE)-docs.sgml.pdf
+DISTCLEANFILES = $(DOC_MODULE)-doc.pdf
 
 DOC_STAMPS += pdf-build.stamp
 CLEANFILES += $(DOC_STAMPS)
@@ -76,6 +77,6 @@
 PDF: pdf-build.stamp
 pdf-build.stamp:
 	if test -f $(srcdir)/$(DOC_MAIN_SGML_FILE); then \
-		dblatex $(srcdir)/$(DOC_MAIN_SGML_FILE); \
+		dblatex $(srcdir)/$(DOC_MAIN_SGML_FILE) -o $(DOC_MODULE)-doc.pdf ; \
 	fi
 	touch pdf-build.stamp

Modified: trunk/doc/C/tmpl/gda-data-model-query.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-model-query.sgml	(original)
+++ trunk/doc/C/tmpl/gda-data-model-query.sgml	Sun Sep  7 19:06:38 2008
@@ -30,38 +30,16 @@
 
 </para>
 
-<!-- ##### ARG GdaDataModelQuery:delete-query ##### -->
+<!-- ##### ARG GdaDataModelQuery:exec-params ##### -->
 <para>
 
 </para>
 
-<!-- ##### ARG GdaDataModelQuery:insert-query ##### -->
+<!-- ##### ARG GdaDataModelQuery:statement ##### -->
 <para>
 
 </para>
 
-<!-- ##### ARG GdaDataModelQuery:query ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG GdaDataModelQuery:update-query ##### -->
-<para>
-
-</para>
-
-<!-- ##### ARG GdaDataModelQuery:use-transaction ##### -->
-<para>
-
-</para>
-
-<!-- ##### ENUM GdaDataModelQueryOptions ##### -->
-<para>
-
-</para>
-
- GDA_DATA_MODEL_QUERY_OPTION_USE_ALL_FIELDS_IF_NO_PK: 
-
 <!-- ##### STRUCT GdaDataModelQueryClass ##### -->
 <para>
 
@@ -82,15 +60,7 @@
 
 @cnc: 
 @select_stmt: 
- Returns: 
-
-
-<!-- ##### FUNCTION gda_data_model_query_get_parameter_list ##### -->
-<para>
-
-</para>
-
- model: 
+ params: 
 @Returns: 
 
 
@@ -104,26 +74,3 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_data_model_query_set_modification_query ##### -->
-<para>
-
-</para>
-
- model: 
- mod_stmt: 
- error: 
- Returns: 
-
-
-<!-- ##### FUNCTION gda_data_model_query_compute_modification_queries ##### -->
-<para>
-
-</para>
-
- model: 
- target: 
- options: 
- error: 
- Returns: 
-
-

Modified: trunk/doc/C/tmpl/gda-data-select.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-select.sgml	(original)
+++ trunk/doc/C/tmpl/gda-data-select.sgml	Sun Sep  7 19:06:38 2008
@@ -15,7 +15,23 @@
   The default behaviour however is to disallow modifications, and this section documents how to characterize
   a <link linkend="GdaDataSelect">GdaDataSelect</link> to allow modifications. Once this is done, any modification
   done to the data model whill be propagated to the modified table in the database using INSERT, UPDATE or DELETE
-  statements, while the data in the data model will always be kept up to date with the modifications done.
+  statements.
+</para>
+<para>
+  After any modification, it is still possible to read values from the data model (even values for rows which have
+  been modified or inserted). The data model might then execute some SELECT statement to fetch some actualized values.
+  Note: there is a corner case where a modification made to a row would make the row not selected at first in the data model
+  (for example is the original SELECT statement included a clause <![CDATA["WHERE id < 100"]]> and the modification sets the 
+  <![CDATA["id"]]> value to 110), then the row will still be in the data model even though it would not be if the SELECT statement
+  which execution created the data model in the first place was re-run. This is illustrated in the schema below:
+  <mediaobject>
+    <imageobject role="html">
+      <imagedata fileref="writable_data_model.png" format="PNG" contentwidth="100mm"/>
+    </imageobject>
+    <textobject>
+      <phrase>GdaDataSelect data model's contents after some modifications</phrase>
+    </textobject>
+  </mediaobject>
 </para>
 
 <!-- ##### SECTION See_Also ##### -->

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

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

Modified: trunk/libgda/Makefile.am
==============================================================================
--- trunk/libgda/Makefile.am	(original)
+++ trunk/libgda/Makefile.am	Sun Sep  7 19:06:38 2008
@@ -100,6 +100,7 @@
 	gda-data-access-wrapper.c \
 	gda-data-proxy.c \
 	gda-data-select.c \
+	gda-data-select-extra.h \
 	gda-easy.c \
 	gda-holder.c \
 	gda-init.c \

Modified: trunk/libgda/gda-data-model-iter.c
==============================================================================
--- trunk/libgda/gda-data-model-iter.c	(original)
+++ trunk/libgda/gda-data-model-iter.c	Sun Sep  7 19:06:38 2008
@@ -527,9 +527,13 @@
  * @iter: a #GdaDataModelIter object
  * @row: the row to set @iter to
  *
- * Synchronizes the values of the parameters in @iter with the values at the @row row
+ * Synchronizes the values of the parameters in @iter with the values at the @row row.
  *
- * If @row < 0 then @iter is not bound to any row of the data model it iters through.
+ * If @row is not a valid row, then the returned value is FALSE, and the "current-row"
+ * property is set to -1.
+ *
+ * If any other error occurred then the returned value is FALSE, but the "current-row"
+ * property is set to the @row row.
  *
  * Returns: TRUE if no error occurred
  */
@@ -556,10 +560,17 @@
  * gda_data_model_iter_move_next
  * @iter: a #GdaDataModelIter object
  *
- * Moves @iter one row further than where it already is (synchronizes the values of the parameters in @iter 
- * with the values at the new row).
+ * Moves @iter one row further than where it already is 
+ * (synchronizes the values of the parameters in @iter with the values at the new row).
  *
- * Returns: TRUE if no error occurred
+ * If the iterator was on the data model's last row, then it can't be moved forward
+ * anymore, and the returned value is FALSE (nore also that the "current-row" property
+ * is set to -1).
+ *
+ * If any other error occurred then the returned value is FALSE, but the "current-row"
+ * property is set to the new current row (one row more than it was before the call).
+ *
+ * Returns: TRUE if the iterator is now at the next row
  */
 gboolean
 gda_data_model_iter_move_next (GdaDataModelIter *iter)
@@ -577,7 +588,14 @@
  * Moves @iter one row before where it already is (synchronizes the values of the parameters in @iter 
  * with the values at the new row).
  *
- * Returns: TRUE if no error occurred
+ * If the iterator was on the data model's first row, then it can't be moved backwards
+ * anymore, and the returned value is FALSE (nore also that the "current-row" property
+ * is set to -1).
+ *
+ * If any other error occurred then the returned value is FALSE, but the "current-row"
+ * property is set to the new current row (one row less than it was before the call).
+ *
+ * Returns: TRUE if the iterator is now at the previous row
  */
 gboolean
 gda_data_model_iter_move_prev (GdaDataModelIter *iter)

Modified: trunk/libgda/gda-data-model-query.c
==============================================================================
--- trunk/libgda/gda-data-model-query.c	(original)
+++ trunk/libgda/gda-data-model-query.c	Sun Sep  7 19:06:38 2008
@@ -27,57 +27,42 @@
 #include <libgda/gda-data-model-query.h>
 #include <libgda/gda-data-model-private.h>
 #include <libgda/gda-data-model-extra.h>
-#include <libgda/gda-data-model-iter.h>
-#include <libgda/gda-statement.h>
-#include <libgda/gda-meta-struct.h>
+#include <libgda/gda-data-select-extra.h>
 #include <libgda/gda-set.h>
-#include <libgda/gda-holder.h>
-#include <libgda/gda-util.h>
-#include <sql-parser/gda-sql-statement.h>
+#include <libgda/gda-statement.h>
+#include <libgda/gda-data-select.h>
 
 struct _GdaDataModelQueryPrivate {
 	GdaConnection    *cnc;
-	GdaStatement     *statements[4]; /* indexed by SEL_* */
-	GdaSet           *params[4];  /* parameters required to execute @query, indexed by SEL_* */
-
 	GdaDataModel     *data;
-	GError           *refresh_error; /* if @data is NULL, then can contain the error */
+	GdaDataSelectInternals *inter;
 
-	gboolean          multiple_updates;
-	gboolean          needs_refresh;
-	GSList           *columns;
+	GError           *exec_error;
+	GdaStatement     *select_stmt;
+	GdaSet           *params;
 
-	gboolean          transaction_allowed;
-	gboolean          transaction_started; /* true if we have started a transaction here */
-	gboolean          transaction_needs_commit;
 	guint             svp_id;   /* counter for savepoints */
+	gboolean          transaction_needs_commit;
+	gboolean          transaction_allowed;
+        gboolean          transaction_started; /* true if we have started a transaction here */
 	gchar            *svp_name; /* non NULL if we have created a savepoint here */
-	/* note: transaction_started and svp_name are mutually exclusive */
-
-	gboolean          emit_reset;
+        /* note: transaction_started and svp_name are mutually exclusive */
 };
 
 /* properties */
 enum
 {
         PROP_0,
-        PROP_SEL_QUERY,
-	PROP_INS_QUERY,
-	PROP_UPD_QUERY,
-	PROP_DEL_QUERY,
-	PROP_USE_TRANSACTION,
-	PROP_CNC
+	PROP_CNC,
+	PROP_STMT,
+	PROP_PARAMS
 };
 
-enum 
-{
-	SEL_QUERY = 0,
-	INS_QUERY = 1,
-	UPD_QUERY = 2,
-	DEL_QUERY = 3
-};
 
 static void gda_data_model_query_class_init (GdaDataModelQueryClass *klass);
+static GObject *gda_data_model_query_constructor (GType type,
+						  guint n_construct_properties,
+						  GObjectConstructParam *construct_properties);
 static void gda_data_model_query_init       (GdaDataModelQuery *model,
 					      GdaDataModelQueryClass *klass);
 static void gda_data_model_query_dispose    (GObject *object);
@@ -101,27 +86,18 @@
 static const GValue        *gda_data_model_query_get_value_at    (GdaDataModel *model, gint col, gint row, GError **error);
 static GdaValueAttribute    gda_data_model_query_get_attributes_at (GdaDataModel *model, gint col, gint row);
 
-static GdaDataModelIter    *gda_data_model_query_create_iter     (GdaDataModel *model);
-
 static gboolean             gda_data_model_query_set_value_at    (GdaDataModel *model, gint col, gint row, 
 								   const GValue *value, GError **error);
 static gboolean             gda_data_model_query_set_values      (GdaDataModel *model, gint row, 
 								   GList *values, GError **error);
 static gint                 gda_data_model_query_append_values   (GdaDataModel *model, const GList *values, GError **error);
-static gint                 gda_data_model_query_append_row      (GdaDataModel *model, GError **error);
 static gboolean             gda_data_model_query_remove_row      (GdaDataModel *model, gint row, 
 								  GError **error);
 static void                 gda_data_model_query_send_hint       (GdaDataModel *model, GdaDataModelHint hint, 
 								  const GValue *hint_value);
 
-static void create_columns (GdaDataModelQuery *model);
-static void forget_statement (GdaDataModelQuery *model, GdaStatement *stmt);
 static void holder_changed_cb (GdaSet *paramlist, GdaHolder *param, GdaDataModelQuery *model);
 
-static void opt_start_transaction_or_svp (GdaDataModelQuery *selmodel);
-static void opt_end_transaction_or_svp (GdaDataModelQuery *selmodel);
-
-
 static GObjectClass *parent_class = NULL;
 
 /* module error */
@@ -189,41 +165,27 @@
                                          g_param_spec_object ("connection", "Connection to use", 
 							      "Connection to use to execute statements",
 							      GDA_TYPE_CONNECTION,
-							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+							      G_PARAM_READABLE | G_PARAM_WRITABLE |
+							      G_PARAM_CONSTRUCT_ONLY));
 
-	g_object_class_install_property (object_class, PROP_SEL_QUERY,
-                                         g_param_spec_object ("query", "SELECT query", 
-							      "SELECT Query to be executed to populate "
+	g_object_class_install_property (object_class, PROP_STMT,
+                                         g_param_spec_object ("statement", "SELECT statement", 
+							      "SELECT statement to be executed to populate "
 							      "the model with data", 
 							      GDA_TYPE_STATEMENT,
 							      G_PARAM_READABLE | G_PARAM_WRITABLE |
 							      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",
-							      GDA_TYPE_STATEMENT,
-							      G_PARAM_READABLE | G_PARAM_WRITABLE));
-
-	g_object_class_install_property (object_class, PROP_UPD_QUERY,
-                                         g_param_spec_object ("update_query", "UPDATE query", 
-							      "UPDATE Query to be executed to update data",
-							      GDA_TYPE_STATEMENT,
-							      G_PARAM_READABLE | G_PARAM_WRITABLE));
+	g_object_class_install_property (object_class, PROP_PARAMS,
+                                         g_param_spec_object ("exec-params", "SELECT statement's parameters", 
+							      "Parameters used with the SELECT statement",
+							      GDA_TYPE_SET,
+							      G_PARAM_READABLE | G_PARAM_WRITABLE |
+							      G_PARAM_CONSTRUCT_ONLY));
 
-	g_object_class_install_property (object_class, PROP_DEL_QUERY,
-                                         g_param_spec_object ("delete_query", "DELETE query", 
-							      "DELETE Query to be executed to remove data",
-							      GDA_TYPE_STATEMENT,
-							      G_PARAM_READABLE | G_PARAM_WRITABLE));
-	
-	g_object_class_install_property (object_class, PROP_USE_TRANSACTION,
-                                         g_param_spec_boolean ("use_transaction", "Use transaction", 
-							       "Run modification statements within a transaction",
-                                                               FALSE,
-							       G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
 
 	/* virtual functions */
+	object_class->constructor = gda_data_model_query_constructor;
 	object_class->dispose = gda_data_model_query_dispose;
 	object_class->finalize = gda_data_model_query_finalize;
 }
@@ -238,7 +200,7 @@
 	iface->i_get_value_at = gda_data_model_query_get_value_at;
 	iface->i_get_attributes_at = gda_data_model_query_get_attributes_at;
 
-	iface->i_create_iter = gda_data_model_query_create_iter; 
+	iface->i_create_iter = NULL;
 	iface->i_iter_at_row = NULL;
 	iface->i_iter_next = NULL;
 	iface->i_iter_prev = NULL;
@@ -246,7 +208,7 @@
 	iface->i_set_value_at = gda_data_model_query_set_value_at;
 	iface->i_set_values = gda_data_model_query_set_values;
         iface->i_append_values = gda_data_model_query_append_values;
-	iface->i_append_row = gda_data_model_query_append_row;
+	iface->i_append_row = NULL;
 	iface->i_remove_row = gda_data_model_query_remove_row;
 	iface->i_find_row = NULL;
 	
@@ -261,20 +223,27 @@
 	g_return_if_fail (GDA_IS_DATA_MODEL_QUERY (model));
 	model->priv = g_new0 (GdaDataModelQueryPrivate, 1);
 	model->priv->data = NULL;
-	model->priv->refresh_error = NULL;
-	model->priv->columns = NULL;
+	model->priv->inter = NULL;
+	model->priv->exec_error = NULL;
+	model->priv->select_stmt = NULL;
+	model->priv->params = NULL;
+}
+
+static GObject *
+gda_data_model_query_constructor (GType type,
+				  guint n_construct_properties,
+				  GObjectConstructParam *construct_properties) 
+{
+	GObject *object;
+	GdaDataModelQuery *model;
 
-	/* model refreshing is performed as soon as any modification is done */
-	model->priv->multiple_updates = FALSE;
-	model->priv->needs_refresh = FALSE;
-
-	model->priv->transaction_allowed = FALSE;
-	model->priv->transaction_started = FALSE;
-	model->priv->transaction_needs_commit = FALSE;
-	model->priv->svp_id = 0;
-	model->priv->svp_name = NULL;
+	object = G_OBJECT_CLASS (parent_class)->constructor (type,
+							     n_construct_properties,
+							     construct_properties);
+	model = (GdaDataModelQuery *) object;
+	gda_data_model_query_refresh (model, NULL);
 
-	model->priv->emit_reset = FALSE;
+	return object;
 }
 
 static void
@@ -286,38 +255,24 @@
 
 	/* free memory */
 	if (model->priv) {
-		gint i;
-
 		if (model->priv->cnc) {
 			g_object_unref (model->priv->cnc);
 			model->priv->cnc = NULL;
 		}
 		
-		if (model->priv->transaction_started || model->priv->svp_name)
-			opt_end_transaction_or_svp (model);
-
-		if (model->priv->columns) {
-			g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
-			g_slist_free (model->priv->columns);
-			model->priv->columns = NULL;
+		if (model->priv->data) {
+			g_object_unref (model->priv->data);
+			model->priv->data = NULL;
 		}
 
-		for (i = SEL_QUERY; i <= DEL_QUERY; i++) {
-			if (model->priv->statements[i])
-				forget_statement (model, model->priv->statements[i]);
-		
-			if (model->priv->params[i]) {
-				if (i == SEL_QUERY)
-					g_signal_handlers_disconnect_by_func (model->priv->params[i],
-									      G_CALLBACK (holder_changed_cb), model);
-				g_object_unref (model->priv->params[i]);
-				model->priv->params[i] = NULL;
-			}
+		if (model->priv->select_stmt) {
+			g_object_unref (model->priv->select_stmt);
+			model->priv->select_stmt = NULL;
 		}
 
-		if (model->priv->data) {
-			g_object_unref (model->priv->data);
-			model->priv->data = NULL;
+		if (model->priv->params) {
+			g_object_unref (model->priv->params);
+			model->priv->params = NULL;
 		}
 	}
 
@@ -334,9 +289,12 @@
 
 	/* free memory */
 	if (model->priv) {
-		if (model->priv->refresh_error)
-			g_error_free (model->priv->refresh_error);
+		g_free (model->priv->svp_name);
+		if (model->priv->inter)
+			_gda_data_select_internals_free (model->priv->inter);
 
+		if (model->priv->exec_error)
+			g_error_free (model->priv->exec_error);
 		g_free (model->priv);
 		model->priv = NULL;
 	}
@@ -345,48 +303,6 @@
 	parent_class->finalize (object);
 }
 
-/*
- * 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;
-}
-
-static void
-check_param_type (GdaHolder *param, GType type)
-{
-	if ((type != 0) && (type !=  gda_holder_get_g_type (param))) {
-		g_warning (_("Type of parameter '%s' is '%s' when it should be '%s', "
-			     "GdaDataModelQuery object will not work correctly"),
-			   gda_holder_get_id (param), 
-			   g_type_name (gda_holder_get_g_type (param)),
-			   g_type_name (type));
-	}
-}
-
 static void
 gda_data_model_query_set_property (GObject *object,
 				   guint param_id,
@@ -394,118 +310,24 @@
 				   GParamSpec *pspec)
 {
 	GdaDataModelQuery *model;
-	gint qindex = param_id - 1;
 
 	model = GDA_DATA_MODEL_QUERY (object);
 	if (model->priv) {
 		switch (param_id) {
 		case PROP_CNC:
-			if (model->priv->cnc) {
-				g_object_unref (model->priv->cnc);
-				model->priv->cnc = NULL;
-			}
 			model->priv->cnc = g_value_get_object (value);
 			if (model->priv->cnc)
 				g_object_ref (model->priv->cnc);
 			break;
-		case PROP_SEL_QUERY:
-		case PROP_INS_QUERY:
-		case PROP_UPD_QUERY:
-		case PROP_DEL_QUERY:
-			if (model->priv->statements[qindex])
-				forget_statement (model, model->priv->statements[qindex]);
-			if (model->priv->params[qindex]) {
-				g_signal_handlers_disconnect_by_func (model->priv->params[qindex],
-								      G_CALLBACK (holder_changed_cb), model);
-				g_object_unref (model->priv->params[qindex]);
-				model->priv->params[qindex] = NULL;
-			}
-
-			model->priv->statements[qindex] = (GdaStatement *) g_value_get_object (value);
-			if (model->priv->statements[qindex]) {
-				/* make a copy of statement */
-				model->priv->statements[qindex] = gda_statement_copy (model->priv->statements[qindex]);
-				
-				if (!gda_statement_get_parameters (model->priv->statements[qindex], 
-								   &(model->priv->params[qindex]), NULL)) {
-					g_warning (_("Could not get statement's parameters, "
-						     "expect some problems with the GdaDataModelQuery"));
-					model->priv->params[qindex] = NULL;
-				}
-
-				if (qindex == SEL_QUERY) {
-					/* SELECT statement */
-					if (model->priv->params[qindex])
-						g_signal_connect (model->priv->params[qindex], "holder_changed",
-								  G_CALLBACK (holder_changed_cb), model);
-				}
-				else {
-					/* other statements: for all the parameters in the param list, 
-					 * if some have a name like "[+-]<num>", then they will be filled with
-					 * the value at the new/old <num>th column before being run, or, if the name is
-					 * the same as a parameter for the SELECT query, then bind them to that parameter */
-					gint num;
-
-					if (model->priv->params [qindex]) {
-						GSList *params;
-						for (params = model->priv->params [qindex]->holders; 
-						     params; params = params->next) {
-							GdaHolder *holder = GDA_HOLDER (params->data);
-							const gchar *pname = gda_holder_get_id (holder);
-							gboolean old_val;
-							
-							if (param_name_to_int (pname, &num, &old_val)) {
-								if (old_val)
-									g_object_set_data ((GObject*) holder, "-num", 
-											   GINT_TO_POINTER (num + 1));
-								else
-									g_object_set_data ((GObject*) holder, "+num", 
-											   GINT_TO_POINTER (num + 1));
-								g_object_set_data ((GObject*) holder, "_num",
-										   GINT_TO_POINTER (num + 1));
-								GdaColumn *col;
-								col = gda_data_model_describe_column ((GdaDataModel *) model,
-												      num);
-								if (col) {
-									check_param_type (holder, gda_column_get_g_type (col));
-									gda_holder_set_not_null (holder,
-												 !gda_column_get_allow_null (col));
-									if (gda_column_get_auto_increment (col) ||
-									    gda_column_get_default_value (col)) {
-										GValue *value;
-										value = gda_value_new_null ();
-										gda_holder_set_default_value (holder, value);
-										gda_value_free (value);
-									}
-								}
-							}
-							else {
-								if (pname && model->priv->params [SEL_QUERY]) {
-									GdaHolder *bind_to;
-									bind_to = gda_set_get_holder 
-										(model->priv->params [SEL_QUERY],
-										 pname);
-									if (bind_to) {
-										check_param_type (holder,
-												  gda_holder_get_g_type (bind_to));
-										if (! gda_holder_set_bind (holder, bind_to, NULL))
-											TO_IMPLEMENT;
-									}
-									else
-										g_warning (_("Could not find a parameter named "
-											     "'%s' among the SELECT query's "
-											     "parameters, the specified "
-											     "modification query will not be executable"),
-											   pname);
-								}
-							}
-						}
-					}
-				}
-			}
+		case PROP_STMT:
+			model->priv->select_stmt = g_value_get_object (value);
+			if (model->priv->select_stmt)
+				g_object_ref (model->priv->select_stmt);
 			break;
-		case PROP_USE_TRANSACTION:
-			model->priv->transaction_allowed = g_value_get_boolean (value);
+		case PROP_PARAMS:
+			model->priv->params = g_value_get_object (value);
+			if (model->priv->params)
+				g_object_ref (model->priv->params);
 			break;
 		default:
 			g_assert_not_reached ();
@@ -521,7 +343,6 @@
 				   GParamSpec *pspec)
 {
 	GdaDataModelQuery *model;
-	gint qindex = param_id - 1;
 
 	model = GDA_DATA_MODEL_QUERY (object);
 	if (model->priv) {
@@ -529,14 +350,11 @@
 		case PROP_CNC:
 			g_value_set_object (value, model->priv->cnc);
 			break;
-		case PROP_SEL_QUERY:
-		case PROP_INS_QUERY:
-		case PROP_UPD_QUERY:
-		case PROP_DEL_QUERY:
-			g_value_set_object (value, G_OBJECT (model->priv->statements[qindex]));
+		case PROP_STMT:
+			g_value_set_object (value, model->priv->select_stmt);
 			break;
-		case PROP_USE_TRANSACTION:
-			g_value_set_boolean (value, model->priv->transaction_allowed);
+		case PROP_PARAMS:
+			g_value_set_object (value, model->priv->params);
 			break;
 		default:
 			g_assert_not_reached ();
@@ -545,97 +363,77 @@
 	}
 }
 
-static void
-forget_statement (GdaDataModelQuery *model, GdaStatement *stmt)
-{
-	gint i;
-	gint qindex = -1;
-	
-	for (i = SEL_QUERY; (i <= DEL_QUERY) && (qindex < 0); i++) 
-		if (stmt == model->priv->statements [i])
-			qindex = i;
-	g_assert (qindex >= 0);
-
-	model->priv->statements [qindex] = NULL;
-	g_object_unref (stmt);
-}
 
 static void
 holder_changed_cb (GdaSet *paramlist, GdaHolder *param, GdaDataModelQuery *model)
 {
-	/* first: sync the parameters which are bound */
-	gboolean allok = TRUE;
-	if (model->priv->params [SEL_QUERY]) {
-		gint i;
-		for (i = INS_QUERY; i <= DEL_QUERY; i++) {
-			if (model->priv->params [i]) {
-				GSList *params;
-				for (params = model->priv->params [i]->holders; params; params = params->next) {
-					GdaHolder *bind_to;
-					bind_to = gda_holder_get_bind (GDA_HOLDER (params->data));
-					if (bind_to == param)
-						if (!gda_holder_set_value (GDA_HOLDER (params->data),
-									   gda_holder_get_value (bind_to), NULL))
-							allok = FALSE;
-				}
-			}
-		}
-	}
-
-	/* second: do a refresh */
-	if (allok && gda_set_is_valid (paramlist))
-		gda_data_model_query_refresh (model, NULL);
-	else {
-		if (model->priv->data) {
-			g_object_unref (model->priv->data);
-			model->priv->data = NULL;
-		}
-		gda_data_model_reset ((GdaDataModel *) model);
-	}
 }
 
 /**
  * gda_data_model_query_new
- * @cnc: a #GdaConnection object, or %NULL
+ * @cnc: a #GdaConnection object
  * @select_stmt: a SELECT statement 
+ * @params: a #GdaSet object containing @select_stmt's execution parameters (see gda_statement_get_parameters()), or %NULL
  *
  * Creates a new #GdaDataModel object using the data returned by the execution of the
  * @select_stmt SELECT statement.
  *
- * If @cnc is %NULL, then a real connection will have to be set before anything usefull can be done
- *
  * Returns: a pointer to the newly created #GdaDataModel.
  */
-GdaDataModel *
-gda_data_model_query_new (GdaConnection *cnc, GdaStatement *select_stmt)
+GdaDataModelQuery *
+gda_data_model_query_new (GdaConnection *cnc, GdaStatement *select_stmt, GdaSet *params)
 {
-	GdaDataModelQuery *model;
-
-	g_return_val_if_fail (!cnc || GDA_IS_CONNECTION (cnc), NULL);
+	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 	g_return_val_if_fail (GDA_IS_STATEMENT (select_stmt), NULL);
+	g_return_val_if_fail (!params || GDA_IS_SET (params), NULL);
 
-	model = g_object_new (GDA_TYPE_DATA_MODEL_QUERY, "connection", cnc, 
-			      "query", select_stmt, NULL);
+	return (GdaDataModelQuery*)  g_object_new (GDA_TYPE_DATA_MODEL_QUERY, 
+						   "connection", cnc, 
+						   "statement", select_stmt, 
+						   "exec-params", params, NULL);
+}
 
-	return GDA_DATA_MODEL (model);
+static gboolean
+do_notify_changes (GdaDataModel *model)
+{
+	gboolean notify_changes = TRUE;
+	if (GDA_DATA_MODEL_GET_CLASS (model)->i_get_notify)
+		notify_changes = (GDA_DATA_MODEL_GET_CLASS (model)->i_get_notify) (model);
+	return notify_changes;
 }
 
-/**
- * gda_data_model_query_get_parameter_list
- * @model: a #GdaDataModelQuery data model
- *
- * If some parameters are required to execute the SELECT query used in the @model data model, then
- * returns the #GdaSet used; otherwise does nothing and returns %NULL.
- *
- * Returns: a #GdaSet object, or %NULL
- */
-GdaSet *
-gda_data_model_query_get_parameter_list (GdaDataModelQuery *model)
+static void
+data_select_changed_cb (GdaDataModel *priv, GdaDataModelQuery *model)
 {
-	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), NULL);
-	g_return_val_if_fail (model->priv, NULL);
+	if (do_notify_changes ((GdaDataModel*) model))
+		g_signal_emit_by_name (model, "changed");
+}
+
+static void
+data_select_reset_cb (GdaDataModel *priv, GdaDataModelQuery *model)
+{
+	g_signal_emit_by_name (model, "reset");
+}
+
+static void
+data_select_row_inserted_cb (GdaDataModel *priv, gint row, GdaDataModelQuery *model)
+{
+	if (do_notify_changes ((GdaDataModel*) model))
+		g_signal_emit_by_name (model, "row-inserted", row);
+}
 
-	return model->priv->params [SEL_QUERY];
+static void
+data_select_row_removed_cb (GdaDataModel *priv, gint row, GdaDataModelQuery *model)
+{
+	if (do_notify_changes ((GdaDataModel*) model))
+		g_signal_emit_by_name (model, "row-removed", row);
+}
+
+static void
+data_select_row_updated_cb (GdaDataModel *priv, gint row, GdaDataModelQuery *model)
+{
+	if (do_notify_changes ((GdaDataModel*) model))
+		g_signal_emit_by_name (model, "row-updated", row);
 }
 
 /**
@@ -654,89 +452,264 @@
 	g_return_val_if_fail (model->priv, FALSE);
 
 	if (model->priv->data) {
+		/* copy the internals of the GdaDataSelect object */
+		model->priv->inter = _gda_data_select_internals_steal ((GdaDataSelect*) model->priv->data);
 		g_object_unref (model->priv->data);
 		model->priv->data = NULL;
 	}
-	if (model->priv->refresh_error) {
-		g_error_free (model->priv->refresh_error);
-		model->priv->refresh_error = NULL;
-	}
 
-	if (! model->priv->statements[SEL_QUERY])
-		return TRUE;
+	if (model->priv->exec_error) {
+		g_error_free (model->priv->exec_error);
+		model->priv->exec_error = NULL;
+	}
 	
 	if (! model->priv->cnc) {
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
+		g_set_error (&(model->priv->exec_error), GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
 			     _("No connection specified"));
+		g_propagate_error (error, g_error_copy (model->priv->exec_error));
 		return FALSE;
 	}
 	if (! gda_connection_is_opened (model->priv->cnc)) {
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
+		g_set_error (&(model->priv->exec_error), GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
 			     _("Specified connection is closed"));
+		g_propagate_error (error, g_error_copy (model->priv->exec_error));
 		return FALSE;
 	}
 
 	model->priv->data = gda_connection_statement_execute_select (model->priv->cnc, 
-								     model->priv->statements[SEL_QUERY], 
-								     model->priv->params [SEL_QUERY],
-								     &model->priv->refresh_error);
+								     model->priv->select_stmt, 
+								     model->priv->params, &(model->priv->exec_error));
 	if (!model->priv->data) {
-		if (model->priv->refresh_error && error)
-			*error = g_error_copy (model->priv->refresh_error);
+		g_propagate_error (error, g_error_copy (model->priv->exec_error));
 		return FALSE;
 	}
-	
+
+	/* if there were some internals from a previous GdaDataSelect, then set the there */
+	if (model->priv->inter) {
+		_gda_data_select_internals_paste ((GdaDataSelect*) model->priv->data, model->priv->inter);
+		model->priv->inter = NULL;
+	}
+
+	/* be ready to propagate signals */
+	g_signal_connect (model->priv->data, "changed",
+			  G_CALLBACK (data_select_changed_cb), model);
+	g_signal_connect (model->priv->data, "reset",
+			  G_CALLBACK (data_select_reset_cb), model);
+	g_signal_connect (model->priv->data, "row-inserted",
+			  G_CALLBACK (data_select_row_inserted_cb), model);
+	g_signal_connect (model->priv->data, "row-removed",
+			  G_CALLBACK (data_select_row_removed_cb), model);
+	g_signal_connect (model->priv->data, "row-updated",
+			  G_CALLBACK (data_select_row_updated_cb), model);
+
 	gda_data_model_reset ((GdaDataModel *) model);
 	return TRUE;
 }
 
 /**
- * gda_data_model_query_set_modification_query
- * @model: a #GdaDataModelQuery data model
- * @mod_stmt: a #GdaStatement object
+ * gda_data_model_query_set_row_selection_condition
+ * @model: a #GdaDataSelect data model
+ * @expr: a #GdaSqlExpr expression
+ * @error: a place to store errors, or %NULL
+ *
+ * Offers the same features as gda_data_model_query_set_row_selection_condition_sql() but using a #GdaSqlExpr
+ * structure instead of an SQL syntax.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_data_model_query_set_row_selection_condition (GdaDataModelQuery *model, GdaSqlExpr *expr, GError **error)
+{
+	GdaDataModelQuery *selmodel;
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
+
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
+		return FALSE;
+	}
+	else
+		return gda_data_select_set_row_selection_condition (GDA_DATA_SELECT (selmodel->priv->data), expr, error);
+}
+
+/**
+ * gda_data_model_query_set_row_selection_condition_sql
+ * @model: a #GdaDataSelect data model
+ * @sql_where: an SQL condition (withouth the WHERE keyword)
+ * @error: a place to store errors, or %NULL
+ *
+ * Specifies the SQL condition corresponding to the WHERE part of a SELECT statement which would
+ * return only 1 row (the expression of the primary key).
+ *
+ * For example for a table created as <![CDATA["CREATE TABLE mytable (part1 int NOT NULL, part2 string NOT NULL, 
+ * name string, PRIMARY KEY (part1, part2))"]]>, and if @pmodel corresponds to the execution of the 
+ * <![CDATA["SELECT name, part1, part2 FROM mytable"]]>, then the sensible value for @sql_where would be
+ * <![CDATA["part1 = ##-1::int AND part2 = ##-2::string"]]> because the values of the 'part1' field are located
+ * in @pmodel's column number 1 and the values of the 'part2' field are located
+ * in @pmodel's column number 2 and the primary key is composed of (part1, part2).
+ *
+ * For more information about the syntax of the parameters (named <![CDATA["##-1::int"]]> for example), see the
+ * <link linkend="GdaSqlParser.description">GdaSqlParser</link> documentation, and 
+ * gda_data_model_query_set_modification_statement().
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_data_model_query_set_row_selection_condition_sql (GdaDataModelQuery *model, const gchar *sql_where, GError **error)
+{
+	GdaDataModelQuery *selmodel;
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
+
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
+		return FALSE;
+	}
+	else
+		return gda_data_select_set_row_selection_condition_sql (GDA_DATA_SELECT (selmodel->priv->data), sql_where, error);
+}
+
+/**
+ * gda_data_model_query_compute_row_selection_condition
+ * @model: a #GdaDataSelect object
  * @error: a place to store errors, or %NULL
  *
- * Sets the modification statement to be used by @model to actually perform any change
- * to the dataset in the database.
+ * Offers the same features as gda_data_model_query_set_row_selection_condition() but the expression
+ * is computed from the meta data associated to the connection being used when @model was created.
  *
- * The provided @mod_stmt statement must be either a INSERT, UPDATE or DELETE query. It can contain
- * parameters, and the parameters named '[+-]&lt;num&gt;' will be replaced when the query is run:
+ * NOTE1: make sure the meta data associated to the connection is up to date before using this
+ * method, see gda_connection_update_meta_store().
+ * 
+ * NOTE2: if the SELECT statement from which @model has been created uses more than one table, or
+ * if the table used does not have any primary key, then this method will fail
+ *
+ * Returns: TRUE if no error occurred.
+ */
+gboolean
+gda_data_model_query_compute_row_selection_condition (GdaDataModelQuery *model, GError **error)
+{
+	GdaDataModelQuery *selmodel;
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
+
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
+		return FALSE;
+	}
+	else
+		return gda_data_select_compute_row_selection_condition (GDA_DATA_SELECT (selmodel->priv->data), error);
+}
+
+/**
+ * gda_data_model_query_set_modification_statement
+ * @model: a #GdaDataSelect data model
+ * @mod_stmt: a #GdaStatement (INSERT, UPDATE or DELETE)
+ * @error: a place to store errors, or %NULL
+ *
+ * Informs @model that it should allow modifications to the data in some columns and some rows
+ * using @mod_stmt to propagate those modifications into the database.
+ *
+ * If @mod_stmt is:
  * <itemizedlist>
- *   <listitem><para>a parameter named +&lt;num&gt; will take the new value set at the 
- *                   &lt;num&gt;th column in @model</para></listitem>
- *   <listitem><para>a parameter named -&lt;num&gt; will take the old value set at the
- *                   &lt;num&gt;th column in @model</para></listitem>
+ *  <listitem><para>an UPDATE statement, then all the rows in @model will be modifyable</para></listitem>
+ *  <listitem><para>a DELETE statement, then it will be possible to delete rows in @model</para></listitem>
+ *  <listitem><para>in INSERT statement, then it will be possible to add some rows to @model</para></listitem>
+ *  <listitem><para>any other statement, then this method will return an error</para></listitem>
  * </itemizedlist>
- * Please note that the "+0" and "-0" parameters names are valid and will respectively 
- * take the new and old values of the first column of @model.
  *
- * Examples of statements are: "INSERT INTO orders (customer, creation_date, delivery_before, delivery_date) VALUES (## / *name:'Customer' type:integer* /, date('now'), ## / *name:"+2" type:date nullok:TRUE * /, NULL)", "DELETE FROM orders WHERE id = ## / *name:"-0" type:integer* /" and "UPDATE orders set id=## / *name:"+0" type:integer* /, delivery_before=## / *name:"+2" type:date nullok:TRUE* /, delivery_date=## / *name:"+3" type:date nullok:TRUE* / WHERE id=## / *name:"-0" type:integer* /"
+ * This method can be called several times to specify different types of modification.
+ *
+ * If @mod_stmt is an UPDATE or DELETE statement then it should have a WHERE part which identifies
+ * a unique row in @model (please note that this property can't be checked but may result
+ * in @model behaving in an unpredictable way).
+ *
+ * NOTE1: However, if the gda_data_model_query_set_row_selection_condition()
+ * or gda_data_model_query_set_row_selection_condition_sql() have been successfully be called before, the WHERE
+ * part of @mod_stmt <emphasis>WILL</emphasis> be modified to use the row selection condition specified through one of
+ * these methods (please not that it is then possible to avoid specifying a WHERE part in @mod_stmt then).
+ *
+ * NOTE2: if gda_data_model_query_set_row_selection_condition()
+ * or gda_data_model_query_set_row_selection_condition_sql() have not yet been successfully be called before, then
+ * the WHERE part of @mod_stmt will be used as if one of these functions had been called.
  *
  * Returns: TRUE if no error occurred.
  */
 gboolean
-gda_data_model_query_set_modification_query (GdaDataModelQuery *model, GdaStatement *mod_stmt, GError **error)
+gda_data_model_query_set_modification_statement (GdaDataModelQuery *model, GdaStatement *mod_stmt, GError **error)
 {
+	GdaDataModelQuery *selmodel;
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
-	g_return_val_if_fail (model->priv, FALSE);
-	g_return_val_if_fail (GDA_IS_STATEMENT (mod_stmt), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
 
-	switch (gda_statement_get_statement_type (mod_stmt)) {
-	case GDA_SQL_STATEMENT_INSERT:
-		g_object_set (model, "insert_query", mod_stmt, NULL);
-		break;
-	case GDA_SQL_STATEMENT_UPDATE:
-		g_object_set (model, "update_query", mod_stmt, NULL);
-		break;
-	case GDA_SQL_STATEMENT_DELETE:
-		g_object_set (model, "delete_query", mod_stmt, NULL);
-		break;
-	default:
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_MODIF_STATEMENT_ERROR,
-			     _("Wrong type of query"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return FALSE;
 	}
-	return TRUE;
+	else
+		return gda_data_select_set_modification_statement (GDA_DATA_SELECT (selmodel->priv->data), mod_stmt, error);
+}
+
+/**
+ * gda_data_model_query_set_modification_statement_sql
+ * @model: a #GdaDataSelect data model
+ * @sql: an SQL text
+ * @error: a place to store errors, or %NULL
+ *
+ * Offers the same feature as gda_data_model_query_set_modification_statement() but using an SQL statement
+ *
+ * Returns: TRUE if no error occurred.
+ */
+gboolean
+gda_data_model_query_set_modification_statement_sql  (GdaDataModelQuery *model, const gchar *sql, GError **error)
+{
+	GdaDataModelQuery *selmodel;
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
+
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
+		return FALSE;
+	}
+	else
+		return gda_data_select_set_modification_statement_sql (GDA_DATA_SELECT (selmodel->priv->data), sql, error);
+}
+
+/**
+ * gda_data_model_query_compute_modification_statements
+ * @model: a #GdaDataSelect data model
+ * @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.
+ * Note: any modification statement set using gda_data_model_query_set_modification_statement() will first be unset
+ *
+ * Returns: TRUE if no error occurred. If FALSE is returned, then some modification statement may still have been
+ * computed
+ */
+gboolean
+gda_data_model_query_compute_modification_statements (GdaDataModelQuery *model, GError **error)
+{
+	GdaDataModelQuery *selmodel;
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
+	selmodel = (GdaDataModelQuery*) model;
+	g_return_val_if_fail (selmodel->priv, FALSE);
+
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
+		return FALSE;
+	}
+	else
+		return gda_data_select_compute_modification_statements (GDA_DATA_SELECT (selmodel->priv->data), error);
 }
 
 /*
@@ -747,16 +720,13 @@
 {
 	GdaDataModelQuery *selmodel;
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), 0);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, 0);
 
-	if (!selmodel->priv->data && !selmodel->priv->refresh_error)
-		gda_data_model_query_refresh (selmodel, NULL);
-	
-	if (selmodel->priv->data)
-		return gda_data_model_get_n_rows (selmodel->priv->data);
+	if (!selmodel->priv->data)
+		return -1;
 	else
-		return 0;
+		return gda_data_model_get_n_rows (selmodel->priv->data);
 }
 
 static gint
@@ -764,171 +734,41 @@
 {
 	GdaDataModelQuery *selmodel;
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), 0);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, 0);
 	
-	if (!selmodel->priv->data && !selmodel->priv->refresh_error) {
-		if (!gda_data_model_query_refresh (selmodel, NULL))
-			return 0;
-	}
-	
-	create_columns (selmodel);
-
-	if (selmodel->priv->columns)
-		return g_slist_length (selmodel->priv->columns);
+	if (selmodel->priv->data)
+		return gda_data_model_get_n_columns (selmodel->priv->data);
 	else
 		return 0;
 }
 
-static void
-create_columns (GdaDataModelQuery *model)
-{
-	if (model->priv->columns)
-		return;
-	if (! model->priv->statements[SEL_QUERY])
-		return;
-
-	/* we need to be able to get (or build) a list of column's title and GType. Scan through the
-	 * statement's GdaSqlSelectFields and if a name and GType can be determined, then use them, otherwise
-	 * use "column%d" and G_TYPE_STRING, and let the user modify that directly using gda_data_model_describe () 
-	 */
-	GdaSqlStatement *sqlst;
-	g_object_get (G_OBJECT (model->priv->statements[SEL_QUERY]), "structure", &sqlst, NULL);
-
-	if (gda_sql_statement_normalize (sqlst, model->priv->cnc, NULL)) {
-		GdaSqlStatementSelect *selst = (GdaSqlStatementSelect*) sqlst->contents;
-		GSList *list;
-		
-		for (list = selst->expr_list; list; list = list->next) {
-			GdaSqlSelectField *field = (GdaSqlSelectField*) list->data;
-			GdaColumn *col;
-
-			col = gda_column_new ();
-			if (field->validity_meta_table_column) {
-				gda_column_set_name (col, field->validity_meta_table_column->column_name);
-				gda_column_set_title (col, field->validity_meta_table_column->column_name);
-				gda_column_set_g_type (col, field->validity_meta_table_column->gtype);
-			}
-			else {
-				gda_column_set_name (col, "col");
-				gda_column_set_title (col, "col");
-				gda_column_set_g_type (col, G_TYPE_STRING);
-			}
-			model->priv->columns = g_slist_append (model->priv->columns, col);
-		}
-	}
-	else {
-		if (model->priv->data) {
-			/* copy model->priv->data's columns */
-			gint i, nb_cols;
-			
-			nb_cols = gda_data_model_get_n_columns (model->priv->data);
-			for (i = 0; i < nb_cols; i++) {
-				GdaColumn *orig, *col;
-				
-				orig = gda_data_model_describe_column (model->priv->data, i);
-				col = gda_column_copy (orig);
-				gda_column_set_position (col, i);
-				model->priv->columns = g_slist_append (model->priv->columns, col);
-			}
-		}
-		else {
-			/* Can't compute the list of columns because the SELECT query is not active 
-			 * and the model does not contain any data;
-			 * next time we emit a "reset" signal */
-			model->priv->emit_reset = TRUE;
-		}
-	}
-	gda_sql_statement_free (sqlst);
-
-	if (model->priv->columns && model->priv->emit_reset) {
-		model->priv->emit_reset = FALSE;
-		gda_data_model_reset (GDA_DATA_MODEL (model));
-	}
-}
-
 static GdaColumn *
 gda_data_model_query_describe_column (GdaDataModel *model, gint col)
 {
 	GdaDataModelQuery *selmodel;
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), NULL);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, NULL);
 
-	if (!selmodel->priv->data  && !selmodel->priv->refresh_error) {
-		if (!gda_data_model_query_refresh (selmodel, NULL))
-			return NULL;
-	}
-
-	create_columns (selmodel);
-	if (selmodel->priv->columns)
-		return g_slist_nth_data (selmodel->priv->columns, col);
-	else
+	if (!selmodel->priv->data)
 		return NULL;
+	else
+		return gda_data_model_describe_column (selmodel->priv->data, col);
 }
 
 static GdaDataModelAccessFlags
 gda_data_model_query_get_access_flags (GdaDataModel *model)
 {
 	GdaDataModelQuery *selmodel;
-	GdaDataModelAccessFlags flags = GDA_DATA_MODEL_ACCESS_RANDOM;
-
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), 0);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, 0);
 
-	if (!selmodel->priv->data && !selmodel->priv->refresh_error)
-		gda_data_model_query_refresh (selmodel, NULL);
-	
-	if (selmodel->priv->data) {
-		gint i;
-		for (i = INS_QUERY; i <= DEL_QUERY; i++) {
-			gboolean allok = TRUE;
-
-			if (selmodel->priv->params [i]) {
-				/* if all the parameters which are not named _[0-9]* are valid, 
-				 * then we grant the corresponding flag */
-				GSList *params;
-				for (params = selmodel->priv->params [i]->holders; params; params = params->next) {
-					gint num;
-					
-					num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "_num")) - 1;
-					if (num < 0)
-						allok = gda_holder_is_valid ((GdaHolder*)(params->data));
-					if (!allok) {
-						break;
-						g_print ("Not OK:\n");
-					}
-				}
-				
-			}
-			else {
-				if (!selmodel->priv->statements [i])
-					allok = FALSE;
-			}
-
-			if (allok && selmodel->priv->params [i]) {
-				switch (i) {
-				case INS_QUERY:
-					flags |= GDA_DATA_MODEL_ACCESS_INSERT;
-					/* g_print ("INS flag\n"); */
-					break;
-				case UPD_QUERY:
-					flags |= GDA_DATA_MODEL_ACCESS_UPDATE;
-					/* g_print ("UPD flag\n"); */
-					break;
-				case DEL_QUERY:
-					flags |= GDA_DATA_MODEL_ACCESS_DELETE;
-					/* g_print ("DEL flag\n"); */
-					break;
-				default:
-					g_assert_not_reached ();
-				}
-			}
-		}
-	}
-
-	return flags;
+	if (!selmodel->priv->data)
+		return 0;
+	else
+		return gda_data_model_get_access_flags (selmodel->priv->data);
 }
 
 static const GValue *
@@ -936,112 +776,33 @@
 {
 	GdaDataModelQuery *selmodel;
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), NULL);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, NULL);
 
-	if (!selmodel->priv->data && !selmodel->priv->refresh_error)
-		gda_data_model_query_refresh (selmodel, NULL);
-	
-	if (selmodel->priv->data)
-		return gda_data_model_get_value_at (selmodel->priv->data, col, row, error);
-	else {
-		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_NOT_FOUND_ERROR,
-			     _("Data model has no data"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return NULL;
 	}
+	else
+		return gda_data_model_get_value_at (selmodel->priv->data, col, row, error);
 }
 
 static GdaValueAttribute
 gda_data_model_query_get_attributes_at (GdaDataModel *model, gint col, gint row)
 {
-	GdaValueAttribute flags = 0;
 	GdaDataModelQuery *selmodel;
-	GdaHolder *p_used = NULL;
-
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), 0);
 	selmodel = (GdaDataModelQuery *) model;
 	g_return_val_if_fail (selmodel->priv, 0);
 	
-	if (selmodel->priv->data)
-		flags = gda_data_model_get_attributes_at (selmodel->priv->data, col, row);
-
-	if ((row >= 0) && selmodel->priv->statements[UPD_QUERY] && selmodel->priv->params[UPD_QUERY]) {
-		GSList *params;
-		for (params = selmodel->priv->params[UPD_QUERY]->holders; params; params = params->next) {
-			if (GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1 == col) {
-				p_used = (GdaHolder *) params->data;
-				break;
-			}
-		}
-	}
-
-	if ((row < 0) && selmodel->priv->statements[INS_QUERY] && selmodel->priv->params[INS_QUERY]) {
-		GSList *params;
-		for (params = selmodel->priv->params[INS_QUERY]->holders; params; params = params->next) {
-			if (GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1 == col) {
-				p_used = (GdaHolder *) params->data;
-				break;
-			}
-		}
-	}
-
-	if (!p_used)
-		flags |= GDA_VALUE_ATTR_NO_MODIF;
-	else {
-		flags &= ~GDA_VALUE_ATTR_NO_MODIF;
-		flags &= ~GDA_VALUE_ATTR_CAN_BE_NULL;
-		flags &= ~GDA_VALUE_ATTR_CAN_BE_DEFAULT;
-		if (! gda_holder_get_not_null (p_used))
-			flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
-		if (gda_holder_get_default_value (p_used)) 
-			flags |= GDA_VALUE_ATTR_CAN_BE_DEFAULT;
-	}
-
-	/*g_print ("%d,%d: %d\n", col, row, flags);*/
-
-	return flags;
-}
-
-static GdaDataModelIter *
-gda_data_model_query_create_iter (GdaDataModel *model)
-{
-	GdaDataModelIter *iter;
-
-	iter = (GdaDataModelIter *) g_object_new (GDA_TYPE_DATA_MODEL_ITER, 
-						  "data_model", model, NULL);
-	/* set the "__gda_entry_plugin" property for all the parameters depending on the SELECT query field */
-	static gboolean warned = FALSE;
-	if (!warned) {
-		warned = TRUE;
-		TO_IMPLEMENT;
-	}
-	/*
-	if (gda_query_is_select_query (GDA_DATA_MODEL_QUERY (model)->priv->statements[SEL_QUERY])) {
-		GSList *list, *fields;
-		GSList *params;
-
-		fields = gda_entity_get_fields (GDA_ENTITY (GDA_DATA_MODEL_QUERY (model)->priv->statements[SEL_QUERY]));
-		params = GDA_SET (iter)->holders;
-
-		for (list = fields; list && params; list = list->next, params = params->next) {
-			GdaEntityField *field = (GdaEntityField *) list->data;
-			if (GDA_IS_QUERY_FIELD_FIELD (field)) {
-				gchar *plugin;
-
-			        g_object_get (G_OBJECT (field), "__gda_entry_plugin", &plugin, NULL);
-				if (plugin) {
-					g_object_set (G_OBJECT (params->data), "__gda_entry_plugin", plugin, NULL);
-					g_free (plugin);
-				}
-			}
-		}
-		g_slist_free (fields);
-	}
-	*/
-
-	return iter;
+	if (!selmodel->priv->data)
+		return 0;
+	else
+		return gda_data_model_get_attributes_at (selmodel->priv->data, col, row);
 }
 
+
 static gchar *try_add_savepoint (GdaDataModelQuery *selmodel);
 static void   try_remove_savepoint (GdaDataModelQuery *selmodel, const gchar *svp_name);
 
@@ -1073,302 +834,79 @@
 		gda_connection_delete_savepoint (selmodel->priv->cnc, svp_name, NULL);
 }
 
-static gboolean
-run_modify_query (GdaDataModelQuery *selmodel, gint query_type, GError **error)
-{
-	gboolean modify_done = FALSE;
-	gchar *svp_name = NULL;
-
-	/* try to add a savepoint if we are not doing multiple updates */
-	if (!selmodel->priv->multiple_updates)
-		svp_name = try_add_savepoint (selmodel);
-
-	if (! selmodel->priv->cnc) {
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
-			     _("No connection specified"));
-		return FALSE;
-	}
-	if (! gda_connection_is_opened (selmodel->priv->cnc)) {
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR, GDA_DATA_MODEL_QUERY_CONNECTION_ERROR,
-			     _("Specified connection is closed"));
-		return FALSE;
-	}
-
-	if (gda_connection_statement_execute_non_select (selmodel->priv->cnc, 
-							 selmodel->priv->statements[query_type], 
-							 selmodel->priv->params [query_type], NULL, error) != -1)
-		modify_done = TRUE;
-
-	if (modify_done) {
-		/* modif query executed without error */
-		if (!selmodel->priv->multiple_updates)
-			gda_data_model_query_refresh (selmodel, NULL);
-		else 
-			selmodel->priv->needs_refresh = TRUE;
-	}
-	else {
-		/* modif query did not execute correctly */
-		if (!selmodel->priv->multiple_updates)
-			/* nothing to do */;
-		else
-			selmodel->priv->transaction_needs_commit = FALSE;
-	}
-
-	/* try to remove the savepoint added above */
-	if (svp_name) {
-		try_remove_savepoint (selmodel, svp_name);
-		g_free (svp_name);
-	}
-
-#ifdef REMOVE
-	if (modify_done && !selmodel->priv->multiple_updates)
-		/* modifications were successfull => refresh needed */
-		gda_data_model_query_refresh (selmodel, NULL);
-	else
-		/* the refresh is delayed */
-		selmodel->priv->needs_refresh = TRUE;
-#endif
-
-	return modify_done;
-}
 
 static gboolean
 gda_data_model_query_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *value, GError **error)
 {
 	GdaDataModelQuery *selmodel;
-	GdaSet *paramlist;
 
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, FALSE);
 
 	/* make sure there is a UPDATE query */
-	if (!selmodel->priv->statements[UPD_QUERY]) {
-		g_set_error (error, 0, 0,
-			     _("No UPDATE query specified, can't update row"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return FALSE;
 	}
-
-	/* set the values of the parameters in the paramlist if necessary */
-	paramlist = selmodel->priv->params[UPD_QUERY];
-	if (paramlist && paramlist->holders) {
-		GSList *params;
-		for (params = paramlist->holders; params; params = params->next) {
-			gint num;
-
-			num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1;
-			if (num >= 0) {
-				/* new values */
-				if (num == col) {
-					if (!gda_holder_set_value (GDA_HOLDER (params->data), value, error))
-						return FALSE;
-				}
-				else {
-					if (! gda_holder_set_value (GDA_HOLDER (params->data), NULL, error))
-						return FALSE;
-				}
-			}
-			else {
-				num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "-num")) - 1;	
-				if (num >= 0) {
-					/* old values */
-					const GValue *cvalue;
-					cvalue = gda_data_model_get_value_at ((GdaDataModel*) selmodel, num, row, error);
-					if (!cvalue || gda_holder_set_value (GDA_HOLDER (params->data), cvalue, error)) 
-						return FALSE;
-				}
-			}
-		}
-	}
-
-	/* render the SQL and run it */
-	return run_modify_query (selmodel, UPD_QUERY, error);
+	else
+		return gda_data_model_set_value_at (selmodel->priv->data, col, row, value, error);
 }
 
 static gboolean
 gda_data_model_query_set_values (GdaDataModel *model, gint row, GList *values, GError **error)
 {
 	GdaDataModelQuery *selmodel;
-	GdaSet *paramlist;
 
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, FALSE);
 
-	/* make sure there is a UPDATE query */
-	if (!selmodel->priv->statements[UPD_QUERY]) {
-		g_set_error (error, 0, 0,
-			     _("No UPDATE query specified, can't update row"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return FALSE;
 	}
-
-	/* set the values of the parameters in the paramlist if necessary */
-	paramlist = selmodel->priv->params[UPD_QUERY];
-	if (paramlist && paramlist->holders) {
-		GSList *params;
-		for (params = paramlist->holders; params; params = params->next) {
-			gint num;
-
-			num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1;
-			if (num >= 0) {
-				/* new values */
-				GValue *value;
-				value = g_list_nth_data ((GList *) values, num);
-				if (value) {
-					if (!gda_holder_set_value (GDA_HOLDER (params->data), value, error))
-						return FALSE;
-				}
-				else {
-					if (! gda_holder_set_value (GDA_HOLDER (params->data), NULL, error))
-						return FALSE;
-				}
-			}
-			else {
-				num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "-num")) - 1;
-				if (num >= 0) {
-					/* old values */
-					const GValue *cvalue;
-					cvalue = gda_data_model_get_value_at ((GdaDataModel*) selmodel, num, row, error);
-					if (!cvalue || gda_holder_set_value (GDA_HOLDER (params->data), cvalue, error)) 
-						return FALSE;
-				}
-			}
-		}
-	}
-
-	/* render the SQL and run it */
-	return run_modify_query (selmodel, UPD_QUERY, error);
+	else
+		return gda_data_model_set_values (selmodel->priv->data, row, values, error);
 }
 
 static gint
 gda_data_model_query_append_values (GdaDataModel *model, const GList *values, GError **error)
 {
 	GdaDataModelQuery *selmodel;
-	GdaSet *paramlist;
-	gboolean retval;
 
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), -1);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, -1);
 
-	/* make sure there is a INSERT query */
-	if (!selmodel->priv->statements[INS_QUERY]) {
-		g_set_error (error, 0, 0,
-			     _("No INSERT query specified, can't insert row"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return -1;
 	}
-
-	/* set the values of the parameters in the paramlist if necessary */
-	paramlist = selmodel->priv->params[INS_QUERY];
-	if (paramlist && paramlist->holders) {
-		GSList *params;
-		for (params = paramlist->holders; params; params = params->next) {
-			gint num;
-
-			num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1;
-			if (num >= 0) {
-				/* new values only */
-				GValue *value;
-				value = g_list_nth_data ((GList *) values, num);
-				if (value) {
-					if (! gda_holder_set_value (GDA_HOLDER (params->data), value, error))
-						return -1;
-				}
-				else 
-					g_object_set (G_OBJECT (params->data), "use-default-value", TRUE, NULL);
-			}
-		}
-	}
-
-	/* render the SQL and run it */
-	retval = run_modify_query (selmodel, INS_QUERY, error);
-
-	if (retval)
-		return 0;
 	else
-		return -1;
+		return gda_data_model_append_values (selmodel->priv->data, values, error);
 }
 
-static gint
-gda_data_model_query_append_row (GdaDataModel *model, GError **error)
-{
-	GdaDataModelQuery *selmodel;
-	GdaSet *paramlist;
-	gboolean retval;
-
-	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), -1);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
-	g_return_val_if_fail (selmodel->priv, -1);
-
-	/* make sure there is a INSERT query */
-	if (!selmodel->priv->statements[INS_QUERY]) {
-		g_set_error (error, 0, 0,
-			     _("No INSERT query specified, can't insert row"));
-		return -1;
-	}
-
-	/* set the values of the parameters in the paramlist if necessary */
-	paramlist = selmodel->priv->params[INS_QUERY];
-	if (paramlist && paramlist->holders) {
-		GSList *params;
-		for (params = paramlist->holders; params; params = params->next) {
-			gint num;
-
-			num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "+num")) - 1;
-
-			if (num >= 0) 
-				/* new values only */
-				if (! gda_holder_set_value (GDA_HOLDER (params->data), NULL, error))
-					return -1;
-		}
-	}
-
-	/* render the SQL and run it */
-	retval = run_modify_query (selmodel, INS_QUERY, error);
-
-	if (retval)
-		return 0;
-	else
-		return -1;
-}
 
 static gboolean
 gda_data_model_query_remove_row (GdaDataModel *model, gint row, GError **error)
 {
 	GdaDataModelQuery *selmodel;
-	GdaSet *paramlist;
 
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_val_if_fail (selmodel->priv, FALSE);
 	
-	/* make sure there is a REMOVE query */
-	if (!selmodel->priv->statements[DEL_QUERY]) {
-		g_set_error (error, 0, 0,
-			     _("No DELETE query specified, can't delete row"));
+	if (!selmodel->priv->data) {
+		g_assert (selmodel->priv->exec_error);
+		g_propagate_error (error, g_error_copy (selmodel->priv->exec_error));
 		return FALSE;
 	}
-
-	/* set the values of the parameters in the paramlist if necessary */
-	paramlist = selmodel->priv->params[DEL_QUERY];
-	if (paramlist && paramlist->holders) {
-		GSList *params;
-		for (params = paramlist->holders; params; params = params->next) {
-			gint num;
-
-			num = GPOINTER_TO_INT (g_object_get_data ((GObject*) params->data, "-num")) - 1;
-			if (num >= 0) {
-				/* old values only */
-				const GValue *cvalue;
-				cvalue = gda_data_model_get_value_at ((GdaDataModel*) selmodel, num, row, error);
-				if (!cvalue || gda_holder_set_value (GDA_HOLDER (params->data), cvalue, error)) 
-					return FALSE;
-			}
-		}
-	}
-
-	/* render the SQL and run it */
-	return run_modify_query (selmodel, DEL_QUERY, error);
+	else
+		return gda_data_model_remove_row (selmodel->priv->data, row, error);
 }
 
 static void
@@ -1421,127 +959,62 @@
 		g_print ("GdaDataModelQuery %p: added savepoint %s\n", selmodel, selmodel->priv->svp_name);
 }
 
-static void
-opt_end_transaction_or_svp (GdaDataModelQuery *selmodel)
+static gboolean
+opt_end_transaction_or_svp (GdaDataModelQuery *selmodel, GError **error)
 {
 	GdaConnection *cnc;
 
 	if (!selmodel->priv->transaction_started && !selmodel->priv->svp_name)
-		return;
+		return TRUE;
 
-	g_print ("GdaDataModelQuery %p (END1): %s\n", selmodel, selmodel->priv->needs_refresh ? "needs refresh" : "no refresh");
 	cnc = selmodel->priv->cnc;
 	if (selmodel->priv->transaction_started) {
 		g_assert (!selmodel->priv->svp_name);
 		if (selmodel->priv->transaction_needs_commit) {
 			/* try to commit transaction */
-			if (!gda_connection_commit_transaction (cnc, NULL, NULL))
-				selmodel->priv->needs_refresh = TRUE;
+			if (!gda_connection_commit_transaction (cnc, NULL, error))
+				return FALSE;
 		}
 		else {
 			/* rollback transaction */
-			selmodel->priv->needs_refresh = gda_connection_rollback_transaction (cnc, NULL, NULL) ? FALSE : TRUE;
+			if (! gda_connection_rollback_transaction (cnc, NULL, error))
+				return FALSE;
 		}
 		selmodel->priv->transaction_started = FALSE;
+		return TRUE;
 	}
 	else {
 		if (!selmodel->priv->transaction_needs_commit) {
-			selmodel->priv->needs_refresh = gda_connection_rollback_savepoint (cnc, selmodel->priv->svp_name, NULL) ?
-				FALSE : TRUE;
+			if (!gda_connection_rollback_savepoint (cnc, selmodel->priv->svp_name, error))
+				return FALSE;
 		}
 		else
 			if (gda_connection_supports_feature (cnc, GDA_CONNECTION_FEATURE_SAVEPOINTS_REMOVE))
-				if (!gda_connection_delete_savepoint (cnc, selmodel->priv->svp_name, NULL))
-					selmodel->priv->needs_refresh = TRUE;
+				if (!gda_connection_delete_savepoint (cnc, selmodel->priv->svp_name, error))
+					return FALSE;
 		g_free (selmodel->priv->svp_name);
 		selmodel->priv->svp_name = NULL;
+		return TRUE;
 	}
-	g_print ("GdaDataModelQuery %p (END2): %s\n", selmodel, selmodel->priv->needs_refresh ? "needs refresh" : "no refresh");
 }
 
-
-
 static void
 gda_data_model_query_send_hint (GdaDataModel *model, GdaDataModelHint hint, const GValue *hint_value)
 {
 	GdaDataModelQuery *selmodel;
 	g_return_if_fail (GDA_IS_DATA_MODEL_QUERY (model));
-	selmodel = GDA_DATA_MODEL_QUERY (model);
+	selmodel = (GdaDataModelQuery*) model;
 	g_return_if_fail (selmodel->priv);
 
 	if (hint == GDA_DATA_MODEL_HINT_REFRESH)
 		gda_data_model_query_refresh (selmodel, NULL);
 	else {
-		if (hint == GDA_DATA_MODEL_HINT_START_BATCH_UPDATE) {
+		if (hint == GDA_DATA_MODEL_HINT_START_BATCH_UPDATE)
 			opt_start_transaction_or_svp (selmodel);
-			selmodel->priv->multiple_updates = TRUE;
-		}
 		else {
-			if (hint == GDA_DATA_MODEL_HINT_END_BATCH_UPDATE) {
-				selmodel->priv->multiple_updates = FALSE;
-				opt_end_transaction_or_svp (selmodel);
-					
-				if (selmodel->priv->needs_refresh)
-					gda_data_model_query_refresh (selmodel, NULL);
-			}
+			if (hint == GDA_DATA_MODEL_HINT_END_BATCH_UPDATE)
+				opt_end_transaction_or_svp (selmodel, NULL);
 		}
 	}
 }
 
-/*
- * Computation of INSERT, DELETE and UPDATE statements
- */
-/**
- * gda_data_model_query_compute_modification_queries
- * @model: a GdaDataModelQuery object
- * @target: the target table to modify, or %NULL
- * @options: options to specify how the statements must be built in some special cases
- * @error: a place to store errors or %NULL
- *
- * Try to compute the INSERT, DELETE and UPDATE statements; any previous modification query
- * will be discarded.
- *
- * If specified, the table which will be updated is the one represented by the @target.
- *
- * If @target is %NULL, then an error will be returned if @model's SELECT query has more than
- * one target.
- *
- * Returns: TRUE at least one of the INSERT, DELETE and UPDATE statements has been computed
- */
-gboolean
-gda_data_model_query_compute_modification_queries (GdaDataModelQuery *model, const gchar *target, 
-						   GdaDataModelQueryOptions options, GError **error)
-{
-	gint i;
-	gboolean require_pk = TRUE;
-	GdaStatement *ins, *upd, *del;
-	g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
-	g_return_val_if_fail (model->priv, FALSE);
-
-	if (!model->priv->statements[SEL_QUERY]) {
-		g_set_error (error, GDA_DATA_MODEL_QUERY_ERROR,
-			     GDA_DATA_MODEL_QUERY_COMPUTE_MODIF_STATEMENTS_ERROR,
-			     _("No SELECT query specified"));
-		return FALSE;
-	}
-
-	for (i = SEL_QUERY + 1; i <= DEL_QUERY; i++) {
-		if (model->priv->statements[i])
-			forget_statement (model, model->priv->statements[i]);
-	}
-	
-	if (options & GDA_DATA_MODEL_QUERY_OPTION_USE_ALL_FIELDS_IF_NO_PK)
-		require_pk = FALSE;
-	if (gda_compute_dml_statements (model->priv->cnc, model->priv->statements[SEL_QUERY], require_pk, 
-					&ins, &upd, &del, error)) {
-		
-		g_object_set (G_OBJECT (model), "insert_query", ins, 
-			      "update-query", upd, "delete-query", del, NULL);
-		if (ins) g_object_unref (ins);
-		if (upd) g_object_unref (upd);
-		if (del) g_object_unref (del);
-		return TRUE;
-	}
-
-	return FALSE;
-}

Modified: trunk/libgda/gda-data-model-query.h
==============================================================================
--- trunk/libgda/gda-data-model-query.h	(original)
+++ trunk/libgda/gda-data-model-query.h	Sun Sep  7 19:06:38 2008
@@ -25,6 +25,7 @@
 
 #include <libgda/gda-data-model.h>
 #include <libgda/gda-connection.h>
+#include <sql-parser/gda-sql-statement.h>
 
 G_BEGIN_DECLS
 
@@ -43,16 +44,10 @@
 #define GDA_DATA_MODEL_QUERY_ERROR gda_data_model_query_error_quark ()
 
 typedef enum {
-        GDA_DATA_MODEL_QUERY_XML_LOAD_ERROR,
-	GDA_DATA_MODEL_QUERY_COMPUTE_MODIF_STATEMENTS_ERROR,
-	GDA_DATA_MODEL_QUERY_MODIF_STATEMENT_ERROR,
+	GDA_DATA_MODEL_QUERY_SELECT_STATEMENT_ERROR,
 	GDA_DATA_MODEL_QUERY_CONNECTION_ERROR
 } GdaDataModelQueryError;
 
-typedef enum {
-	GDA_DATA_MODEL_QUERY_OPTION_USE_ALL_FIELDS_IF_NO_PK = 1 << 0
-} GdaDataModelQueryOptions;
-
 struct _GdaDataModelQuery {
 	GObject                    object;
 	GdaDataModelQueryPrivate  *priv;
@@ -62,16 +57,17 @@
 	GObjectClass               parent_class;
 };
 
-GType             gda_data_model_query_get_type                     (void) G_GNUC_CONST;
-GdaDataModel     *gda_data_model_query_new                          (GdaConnection *cnc, GdaStatement *select_stmt);
-
-GdaSet           *gda_data_model_query_get_parameter_list           (GdaDataModelQuery *model);
-gboolean          gda_data_model_query_refresh                      (GdaDataModelQuery *model, GError **error);
-gboolean          gda_data_model_query_set_modification_query       (GdaDataModelQuery *model, 
-								     GdaStatement *mod_stmt, GError **error);
-gboolean          gda_data_model_query_compute_modification_queries (GdaDataModelQuery *model, const gchar *target,
-								     GdaDataModelQueryOptions options, GError **error);
-
+GType              gda_data_model_query_get_type                        (void) G_GNUC_CONST;
+GdaDataModelQuery *gda_data_model_query_new                             (GdaConnection *cnc, GdaStatement *select_stmt, GdaSet *params);
+gboolean           gda_data_model_query_refresh                         (GdaDataModelQuery *model, GError **error);
+
+gboolean           gda_data_model_query_set_row_selection_condition     (GdaDataModelQuery *model, GdaSqlExpr *expr, GError **error);
+gboolean           gda_data_model_query_set_row_selection_condition_sql (GdaDataModelQuery *model, const gchar *sql_where, GError **error);
+gboolean           gda_data_model_query_compute_row_selection_condition (GdaDataModelQuery *model, GError **error);
+
+gboolean           gda_data_model_query_set_modification_statement      (GdaDataModelQuery *model, GdaStatement *mod_stmt, GError **error);
+gboolean           gda_data_model_query_set_modification_statement_sql  (GdaDataModelQuery *model, const gchar *sql, GError **error);
+gboolean           gda_data_model_query_compute_modification_statements (GdaDataModelQuery *model, GError **error);
 
 G_END_DECLS
 

Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c	(original)
+++ trunk/libgda/gda-data-model.c	Sun Sep  7 19:06:38 2008
@@ -812,8 +812,11 @@
 		return FALSE;
 
 	/* validity tests */
-	if (row >= gda_data_model_get_n_rows (model)) 
-		goto onerror;
+	if ((row < 0) || (row >= gda_data_model_get_n_rows (model))) {
+		gda_data_model_iter_invalidate_contents (iter);
+		g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
+		return FALSE;
+	}
 		
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
 	
@@ -827,18 +830,18 @@
 	for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
 		const GValue *cvalue;
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
-		if (!cvalue || !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL))
-			goto onerror;
+		if (!cvalue || 
+		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
+			g_object_set (G_OBJECT (iter), "current_row", row, 
+				      "update_model", update_model, NULL);
+			gda_data_model_iter_invalidate_contents (iter);
+			return FALSE;
+		}
 		set_param_attributes ((GdaHolder*) list->data, 
 				      gda_data_model_get_attributes_at (model, col, row));
 	}
 	g_object_set (G_OBJECT (iter), "current_row", row, "update_model", update_model, NULL);
 	return TRUE;
-
- onerror:
-	gda_data_model_iter_invalidate_contents (iter);
-	g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
-	return FALSE;
 }
 
 static void
@@ -900,8 +903,11 @@
 
 	g_object_get (G_OBJECT (iter), "current_row", &row, NULL);
 	row++;
-	if (row >= gda_data_model_get_n_rows (model)) 
-		goto onerror;
+	if (row >= gda_data_model_get_n_rows (model)) {
+		gda_data_model_iter_invalidate_contents (iter);
+		g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
+		return FALSE;
+	}
 	
 	/* actual sync. */
 	g_object_get (G_OBJECT (iter), "update_model", &update_model, NULL);
@@ -909,19 +915,19 @@
 	for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
 		const GValue *cvalue;
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
-		if (!cvalue || !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) 
-			goto onerror;
+		if (!cvalue || 
+		    !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) {
+			g_object_set (G_OBJECT (iter), "current_row", row, 
+				      "update_model", update_model, NULL);
+			gda_data_model_iter_invalidate_contents (iter);
+			return FALSE;
+		}
 		set_param_attributes ((GdaHolder *) list->data, 
 				      gda_data_model_get_attributes_at (model, col, row));
 	}
 	g_object_set (G_OBJECT (iter), "current_row", row, 
 		      "update_model", update_model, NULL);
 	return TRUE;
-	
- onerror:
-	gda_data_model_iter_invalidate_contents (iter);
-	g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
-	return FALSE;
 }
 
 /**
@@ -970,8 +976,11 @@
 
 	g_object_get (G_OBJECT (iter), "current_row", &row, NULL);
 	row--;
-	if (row < 0)
-		goto onerror;
+	if (row < 0) {
+		gda_data_model_iter_invalidate_contents (iter);
+		g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
+		return FALSE;
+	}
 	
 	/* actual sync. */
 	g_object_get (G_OBJECT (iter), "update_model", &update_model, NULL);
@@ -979,19 +988,19 @@
 	for (col = 0, list = ((GdaSet *) iter)->holders; list; col++, list = list->next) {
 		const GValue *cvalue;
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
-		if (!cvalue || !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL))
-			goto onerror;
+		if (!cvalue || 
+		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
+			g_object_set (G_OBJECT (iter), "current_row", row, 
+				      "update_model", update_model, NULL);
+			gda_data_model_iter_invalidate_contents (iter);
+			return FALSE;
+		}
 		set_param_attributes ((GdaHolder*) list->data,
 				      gda_data_model_get_attributes_at (model, col, row));
 	}
 	g_object_set (G_OBJECT (iter), "current_row", row, 
 		      "update_model", update_model, NULL);
 	return TRUE;
-
- onerror:
-	gda_data_model_iter_invalidate_contents (iter);
-	g_object_set (G_OBJECT (iter), "current_row", -1, NULL);
-	return FALSE;
 }
 
 
@@ -1906,7 +1915,14 @@
 		to_nb_rows = gda_data_model_get_n_rows (to);
 	}
 
-	gda_data_model_iter_move_next (from_iter); /* move to first row */
+	gboolean mstatus;
+	mstatus = gda_data_model_iter_move_next (from_iter); /* move to first row */
+	if (!mstatus) {
+		gint crow;
+		g_object_get (from_iter, "current-row", &crow, NULL);
+		if (crow >= 0)
+			retval = FALSE;
+	}
 	while (retval && gda_data_model_iter_is_valid (from_iter)) {
 		GList *values = NULL;
 		GList *avlist = append_values;
@@ -1965,7 +1981,13 @@
 		}
 		
 		g_list_free (values);
-		gda_data_model_iter_move_next (from_iter);
+		mstatus = gda_data_model_iter_move_next (from_iter);
+		if (!mstatus) {
+			gint crow;
+			g_object_get (from_iter, "current-row", &crow, NULL);
+			if (crow >= 0)
+				retval = FALSE;
+		}
 	}
 	
 	/* free memory */

Added: trunk/libgda/gda-data-select-extra.h
==============================================================================
--- (empty file)
+++ trunk/libgda/gda-data-select-extra.h	Sun Sep  7 19:06:38 2008
@@ -0,0 +1,63 @@
+/* GDA common library
+ * Copyright (C) 2008 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GDA_DATA_SELECT_EXTRA_H__
+#define __GDA_DATA_SELECT_EXTRA_H__
+
+#include <glib-object.h>
+#include <libgda/gda-row.h>
+#include <libgda/providers-support/gda-pstmt.h>
+#include <sql-parser/gda-sql-statement.h>
+#include <libgda/gda-data-select.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+	FIRST_QUERY = 0,
+        INS_QUERY  = 0,
+        UPD_QUERY  = 1,
+        DEL_QUERY  = 2,
+	NB_QUERIES = 3
+} ModType;
+
+typedef struct {
+	gboolean                safely_locked;
+	GdaSqlExpr             *unique_row_condition;
+	gint                   *insert_to_select_mapping; /* see compute_insert_select_params_mapping() */
+	
+	GdaSet                 *exec_set; /* owned by this object (copied) */
+	GdaSet                 *modif_set; /* owned by this object */
+	GdaStatement           *modif_stmts[NB_QUERIES];
+	GHashTable             *upd_stmts; /* key = a gboolean vector with TRUEs when the column is used, value = an UPDATE GdaStatement  */
+	GHashTable             *ins_stmts; /* key = a gboolean vector with TRUEs when the column is used, value = an INSERT GdaStatement  */
+	GdaStatement           *one_row_select_stmt; /* used to retreive one row after an UPDATE
+						      * or INSERT operation */
+} GdaDataSelectInternals;
+
+GdaDataSelectInternals *_gda_data_select_internals_steal (GdaDataSelect *model);
+void                    _gda_data_select_internals_paste (GdaDataSelect *model, GdaDataSelectInternals *inter);
+void                    _gda_data_select_internals_free (GdaDataSelectInternals *inter);
+
+G_END_DECLS
+
+#endif

Modified: trunk/libgda/gda-data-select.c
==============================================================================
--- trunk/libgda/gda-data-select.c	(original)
+++ trunk/libgda/gda-data-select.c	Sun Sep  7 19:06:38 2008
@@ -27,6 +27,7 @@
 #include <string.h>
 #include "gda-data-select.h"
 #include "providers-support/gda-data-select-priv.h"
+#include "gda-data-select-extra.h"
 #include "gda-row.h"
 #include "providers-support/gda-pstmt.h"
 #include <libgda/gda-statement.h>
@@ -37,15 +38,6 @@
 
 #define CLASS(x) (GDA_DATA_SELECT_CLASS (G_OBJECT_GET_CLASS (x)))
 
-typedef enum
-{
-	FIRST_QUERY = 0,
-        INS_QUERY  = 0,
-        UPD_QUERY  = 1,
-        DEL_QUERY  = 2,
-	NB_QUERIES = 3
-} ModType;
-
 typedef struct {
 	GdaStatement *select;
 	GdaSet       *params;
@@ -82,16 +74,7 @@
 	GdaDataModelAccessFlags usage_flags;
 	
 	/* attributes specific to data model modifications */
-	GdaSqlExpr             *unique_row_condition;
-	gint                   *insert_to_select_mapping; /* see compute_insert_select_params_mapping() */
-
-	GdaSet                 *exec_set; /* owned by this object (copied) */
-	GdaSet                 *modif_set; /* owned by this object */
-	GdaStatement           *modif_stmts[NB_QUERIES];
-	GHashTable             *upd_stmts; /* key = a gboolean vector with TRUEs when the column is used, value = an UPDATE GdaStatement  */
-	GHashTable             *ins_stmts; /* key = a gboolean vector with TRUEs when the column is used, value = an INSERT GdaStatement  */
-	GdaStatement           *one_row_select_stmt; /* used to retreive one row after an UPDATE
-						      * or INSERT operation */
+	GdaDataSelectInternals  *modif_internals;
 
 	/* Global overriding data when the data model has been modified */
 	GArray                 *del_rows; /* array[index] = number of the index'th deleted row,
@@ -139,7 +122,7 @@
 /* utility functions */
 typedef struct {
 	gint    size; /* number of elements in the @data array */
-	guchar *data; /* data[0] tp data[ size-1] are valid */
+	guchar *data; /* data[0] to data[ size-1] are valid */
 } BVector;
 static GdaStatement *check_acceptable_statement (GdaDataSelect *model, GError **error);
 static GdaStatement *compute_single_update_stmt (GdaDataSelect *model, BVector *bv, GError **error);
@@ -296,6 +279,7 @@
 static void
 gda_data_select_init (GdaDataSelect *model, GdaDataSelectClass *klass)
 {
+	ModType i;
 	g_return_if_fail (GDA_IS_DATA_SELECT (model));
 	model->priv = g_new0 (GdaDataSelectPrivate, 1);
 	model->priv->cnc = NULL;
@@ -308,13 +292,17 @@
 	model->priv->iter_row = G_MININT;
         model->priv->iter = NULL;
 
-	model->priv->unique_row_condition = NULL;
-	model->priv->insert_to_select_mapping = NULL;
-	model->priv->modif_set = NULL;
-	model->priv->exec_set = NULL;
-	model->priv->upd_stmts = NULL;
-	model->priv->ins_stmts = NULL;
-	model->priv->one_row_select_stmt = NULL;
+	model->priv->modif_internals = g_new0 (GdaDataSelectInternals, 1);
+	model->priv->modif_internals->safely_locked = FALSE;
+	model->priv->modif_internals->unique_row_condition = NULL;
+	model->priv->modif_internals->insert_to_select_mapping = NULL;
+	model->priv->modif_internals->modif_set = NULL;
+	model->priv->modif_internals->exec_set = NULL;
+	for (i = FIRST_QUERY; i < NB_QUERIES; i++)
+		model->priv->modif_internals->modif_stmts[i] = NULL;
+	model->priv->modif_internals->upd_stmts = NULL;
+	model->priv->modif_internals->ins_stmts = NULL;
+	model->priv->modif_internals->one_row_select_stmt = NULL;
 
 	model->priv->upd_rows = NULL;
 	model->priv->del_rows = NULL;
@@ -331,14 +319,9 @@
 	if (model->priv) {
 		gint i;
 
-		if (model->priv->unique_row_condition) {
-			gda_sql_expr_free (model->priv->unique_row_condition);
-			model->priv->unique_row_condition = NULL;
-		}
-
-		if (model->priv->insert_to_select_mapping) {
-			g_free (model->priv->insert_to_select_mapping);
-			model->priv->insert_to_select_mapping = NULL;
+		if (model->priv->modif_internals) {
+			_gda_data_select_internals_free (model->priv->modif_internals);
+			model->priv->modif_internals = NULL;
 		}
 
 		if (model->priv->upd_rows) {
@@ -356,15 +339,11 @@
 			model->priv->cnc = NULL;
 		}
 
-		if (model->priv->one_row_select_stmt) {
-			g_object_unref (model->priv->one_row_select_stmt);
-			model->priv->one_row_select_stmt = NULL;
-		}
-
 		if (model->prep_stmt) {
 			g_object_unref (model->prep_stmt);
 			model->prep_stmt = NULL;
 		}
+
 		if (model->priv->rows) {
 			for (i = 0; i < model->priv->rows->len; i++) {
 				GdaRow *prow;
@@ -383,35 +362,58 @@
 			g_slist_free (model->priv->columns);
 			model->priv->columns = NULL;
 		}
+	}
 
-		if (model->priv->modif_set) {
-			g_object_unref (model->priv->modif_set);
-			model->priv->modif_set = NULL;
-		}
+	/* chain to parent class */
+	parent_class->dispose (object);
+}
 
-		if (model->priv->exec_set) {
-			g_object_unref (model->priv->exec_set);
-			model->priv->exec_set = NULL;
-		}
+void
+_gda_data_select_internals_free (GdaDataSelectInternals *inter)
+{
+	ModType i;
 
-		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) {
-			g_hash_table_destroy (model->priv->upd_stmts);
-			model->priv->upd_stmts = NULL;
-		}
-		if (model->priv->ins_stmts) {
-			g_hash_table_destroy (model->priv->ins_stmts);
-			model->priv->ins_stmts = NULL;
+	if (inter->unique_row_condition) {
+		gda_sql_expr_free (inter->unique_row_condition);
+		inter->unique_row_condition = NULL;
+	}
+	
+	if (inter->insert_to_select_mapping) {
+		g_free (inter->insert_to_select_mapping);
+		inter->insert_to_select_mapping = NULL;
+	}
+
+	if (inter->modif_set) {
+		g_object_unref (inter->modif_set);
+		inter->modif_set = NULL;
+	}
+	
+	if (inter->exec_set) {
+		g_object_unref (inter->exec_set);
+		inter->exec_set = NULL;
+	}
+	
+	for (i = FIRST_QUERY; i < NB_QUERIES; i++) {
+		if (inter->modif_stmts [i]) {
+			g_object_unref (inter->modif_stmts [i]);
+			inter->modif_stmts [i] = NULL;
 		}
 	}
+	if (inter->upd_stmts) {
+		g_hash_table_destroy (inter->upd_stmts);
+		inter->upd_stmts = NULL;
+	}
+	if (inter->ins_stmts) {
+		g_hash_table_destroy (inter->ins_stmts);
+		inter->ins_stmts = NULL;
+	}
 
-	/* chain to parent class */
-	parent_class->dispose (object);
+	if (inter->one_row_select_stmt) {
+		g_object_unref (inter->one_row_select_stmt);
+		inter->one_row_select_stmt = NULL;
+	}
+
+	g_free (inter);
 }
 
 static void
@@ -431,6 +433,24 @@
 	parent_class->finalize (object);
 }
 
+GdaDataSelectInternals *
+_gda_data_select_internals_steal (GdaDataSelect *model)
+{
+	GdaDataSelectInternals *inter;
+	inter = model->priv->modif_internals;
+	model->priv->modif_internals = NULL;
+
+	return inter;
+}
+
+void
+_gda_data_select_internals_paste (GdaDataSelect *model, GdaDataSelectInternals *inter)
+{
+	if (model->priv->modif_internals)
+		_gda_data_select_internals_free (model->priv->modif_internals);
+	model->priv->modif_internals = inter;
+}
+
 static void
 create_columns (GdaDataSelect *model) 
 {
@@ -509,29 +529,29 @@
 			GdaSet *set;
 			set = g_value_get_object (value);
 			if (set) 
-				model->priv->exec_set = gda_set_copy (set);
+				model->priv->modif_internals->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]);
-			model->priv->modif_stmts [INS_QUERY] = g_value_get_object (value);
-			if (model->priv->modif_stmts [INS_QUERY])
-				g_object_ref (model->priv->modif_stmts [INS_QUERY]);
+			if (model->priv->modif_internals->modif_stmts [INS_QUERY])
+				g_object_unref (model->priv->modif_internals->modif_stmts [INS_QUERY]);
+			model->priv->modif_internals->modif_stmts [INS_QUERY] = g_value_get_object (value);
+			if (model->priv->modif_internals->modif_stmts [INS_QUERY])
+				g_object_ref (model->priv->modif_internals->modif_stmts [INS_QUERY]);
 			break;
 		case PROP_DEL_QUERY:
-			if (model->priv->modif_stmts [DEL_QUERY])
-				g_object_unref (model->priv->modif_stmts [DEL_QUERY]);
-			model->priv->modif_stmts [DEL_QUERY] = g_value_get_object (value);
-			if (model->priv->modif_stmts [DEL_QUERY])
-				g_object_ref (model->priv->modif_stmts [DEL_QUERY]);
+			if (model->priv->modif_internals->modif_stmts [DEL_QUERY])
+				g_object_unref (model->priv->modif_internals->modif_stmts [DEL_QUERY]);
+			model->priv->modif_internals->modif_stmts [DEL_QUERY] = g_value_get_object (value);
+			if (model->priv->modif_internals->modif_stmts [DEL_QUERY])
+				g_object_ref (model->priv->modif_internals->modif_stmts [DEL_QUERY]);
 			break;
 		case PROP_UPD_QUERY:
-			if (model->priv->modif_stmts [UPD_QUERY])
-				g_object_unref (model->priv->modif_stmts [UPD_QUERY]);
-			model->priv->modif_stmts [UPD_QUERY] = g_value_get_object (value);
-			if (model->priv->modif_stmts [UPD_QUERY])
-				g_object_ref (model->priv->modif_stmts [UPD_QUERY]);
+			if (model->priv->modif_internals->modif_stmts [UPD_QUERY])
+				g_object_unref (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
+			model->priv->modif_internals->modif_stmts [UPD_QUERY] = g_value_get_object (value);
+			if (model->priv->modif_internals->modif_stmts [UPD_QUERY])
+				g_object_ref (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
 			break;
 		default:
 			break;
@@ -567,16 +587,16 @@
 			}
 			break;
 		case PROP_PARAMS:
-			g_value_set_object (value, model->priv->exec_set);
+			g_value_set_object (value, model->priv->modif_internals->exec_set);
 			break;
 		case PROP_INS_QUERY:
-			g_value_set_object (value, model->priv->modif_stmts [INS_QUERY]);
+			g_value_set_object (value, model->priv->modif_internals->modif_stmts [INS_QUERY]);
 			break;
 		case PROP_DEL_QUERY:
-			g_value_set_object (value, model->priv->modif_stmts [DEL_QUERY]);
+			g_value_set_object (value, model->priv->modif_internals->modif_stmts [DEL_QUERY]);
 			break;
 		case PROP_UPD_QUERY:
-			g_value_set_object (value, model->priv->modif_stmts [UPD_QUERY]);
+			g_value_set_object (value, model->priv->modif_internals->modif_stmts [UPD_QUERY]);
 			break;
 		default:
 			break;
@@ -649,38 +669,38 @@
 }
 
 /*
- * Add the +/-<col num> holders to model->priv->modif_set
+ * Add the +/-<col num> holders to model->priv->modif_internals->modif_set
  */
 static gboolean
 compute_modif_set (GdaDataSelect *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);
+	if (model->priv->modif_internals->modif_set)
+		g_object_unref (model->priv->modif_internals->modif_set);
+	if (model->priv->modif_internals->exec_set)
+		model->priv->modif_internals->modif_set = gda_set_copy (model->priv->modif_internals->exec_set);
 	else
-		model->priv->modif_set = gda_set_new (NULL);
+		model->priv->modif_internals->modif_set = gda_set_new (NULL);
 
 	for (i = 0; i < NB_QUERIES; i++) {
 		GdaSet *set;
-		if (! model->priv->modif_stmts [i])
+		if (! model->priv->modif_internals->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;
+		if (! gda_statement_get_parameters (model->priv->modif_internals->modif_stmts [i], &set, error)) {
+			g_object_unref (model->priv->modif_internals->modif_set);
+			model->priv->modif_internals->modif_set = NULL;
 			return FALSE;
 		}
 
-		gda_set_merge_with_set (model->priv->modif_set, set);
+		gda_set_merge_with_set (model->priv->modif_internals->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) {
+	for (list = model->priv->modif_internals->modif_set->holders; list; list = list->next) {
 		GdaHolder *h = GDA_HOLDER (list->data);
 		g_print ("=> holder '%s'\n", gda_holder_get_id (h));
 	}
@@ -877,14 +897,14 @@
 
 		mtype = DEL_QUERY;
 
-		/* if there is no WHERE part, then use model->priv->unique_row_condition if set */
+		/* if there is no WHERE part, then use model->priv->modif_internals->unique_row_condition if set */
 		g_object_get (G_OBJECT (mod_stmt), "structure", &sqlst, NULL);
 		g_assert (sqlst);
 		del = (GdaSqlStatementDelete*) sqlst->contents;
 		if (!del->cond) {
-			if (model->priv->unique_row_condition) {
-				/* copy model->priv->unique_row_condition */
-				del->cond = gda_sql_expr_copy (model->priv->unique_row_condition);
+			if (model->priv->modif_internals->unique_row_condition) {
+				/* copy model->priv->modif_internals->unique_row_condition */
+				del->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
 				GDA_SQL_ANY_PART (del->cond)->parent = GDA_SQL_ANY_PART (del);
 				g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
 			}
@@ -896,10 +916,10 @@
 			}
 		}
 		else {
-			if (model->priv->unique_row_condition) {
-				/* replace WHERE with model->priv->unique_row_condition */
+			if (model->priv->modif_internals->unique_row_condition) {
+				/* replace WHERE with model->priv->modif_internals->unique_row_condition */
 				gda_sql_expr_free (del->cond);
-				del->cond = gda_sql_expr_copy (model->priv->unique_row_condition);
+				del->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
 				GDA_SQL_ANY_PART (del->cond)->parent = GDA_SQL_ANY_PART (del);
 				g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
 			}
@@ -917,14 +937,14 @@
 
 		mtype = UPD_QUERY;
 
-		/* if there is no WHERE part, then use model->priv->unique_row_condition if set */
+		/* if there is no WHERE part, then use model->priv->modif_internals->unique_row_condition if set */
 		g_object_get (G_OBJECT (mod_stmt), "structure", &sqlst, NULL);
 		g_assert (sqlst);
 		upd = (GdaSqlStatementUpdate*) sqlst->contents;
 		if (!upd->cond) {
-			if (model->priv->unique_row_condition) {
-				/* copy model->priv->unique_row_condition */
-				upd->cond = gda_sql_expr_copy (model->priv->unique_row_condition);
+			if (model->priv->modif_internals->unique_row_condition) {
+				/* copy model->priv->modif_internals->unique_row_condition */
+				upd->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
 				GDA_SQL_ANY_PART (upd->cond)->parent = GDA_SQL_ANY_PART (upd);
 				g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
 			}
@@ -936,10 +956,10 @@
 			}
 		}	
 		else {
-			if (model->priv->unique_row_condition) {
-				/* replace WHERE with model->priv->unique_row_condition */
+			if (model->priv->modif_internals->unique_row_condition) {
+				/* replace WHERE with model->priv->modif_internals->unique_row_condition */
 				gda_sql_expr_free (upd->cond);
-				upd->cond = gda_sql_expr_copy (model->priv->unique_row_condition);
+				upd->cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
 				GDA_SQL_ANY_PART (upd->cond)->parent = GDA_SQL_ANY_PART (upd);
 				g_object_set (G_OBJECT (mod_stmt), "structure", sqlst, NULL);
 			}
@@ -959,16 +979,16 @@
 		if (! gda_statement_check_structure (mod_stmt, error))
 			return FALSE;
 
-		if (model->priv->modif_stmts[mtype]) {
-			g_object_unref (model->priv->modif_stmts[mtype]);
-			model->priv->modif_stmts[mtype] = NULL;
+		if (model->priv->modif_internals->modif_stmts[mtype]) {
+			g_object_unref (model->priv->modif_internals->modif_stmts[mtype]);
+			model->priv->modif_internals->modif_stmts[mtype] = NULL;
 		}
 
-		/* prepare model->priv->modif_set */
+		/* prepare model->priv->modif_internals->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 */
+		/* check that all the parameters required to execute @mod_stmt are in model->priv->modif_internals->modif_set */
 		GdaSet *params;
 		GSList *list;
 		if (! gda_statement_get_parameters (mod_stmt, &params, error))
@@ -976,7 +996,7 @@
 		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));
+			eholder = gda_set_get_holder (model->priv->modif_internals->modif_set, gda_holder_get_id (holder));
 			if (!eholder) {
 				gint num;
 				gboolean is_old;
@@ -995,7 +1015,7 @@
 					g_object_unref (params);
 					return FALSE;
 				}
-				gda_set_add_holder (model->priv->modif_set, holder);
+				gda_set_add_holder (model->priv->modif_internals->modif_set, holder);
 			}
 			else if (gda_holder_get_g_type (holder) != gda_holder_get_g_type (eholder)) {
 				g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
@@ -1009,7 +1029,7 @@
 		}
 		g_object_unref (params);
 
-		model->priv->modif_stmts[mtype] = mod_stmt;
+		model->priv->modif_internals->modif_stmts[mtype] = mod_stmt;
 		g_object_ref (mod_stmt);
 	}
 	else {
@@ -1021,8 +1041,8 @@
 #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) {
+	if (model->priv->modif_internals->modif_set) {
+		for (hlist = model->priv->modif_internals->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));
@@ -1064,9 +1084,9 @@
 		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;
+		if (model->priv->modif_internals->modif_stmts[mtype]) {
+			g_object_unref (model->priv->modif_internals->modif_stmts[mtype]);
+			model->priv->modif_internals->modif_stmts[mtype] = NULL;
 		}
 
 	retval = gda_compute_dml_statements (model->priv->cnc, stmt, TRUE,
@@ -1136,7 +1156,7 @@
 	if (!check_acceptable_statement (model, error))
 		return FALSE;
 
-	if (model->priv->unique_row_condition) {
+	if (model->priv->modif_internals->unique_row_condition) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
 			     _("Unique row condition has already been specified"));
 		return FALSE;
@@ -1148,7 +1168,7 @@
 	if (!valid)
 		return FALSE;
 	
-	model->priv->unique_row_condition = gda_sql_expr_copy (expr);
+	model->priv->modif_internals->unique_row_condition = gda_sql_expr_copy (expr);
 	return TRUE;
 }
 
@@ -1191,7 +1211,7 @@
 	if (!check_acceptable_statement (model, error))
 		return FALSE;
 
-	if (model->priv->unique_row_condition) {
+	if (model->priv->modif_internals->unique_row_condition) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MODIFICATION_STATEMENT_ERROR,
 			     _("Unique row condition has already been specified"));
 		return FALSE;
@@ -1376,12 +1396,14 @@
 			flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
 	}
 
-	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;
+	if (! imodel->priv->modif_internals->safely_locked) {
+		if (imodel->priv->modif_internals->modif_stmts [UPD_QUERY])
+			flags |= GDA_DATA_MODEL_ACCESS_UPDATE;
+		if (imodel->priv->modif_internals->modif_stmts [INS_QUERY])
+			flags |= GDA_DATA_MODEL_ACCESS_INSERT;
+		if (imodel->priv->modif_internals->modif_stmts [DEL_QUERY])
+			flags |= GDA_DATA_MODEL_ACCESS_DELETE;
+	}
 
 	return flags;
 }
@@ -1471,7 +1493,8 @@
 			GdaDataModel *tmpmodel;
 			if (!dstmt->select || !dstmt->params) {
 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
-					     _("Unable to retreive data after modifications"));
+					     _("Unable to retreive data after modifications, no further modification will be allowed"));
+				imodel->priv->modif_internals->safely_locked = TRUE;
 				return NULL;
 			}
 			tmpmodel = gda_connection_statement_execute_select (imodel->priv->cnc, 
@@ -1479,14 +1502,16 @@
 									    dstmt->params, NULL);
 			if (!tmpmodel) {
 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
-					     _("Unable to retreive data after modifications"));
+					     _("Unable to retreive data after modifications, no further modification will be allowed"));
+				imodel->priv->modif_internals->safely_locked = TRUE;
 				return NULL;
 			}
 
 			if (gda_data_model_get_n_rows (tmpmodel) != 1) {
 				g_object_unref (tmpmodel);
 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
-					     _("Unable to retreive data after modifications"));
+					     _("Unable to retreive data after modifications, no further modification will be allowed"));
+				imodel->priv->modif_internals->safely_locked = TRUE;
 				return NULL;
 			}
 
@@ -1507,7 +1532,8 @@
 						g_object_unref (tmpmodel);
 						g_object_unref (prow);
 						g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
-							     _("Unable to retreive data after modifications"));
+							     _("Unable to retreive data after modifications, no further modification will be allowed"));
+						imodel->priv->modif_internals->safely_locked = TRUE;
 						return NULL;
 					}
 				}
@@ -1541,16 +1567,21 @@
 static GdaValueAttribute
 gda_data_select_get_attributes_at (GdaDataModel *model, gint col, gint row)
 {
-	GdaValueAttribute flags = 0;
+	GdaValueAttribute flags = GDA_VALUE_ATTR_IS_UNCHANGED;
 	GdaDataSelect *imodel;
 
 	g_return_val_if_fail (GDA_IS_DATA_SELECT (model), 0);
 	imodel = (GdaDataSelect *) model;
 	g_return_val_if_fail (imodel->priv, 0);
 	
-	if (! imodel->priv->modif_stmts [UPD_QUERY])
+	if (imodel->priv->modif_internals->safely_locked || !imodel->priv->modif_internals->modif_stmts [UPD_QUERY])
 		flags = GDA_VALUE_ATTR_NO_MODIF;
-	
+	GdaColumn *gdacol = g_slist_nth_data (imodel->priv->columns, col);
+	if (gdacol) {
+		if (gda_column_get_allow_null (gdacol))
+			flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
+	}
+
 	return flags;
 }
 
@@ -1750,7 +1781,7 @@
 }
 
 /*
- * creates a derivative of the model->priv->modif_stmts [UPD_QUERY] statement
+ * creates a derivative of the model->priv->modif_internals->modif_stmts [UPD_QUERY] statement
  * where only the columns where @bv->data[colnum] is not 0 are updated.
  *
  * Returns: a new #GdaStatement, or %NULL
@@ -1763,8 +1794,8 @@
 	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 (model->priv->modif_internals->modif_stmts [UPD_QUERY]);
+	g_object_get (G_OBJECT (model->priv->modif_internals->modif_stmts [UPD_QUERY]), "structure", &sqlst, NULL);
 	g_assert (sqlst);
 	g_free (sqlst->sql);
 	sqlst->sql = NULL;
@@ -1843,7 +1874,7 @@
 }
 
 /*
- * creates a derivative of the model->priv->modif_stmts [INS_QUERY] statement
+ * creates a derivative of the model->priv->modif_internals->modif_stmts [INS_QUERY] statement
  * where only the columns where @bv->data[colnum] is not 0 are not mentionned.
  *
  * Returns: a new #GdaStatement, or %NULL
@@ -1856,8 +1887,8 @@
 	GdaStatement *insstmt = NULL;
 
 	/* get a copy of complete INSERT stmt */
-	g_assert (model->priv->modif_stmts [INS_QUERY]);
-	g_object_get (G_OBJECT (model->priv->modif_stmts [INS_QUERY]), "structure", &sqlst, NULL);
+	g_assert (model->priv->modif_internals->modif_stmts [INS_QUERY]);
+	g_object_get (G_OBJECT (model->priv->modif_internals->modif_stmts [INS_QUERY]), "structure", &sqlst, NULL);
 	g_assert (sqlst);
 	g_free (sqlst->sql);
 	sqlst->sql = NULL;
@@ -1941,7 +1972,7 @@
  * after an UPDATE or INSERT statement has been run)
  *
  * The new created statement is merely the copy of the SELECT statement where the WHERE part
- * has been altered as "<original condition> AND <update condition>"
+ * has been altered as "<update condition>"
  *
  * Returns: a new #GdaStatement, or %NULL
  */
@@ -1966,13 +1997,13 @@
 		return NULL;
 	}
 	
-	if (model->priv->unique_row_condition)
-		row_cond = gda_sql_expr_copy (model->priv->unique_row_condition);
-	else if (model->priv->modif_stmts [DEL_QUERY]) {
+	if (model->priv->modif_internals->unique_row_condition)
+		row_cond = gda_sql_expr_copy (model->priv->modif_internals->unique_row_condition);
+	else if (model->priv->modif_internals->modif_stmts [DEL_QUERY]) {
 		GdaStatement *del_stmt;
 		GdaSqlStatement *del_sqlst;
 		GdaSqlStatementDelete *del;
-		del_stmt = model->priv->modif_stmts [DEL_QUERY];
+		del_stmt = model->priv->modif_internals->modif_stmts [DEL_QUERY];
 		
 		g_object_get (G_OBJECT (del_stmt), "structure", &del_sqlst, NULL);
 		del = (GdaSqlStatementDelete*) del_sqlst->contents;
@@ -1984,11 +2015,11 @@
 			row_cond = NULL;
 		}
 	}
-	else if (model->priv->modif_stmts [UPD_QUERY]) {
+	else if (model->priv->modif_internals->modif_stmts [UPD_QUERY]) {
 		GdaStatement *upd_stmt;
 		GdaSqlStatement *upd_sqlst;
 		GdaSqlStatementUpdate *upd;
-		upd_stmt = model->priv->modif_stmts [UPD_QUERY];
+		upd_stmt = model->priv->modif_internals->modif_stmts [UPD_QUERY];
 		
 		g_object_get (G_OBJECT (upd_stmt), "structure", &upd_sqlst, NULL);
 		upd = (GdaSqlStatementUpdate*) upd_sqlst->contents;
@@ -2020,25 +2051,10 @@
 	sel = (GdaSqlStatementSelect*) sel_sqlst->contents;
 	g_free (sel_sqlst->sql);
 	sel_sqlst->sql = NULL;
-	if (sel->where_cond) {
-		GdaSqlExpr *and_expr;
-		GdaSqlOperation *and_cond;
-
-		and_expr = gda_sql_expr_new (GDA_SQL_ANY_PART (sel));
-		and_cond = gda_sql_operation_new (GDA_SQL_ANY_PART (and_expr));
-		and_expr->cond = and_cond;
-		and_cond->operator_type = GDA_SQL_OPERATOR_TYPE_AND;
-		and_cond->operands = g_slist_append (NULL, sel->where_cond);
-		GDA_SQL_ANY_PART (sel->where_cond)->parent = GDA_SQL_ANY_PART (and_cond);
-		sel->where_cond = and_expr;
-
-		and_cond->operands = g_slist_append (and_cond->operands, row_cond);
-		GDA_SQL_ANY_PART (row_cond)->parent = GDA_SQL_ANY_PART (and_cond);
-	}
-	else {
-		sel->where_cond = row_cond;
-		GDA_SQL_ANY_PART (row_cond)->parent = GDA_SQL_ANY_PART (sel);
-	}
+	if (sel->where_cond) 
+		gda_sql_expr_free (sel->where_cond);
+	sel->where_cond = row_cond;
+	GDA_SQL_ANY_PART (row_cond)->parent = GDA_SQL_ANY_PART (sel);
 
 	ret_stmt = (GdaStatement *) g_object_new (GDA_TYPE_STATEMENT, "structure", sel_sqlst, NULL);
 
@@ -2103,12 +2119,17 @@
 	/* arguments check */
 	g_assert (bv);
 
+	if (imodel->priv->modif_internals->safely_locked) {
+		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
+			     _("Modifications are not allowed anymore"));
+		return 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]) {
+	if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
 			     _("No UPDATE statement provided"));
 		return FALSE;
@@ -2119,15 +2140,15 @@
 		return FALSE;
 
 	/* compute UPDATE statement */
-	if (! imodel->priv->upd_stmts)
-		imodel->priv->upd_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal, 
+	if (! imodel->priv->modif_internals->upd_stmts)
+		imodel->priv->modif_internals->upd_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal, 
 								 (GDestroyNotify) bvector_free, g_object_unref);
-	stmt = g_hash_table_lookup (imodel->priv->upd_stmts, bv);
+	stmt = g_hash_table_lookup (imodel->priv->modif_internals->upd_stmts, bv);
 	if (! stmt) {
 		stmt = compute_single_update_stmt (imodel, bv, error);
 		if (stmt) {
 			free_bv = FALSE;
-			g_hash_table_insert (imodel->priv->upd_stmts, bv, stmt);
+			g_hash_table_insert (imodel->priv->modif_internals->upd_stmts, bv, stmt);
 		}
 		else {
 			bvector_free (bv);
@@ -2139,7 +2160,7 @@
 	ncols = gda_data_select_get_n_columns ((GdaDataModel*) imodel);
 	for (i = 0; i < ncols; i++) {
 		str = g_strdup_printf ("-%d", i);
-		holder = gda_set_get_holder (imodel->priv->modif_set, str);
+		holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
 		g_free (str);
 		if (holder) {
 			const GValue *cvalue;
@@ -2161,7 +2182,7 @@
 	GError *lerror = NULL;
 	sql = gda_statement_to_sql_extended (stmt,
 					     imodel->priv->cnc,
-					     imodel->priv->modif_set, 
+					     imodel->priv->modif_internals->modif_set, 
 					     GDA_STATEMENT_SQL_PRETTY, NULL,
 					     &lerror);
 	g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2171,31 +2192,56 @@
 #endif
 
 	if (gda_connection_statement_execute_non_select (imodel->priv->cnc, stmt,
-							 imodel->priv->modif_set, NULL, error) == -1)
+							 imodel->priv->modif_internals->modif_set, NULL, error) == -1)
 		return FALSE;
 	
 	/* mark that this row has been modified */
 	DelayedSelectStmt *dstmt;
 	dstmt = g_new0 (DelayedSelectStmt, 1);
-	if (! imodel->priv->one_row_select_stmt)
-		imodel->priv->one_row_select_stmt = compute_single_select_stmt (imodel, error);
-	if (imodel->priv->one_row_select_stmt) {
-		dstmt->select = g_object_ref (imodel->priv->one_row_select_stmt);
+	if (! imodel->priv->modif_internals->one_row_select_stmt)
+		imodel->priv->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
+	if (imodel->priv->modif_internals->one_row_select_stmt) {
+		dstmt->select = g_object_ref (imodel->priv->modif_internals->one_row_select_stmt);
 		gda_statement_get_parameters (dstmt->select, &(dstmt->params), NULL);
 		if (dstmt->params) {
 			GSList *list;
-			for (list = dstmt->params->holders; list; list = list->next) {
+			gboolean allok = TRUE;
+
+			/* overwrite old values with new values if some have been provided */
+			for (list = imodel->priv->modif_internals->modif_set->holders; list; list = list->next) {
+				GdaHolder *h = (GdaHolder*) list->data;
+				gint res;
+				gboolean old;
+				if (gda_holder_is_valid ((GdaHolder*) list->data) && 
+				    param_name_to_int (gda_holder_get_id (h), &res, &old) && 
+				    !old) {
+					str = g_strdup_printf ("-%d", res);
+					holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
+					g_free (str);
+					if (holder &&
+					    ! gda_holder_set_value (holder, gda_holder_get_value (h), error)) {
+						allok = FALSE;
+						break;
+					}
+				}
+			}
+
+			for (list = dstmt->params->holders; list && allok; list = list->next) {
 				GdaHolder *holder = GDA_HOLDER (list->data);
 				GdaHolder *eholder;
-				eholder = gda_set_get_holder (imodel->priv->modif_set, 
+				eholder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, 
 							      gda_holder_get_id (holder));
 				if (!eholder || 
-				    ! gda_holder_set_value (holder, gda_holder_get_value (eholder), error)) {
-					g_object_unref (dstmt->params);
-					dstmt->params = NULL;
+				    ! gda_holder_set_value (holder, gda_holder_get_value (eholder), NULL)) {
+					allok = FALSE;
 					break;
 				}
 			}
+
+			if (!allok) {
+				g_object_unref (dstmt->params);
+				dstmt->params = NULL;
+			}
 		}
 	}
 	dstmt->row = NULL;
@@ -2227,12 +2273,17 @@
 	imodel = (GdaDataSelect *) model;
 	g_return_val_if_fail (imodel->priv, FALSE);
 
+	if (imodel->priv->modif_internals->safely_locked) {
+		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
+			     _("Modifications are not allowed anymore"));
+		return 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]) {
+	if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
 			     _("No UPDATE statement provided"));
 		return FALSE;
@@ -2246,9 +2297,17 @@
 		return FALSE;
 	}
 	
+	/* invalidate all the imodel->priv->modif_internals->modif_set's value holders */
+	GSList *list;
+	for (list = imodel->priv->modif_internals->modif_set->holders; list; list = list->next) {
+		GdaHolder *h = (GdaHolder*) list->data;
+		if (param_name_to_int (gda_holder_get_id (h), NULL, NULL))
+			gda_holder_force_invalid ((GdaHolder*) list->data);
+	}
+
 	/* give values to params for new value */
 	str = g_strdup_printf ("+%d", col);
-	holder = gda_set_get_holder (imodel->priv->modif_set, str);
+	holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
 	g_free (str);
 	if (! holder) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2294,12 +2353,17 @@
 	/* arguments check */
 	g_return_val_if_fail (imodel->priv, FALSE);
 
+	if (imodel->priv->modif_internals->safely_locked) {
+		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
+			     _("Modifications are not allowed anymore"));
+		return 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]) {
+	if (! imodel->priv->modif_internals->modif_stmts [UPD_QUERY]) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
 			     _("No UPDATE statement provided"));
 		return FALSE;
@@ -2332,13 +2396,21 @@
 		return TRUE;
 	}
 
+	/* invalidate all the imodel->priv->modif_internals->modif_set's value holders */
+	GSList *slist;
+	for (slist = imodel->priv->modif_internals->modif_set->holders; slist; slist = slist->next) {
+		GdaHolder *h = (GdaHolder*) slist->data;
+		if (param_name_to_int (gda_holder_get_id (h), NULL, NULL))
+			gda_holder_force_invalid ((GdaHolder*) slist->data);
+	}
+
 	/* give values to params for new values */
 	for (i = 0, list = values; list; i++, list = list->next) {
 		if (!bv->data[i])
 			continue;
 
 		str = g_strdup_printf ("+%d", i);
-		holder = gda_set_get_holder (imodel->priv->modif_set, str);
+		holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
 		g_free (str);
 		if (! holder) {
 			g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2368,12 +2440,17 @@
 
 	g_return_val_if_fail (imodel->priv, -1);
 
+	if (imodel->priv->modif_internals->safely_locked) {
+		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
+			     _("Modifications are not allowed anymore"));
+		return 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 -1;
 	}
-	if (! imodel->priv->modif_stmts [INS_QUERY]) {
+	if (! imodel->priv->modif_internals->modif_stmts [INS_QUERY]) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
 			     _("No INSERT statement provided"));
 		return -1;
@@ -2414,15 +2491,15 @@
 
 	/* compute INSERT statement */
 	GdaStatement *stmt;
-	if (! imodel->priv->ins_stmts)
-		imodel->priv->ins_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal, 
+	if (! imodel->priv->modif_internals->ins_stmts)
+		imodel->priv->modif_internals->ins_stmts = g_hash_table_new_full ((GHashFunc) bvector_hash, (GEqualFunc) bvector_equal, 
 								 (GDestroyNotify) bvector_free, g_object_unref);
-	stmt = g_hash_table_lookup (imodel->priv->ins_stmts, bv);
+	stmt = g_hash_table_lookup (imodel->priv->modif_internals->ins_stmts, bv);
 	if (! stmt) {
 		stmt = compute_single_insert_stmt (imodel, bv, error);
 		if (stmt) {
 			free_bv = FALSE;
-			g_hash_table_insert (imodel->priv->ins_stmts, bv, stmt);
+			g_hash_table_insert (imodel->priv->modif_internals->ins_stmts, bv, stmt);
 		}
 		else {
 			bvector_free (bv);
@@ -2436,7 +2513,7 @@
 			continue;
 
 		str = g_strdup_printf ("+%d", i);
-		holder = gda_set_get_holder (imodel->priv->modif_set, str);
+		holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
 		g_free (str);
 		if (! holder) {
 			g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
@@ -2459,7 +2536,7 @@
 	GError *lerror = NULL;
 	sql = gda_statement_to_sql_extended (stmt,
 					     imodel->priv->cnc,
-					     imodel->priv->modif_set, 
+					     imodel->priv->modif_internals->modif_set, 
 					     GDA_STATEMENT_SQL_PRETTY, NULL,
 					     &lerror);
 	g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2475,24 +2552,24 @@
 
 	GdaSet *last_insert;
 	if (gda_connection_statement_execute_non_select (imodel->priv->cnc, stmt,
-							 imodel->priv->modif_set, &last_insert, error) == -1)
+							 imodel->priv->modif_internals->modif_set, &last_insert, error) == -1)
 		return -1;
 
 	/* mark that this row has been modified */
 	DelayedSelectStmt *dstmt;
 	dstmt = g_new0 (DelayedSelectStmt, 1);
-	if (! imodel->priv->one_row_select_stmt)
-		imodel->priv->one_row_select_stmt = compute_single_select_stmt (imodel, error);
-	if (last_insert && imodel->priv->one_row_select_stmt) {
-		dstmt->select = g_object_ref (imodel->priv->one_row_select_stmt);
+	if (! imodel->priv->modif_internals->one_row_select_stmt)
+		imodel->priv->modif_internals->one_row_select_stmt = compute_single_select_stmt (imodel, error);
+	if (last_insert && imodel->priv->modif_internals->one_row_select_stmt) {
+		dstmt->select = g_object_ref (imodel->priv->modif_internals->one_row_select_stmt);
 		gda_statement_get_parameters (dstmt->select, &(dstmt->params), NULL);
 		if (dstmt->params) {
 			GSList *list;
-			if (! imodel->priv->insert_to_select_mapping)
-				imodel->priv->insert_to_select_mapping = 
+			if (! imodel->priv->modif_internals->insert_to_select_mapping)
+				imodel->priv->modif_internals->insert_to_select_mapping = 
 					compute_insert_select_params_mapping (dstmt->params, last_insert,
-									      imodel->priv->unique_row_condition);
-			if (imodel->priv->insert_to_select_mapping) {
+									      imodel->priv->modif_internals->unique_row_condition);
+			if (imodel->priv->modif_internals->insert_to_select_mapping) {
 				for (list = dstmt->params->holders; list; list = list->next) {
 					GdaHolder *holder = GDA_HOLDER (list->data);
 					GdaHolder *eholder;
@@ -2501,7 +2578,7 @@
 					g_assert (param_name_to_int (gda_holder_get_id (holder), &pos, NULL));
 					
 					eholder = g_slist_nth_data (last_insert->holders, 
-								    imodel->priv->insert_to_select_mapping[pos]);
+								    imodel->priv->modif_internals->insert_to_select_mapping[pos]);
 					if (!eholder || 
 					    ! gda_holder_set_value (holder, gda_holder_get_value (eholder), error)) {
 						g_object_unref (dstmt->params);
@@ -2546,12 +2623,17 @@
 
 	g_return_val_if_fail (imodel->priv, FALSE);
 
+	if (imodel->priv->modif_internals->safely_locked) {
+		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_SAFETY_LOCKED_ERROR,
+			     _("Modifications are not allowed anymore"));
+		return 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 [DEL_QUERY]) {
+	if (! imodel->priv->modif_internals->modif_stmts [DEL_QUERY]) {
 		g_set_error (error, GDA_DATA_SELECT_ERROR, GDA_DATA_SELECT_MISSING_MODIFICATION_STATEMENT_ERROR,
 			     _("No DELETE statement provided"));
 		return FALSE;
@@ -2564,7 +2646,7 @@
 	ncols = gda_data_select_get_n_columns (model);
 	for (i = 0; i < ncols; i++) {
 		str = g_strdup_printf ("-%d", i);
-		holder = gda_set_get_holder (imodel->priv->modif_set, str);
+		holder = gda_set_get_holder (imodel->priv->modif_internals->modif_set, str);
 		g_free (str);
 		if (holder) {
 			const GValue *cvalue;
@@ -2579,9 +2661,9 @@
 #ifdef GDA_DEBUG_NO
 	gchar *sql;
 	GError *lerror = NULL;
-	sql = gda_statement_to_sql_extended (imodel->priv->modif_stmts [DEL_QUERY],
+	sql = gda_statement_to_sql_extended (imodel->priv->modif_internals->modif_stmts [DEL_QUERY],
 					     imodel->priv->cnc,
-					     imodel->priv->modif_set, 
+					     imodel->priv->modif_internals->modif_set, 
 					     GDA_STATEMENT_SQL_PRETTY, NULL,
 					     &lerror);
 	g_print ("%s(): SQL=> %s\n", __FUNCTION__, sql);
@@ -2590,8 +2672,8 @@
 	g_free (sql);
 #endif
 	if (gda_connection_statement_execute_non_select (imodel->priv->cnc, 
-							 imodel->priv->modif_stmts [DEL_QUERY],
-							 imodel->priv->modif_set, NULL, error) == -1)
+							 imodel->priv->modif_internals->modif_stmts [DEL_QUERY],
+							 imodel->priv->modif_internals->modif_set, NULL, error) == -1)
 		return FALSE;
 
 	/* mark that this row has been removed */
@@ -2615,7 +2697,7 @@
  *
  * The way of preceeding is: 
  *   - for each parameter required by model->one_row_select_stmt statement (the @sel_params argument), 
- *     use the model->priv->unique_row_condition to get the name of the corresponding column (the GdaHolder's ID
+ *     use the model->priv->modif_internals->unique_row_condition to get the name of the corresponding column (the GdaHolder's ID
  *     is "-<num1>" )
  *   - from the column name get the GdaHolder in the GdaSet retruned after the INSERT statement (the
  *     @ins_values argument) using the "name" property of each GdaHolder in the GdaSet (the GdaHolder's ID

Modified: trunk/libgda/gda-data-select.h
==============================================================================
--- trunk/libgda/gda-data-select.h	(original)
+++ trunk/libgda/gda-data-select.h	Sun Sep  7 19:06:38 2008
@@ -50,7 +50,8 @@
 	GDA_DATA_SELECT_CONNECTION_ERROR,
 	GDA_DATA_SELECT_VALUE_ERROR,
 	GDA_DATA_SELECT_ACCESS_ERROR,
-	GDA_DATA_SELECT_SQL_ERROR
+	GDA_DATA_SELECT_SQL_ERROR,
+	GDA_DATA_SELECT_SAFETY_LOCKED_ERROR
 };
 
 struct _GdaDataSelect {

Modified: trunk/libgda/sqlite/virtual/gda-vconnection-hub.c
==============================================================================
--- trunk/libgda/sqlite/virtual/gda-vconnection-hub.c	(original)
+++ trunk/libgda/sqlite/virtual/gda-vconnection-hub.c	Sun Sep  7 19:06:38 2008
@@ -27,7 +27,7 @@
 #include "gda-virtual-provider.h"
 #include <sql-parser/gda-sql-parser.h>
 #include <libgda/gda-util.h>
-#include <libgda/gda-data-model-query.h>
+#include <libgda/gda-data-select.h>
 
 typedef struct {
 	GdaVconnectionHub *hub;
@@ -404,11 +404,10 @@
 	tmp = g_strdup_printf ("SELECT * FROM %s", g_value_get_string (lspec->table_name));
 	stmt = gda_sql_parser_parse_string (internal_parser, tmp, NULL, NULL);
 	g_free (tmp);
-	model = gda_data_model_query_new (lspec->hc->cnc, stmt);
+	model = gda_connection_statement_execute_select (lspec->hc->cnc, stmt, NULL, NULL);
 	g_object_unref (stmt);
-	gda_data_model_query_compute_modification_queries (GDA_DATA_MODEL_QUERY (model), 
-							   g_value_get_string (lspec->table_name), 
-							   GDA_DATA_MODEL_QUERY_OPTION_USE_ALL_FIELDS_IF_NO_PK, NULL);
+	if (model) 
+		gda_data_select_compute_modification_statements (GDA_DATA_SELECT (model), NULL);
 
 	return model;
 }

Modified: trunk/tests/data-models/Makefile.am
==============================================================================
--- trunk/tests/data-models/Makefile.am	(original)
+++ trunk/tests/data-models/Makefile.am	Sun Sep  7 19:06:38 2008
@@ -7,8 +7,8 @@
 	-DTOP_SRC_DIR=\""$(top_srcdir)"\" \
 	-DTOP_BUILD_DIR=\""$(top_builddir)"\"
 
-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
+check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_model_query
+TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_model_query
 
 common_sources = 
 
@@ -37,13 +37,20 @@
 
 check_pmodel_SOURCES = $(common_sources) check_pmodel.c
 check_pmodel_CFLAGS = \
-	-I$(top_srcdir)/libgda/sqlite \
-	-I$(top_srcdir)/libgda/sqlite/pmodel
+	-I$(top_srcdir)/libgda/sqlite
 check_pmodel_LDADD = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(top_builddir)/tests/libgda-test-4.0.la \
 	$(LIBGDA_LIBS)
 
+check_model_query_SOURCES = $(common_sources) check_model_query.c
+check_model_query_CFLAGS = \
+	-I$(top_srcdir)/libgda/sqlite
+check_model_query_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 \
@@ -54,4 +61,4 @@
 	pmodel_data_locations.xml
 
 CLEANFILES = \
-	pmodel.db
+	pmodel.db modelquery.db

Added: trunk/tests/data-models/check_model_query.c
==============================================================================
--- (empty file)
+++ trunk/tests/data-models/check_model_query.c	Sun Sep  7 19:06:38 2008
@@ -0,0 +1,696 @@
+#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 <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
+
+/* signals checking */
+typedef struct {
+	GdaDataModel *model;
+	gchar         type; /* U, I, D */
+	gint          row;
+} SigEvent;
+static GSList *signals = NULL;
+static void     monitor_model_signals (GdaDataModel *model);
+static void     signal_callback (GdaDataModel *model, gint row, gchar *type);
+static void     clear_signals (void);
+static gboolean check_expected_signal (GdaDataModel *model, gchar type, gint row);
+static gboolean check_no_expected_signal (GdaDataModel *model);
+
+/* utility functions */
+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);
+static gboolean check_set_value_at (GdaDataModel *model, gint col, gint row, 
+				    const GValue *set_value, 
+				    GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
+static gboolean check_set_value_at_ext (GdaDataModel *model, gint col, gint row, 
+					const GValue *set_value, 
+					GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params, GError **error);
+static gboolean check_set_values (GdaDataModel *model, gint row, GList *set_values,
+				  GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
+static gint check_append_values (GdaDataModel *model, GList *set_values,
+				 GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
+
+typedef gboolean (*TestFunc) (GdaConnection *);
+static gint test1 (GdaConnection *cnc);
+static gint test2 (GdaConnection *cnc);
+
+TestFunc tests[] = {
+        test1,
+        test2
+};
+
+int
+main (int argc, char **argv)
+{
+	gint i, ntests = 0, number_failed = 0;
+	GdaConnection *cnc;
+
+	/* set up test environment */
+        g_setenv ("GDA_TOP_BUILD_DIR", TOP_BUILD_DIR, 0);
+	g_setenv ("GDA_TOP_SRC_DIR", TOP_SRC_DIR, TRUE);
+	gda_init ();
+
+	g_unlink ("modelquery.db");
+	cnc = setup_connection ();
+	
+	for (i = 0; i < sizeof (tests) / sizeof (TestFunc); i++) {
+		g_print ("---------- test %d ----------\n", i+1);
+		gint n = tests[i] (cnc);
+		number_failed += n;
+		if (n > 0) 
+			g_print ("Test %d failed\n", i+1);
+		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=modelquery", 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");
+
+	/* update meta store */
+	if (! gda_connection_update_meta_store (cnc, NULL, &error)) {
+		g_print ("Error updateing meta store: %s\n", error && error->message ? error->message : "No detail");
+                g_error_free (error);
+                exit (EXIT_FAILURE);
+	}
+
+        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;
+}
+
+/*
+ * check modifications statements' setting
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test1 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt;
+	gint nfailed = 0;
+
+	/* create GdaDataModelQuery */
+	stmt = stmt_from_string ("SELECT * FROM customers WHERE id= ##theid::gint");
+	model = (GdaDataModel*) gda_data_model_query_new (cnc, stmt, NULL);
+	g_assert (model);
+	if (gda_data_model_get_n_rows (model) >= 0) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Returned n_rows should be -1\n");
+#endif
+		goto out;
+	}
+
+	if (gda_data_model_get_value_at (model, 0, 0, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_get_value_at() should return NULL\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;
+}
+
+/*
+ * check gda_data_model_set_value_at()
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test2 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt, *mod_stmt;
+	gint nfailed = 0;
+	GValue *value;
+
+	clear_signals ();
+
+	/* create GdaDataModelQuery */
+	stmt = stmt_from_string ("SELECT * FROM customers");
+	model = (GdaDataModel*) gda_data_model_query_new (cnc, stmt, NULL);
+
+	/* test INSERT with undefined params */
+	mod_stmt = stmt_from_string ("UPDATE customers SET name = ##+1::string, last_update = ##+2::timestamp WHERE id = ##-0::gint");
+	if (!gda_data_model_query_set_modification_statement (GDA_DATA_MODEL_QUERY (model), mod_stmt, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_query_set_modification_statement() should have succedded, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+
+	/****/
+	monitor_model_signals (model);
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "Jack");
+	if (! check_set_value_at (model, 1, 0, value, cnc, stmt, NULL)) {
+		nfailed ++;
+		goto out;
+	}
+	if (! check_expected_signal (model, 'U', 0)) {
+		nfailed++;
+		goto out;
+	}
+	gda_value_free (value);
+	clear_signals ();
+
+	/****/
+	gda_value_set_from_string ((value = gda_value_new (GDA_TYPE_TIMESTAMP)), 
+				   "2009-11-30 11:22:33", GDA_TYPE_TIMESTAMP);
+	if (! check_set_value_at (model, 2, 1, value, cnc, stmt, NULL)) {
+		nfailed ++;
+		goto out;
+	}
+	gda_value_free (value);
+
+	/****/
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "Henry");
+	if (! check_set_value_at (model, 1, 0, value, cnc, stmt, NULL)) {
+		nfailed ++;
+		goto out;
+	}
+	gda_value_free (value);
+
+	/****/
+	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);
+	error = NULL;
+
+	GdaDataModel *copy;
+	copy = (GdaDataModel *) gda_data_model_array_copy_model (model, &error);
+	if (!copy) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not copy data model: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+	gda_data_model_dump (model, stdout);
+	gda_data_model_dump (copy, stdout);
+	if (! gda_data_model_query_refresh (GDA_DATA_MODEL_QUERY (model), &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_query_refresh() failed: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+	gda_data_model_dump (model, stdout);
+
+ out:
+	g_object_unref (model);
+	g_object_unref (stmt);
+
+	return nfailed;
+}
+
+
+
+/*
+ * Checking value function:
+ *  - reads the value of @model at the provided column and row and compares with the expected @set_value
+ *  - if @stmt is not NULL, then re-run the statement and compares with @model
+ */
+static gboolean
+check_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *set_value, 
+		    GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params)
+{
+	GError *error = NULL;
+	gboolean retval;
+	retval = check_set_value_at_ext (model, col, row, set_value, cnc, stmt, stmt_params, &error);
+	if (error)
+		g_error_free (error);
+	return retval;
+}
+
+static gboolean
+check_set_value_at_ext (GdaDataModel *model, gint col, gint row, 
+			const GValue *set_value, 
+			GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params, GError **error)
+{
+	const GValue *get_value;
+
+	if (! gda_data_model_set_value_at (model, col, row, set_value, error)) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_set_value_at(%d,%d) failed: %s\n",
+			 col, row, 
+			 error && *error && (*error)->message ? (*error)->message : "No detail");
+#endif
+		return FALSE;
+	}
+	get_value = gda_data_model_get_value_at (model, col, row, error);
+	if (!get_value) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Can't get data model's value: %s\n",
+			 error && *error && (*error)->message ? (*error)->message : "No detail");
+#endif
+		return FALSE;
+	}
+	if (gda_value_compare (get_value, set_value)) {
+#ifdef CHECK_EXTRA_INFO
+		gchar *s1, *s2;
+		s1 = gda_value_stringify (get_value);
+		s2 = gda_value_stringify (set_value);
+		g_print ("gda_data_model_get_value_at(%d,%d) returned '%s' when it should have returned '%s'\n", col, row, s1, s2);
+		g_free (s1);
+		g_free (s2);
+#endif
+		return FALSE;
+	}
+
+	if (stmt) {
+		/* run the statement and compare it with @model */
+		GdaDataModel *rerun;
+		GdaDataComparator *cmp;
+		gboolean cmpres = TRUE;
+		rerun = gda_connection_statement_execute_select (cnc, stmt, stmt_params, error);
+		if (!rerun) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not re-run the SELECT statement: %s\n",
+				 error && *error && (*error)->message ? (*error)->message : "No detail");
+#endif
+			return FALSE;
+		}
+
+		cmp = (GdaDataComparator*) gda_data_comparator_new (model, rerun);
+		if (! gda_data_comparator_compute_diff (cmp, error)) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not compute the data model differences: %s\n",
+				 error && *error && (*error)->message ? (*error)->message : "No detail");
+#endif
+			cmpres = FALSE;
+		}
+		if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("There are some differences when re-running the SELECT statement...\n");
+#endif
+			cmpres = FALSE;
+			gda_data_model_dump (model, stdout);
+			gda_data_model_dump (rerun, stdout);
+			if (error) {
+				g_set_error (error, 0, -1,
+					     "There are some differences when re-running the SELECT statement...");
+			}
+		}
+		g_object_unref (cmp);
+		g_object_unref (rerun);
+		return cmpres;
+	}
+
+	return TRUE;
+}
+
+/*
+ * Checking value function:
+ *  - reads the value of @model at the provided column and row and compares with the expected @set_value
+ *  - if @stmt is not NULL, then re-run the statement and compares with @model
+ */
+static gboolean
+check_set_values (GdaDataModel *model, gint row, GList *set_values, GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params)
+{
+	GError *error = NULL;
+	GList *list;
+	gint i;
+
+	if (! gda_data_model_set_values (model, row, set_values, &error)) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_set_values (%d) failed: %s\n",
+			 row, 
+			 error && error->message ? error->message : "No detail");
+#endif
+		return FALSE;
+	}
+
+	for (i = 0, list = set_values; list; i++, list = list->next) {
+		const GValue *get_value;
+		
+		if (!list->data)
+			continue;
+		
+		get_value = gda_data_model_get_value_at (model, i, row, &error);
+		if (!get_value) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Can't get data model's value: %s",
+				 error && error->message ? error->message : "No detail");
+#endif
+			return FALSE;
+		}
+		if (gda_value_compare (get_value, (GValue*) list->data)) {
+#ifdef CHECK_EXTRA_INFO
+			gchar *s1, *s2;
+			s1 = gda_value_stringify (get_value);
+			s2 = gda_value_stringify ((GValue*) list->data);
+			g_print ("gda_data_model_get_value_at(%d,%d) returned '%s' when it should have returned '%s'\n", i, row, s1, s2);
+			g_free (s1);
+			g_free (s2);
+#endif
+			return FALSE;
+		}
+	}
+
+	if (stmt) {
+		/* run the statement and compare it with @model */
+		GdaDataModel *rerun;
+		GdaDataComparator *cmp;
+		GError *error = NULL;
+		gboolean cmpres = TRUE;
+		rerun = gda_connection_statement_execute_select (cnc, stmt, stmt_params, &error);
+		if (!rerun) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not re-run the SELECT statement: %s\n",
+				 error && error->message ? error->message : "No detail");
+#endif
+			return FALSE;
+		}
+
+		cmp = (GdaDataComparator*) gda_data_comparator_new (model, rerun);
+		if (! gda_data_comparator_compute_diff (cmp, &error)) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not compute the data model differences: %s\n",
+				 error && error->message ? error->message : "No detail");
+#endif
+			cmpres = FALSE;
+		}
+		if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("There are some differences when re-running the SELECT statement...\n");
+#endif
+			cmpres = FALSE;
+		}
+
+		g_object_unref (cmp);
+		g_object_unref (rerun);
+		return cmpres;
+	}
+
+	return TRUE;
+}
+
+/*
+ * Checking value function:
+ *  - reads the value of @model at the provided column and row and compares with the expected @set_value
+ *  - if @stmt is not NULL, then re-run the statement and compares with @model
+ *
+ * Returns: -1 if an error occurred, or the new row number
+ */
+static gint
+check_append_values (GdaDataModel *model, GList *set_values, GdaConnection *cnc, 
+		     GdaStatement *stmt, GdaSet *stmt_params)
+{
+	GError *error = NULL;
+	GList *list;
+	gint i, newrow;
+
+	newrow = gda_data_model_append_values (model, set_values, &error);
+	if (newrow < 0) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_model_append_values () failed: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		return -1;
+	}
+
+	for (i = 0, list = set_values; list; i++, list = list->next) {
+		const GValue *get_value;
+		
+		if (!list->data)
+			continue;
+		
+		get_value = gda_data_model_get_value_at (model, i, newrow, &error);
+		if (!get_value) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Can't get data model's value: %s",
+				 error && error->message ? error->message : "No detail");
+#endif
+			return FALSE;
+		}
+		if (gda_value_compare (get_value, (GValue*) list->data)) {
+#ifdef CHECK_EXTRA_INFO
+			gchar *s1, *s2;
+			s1 = gda_value_stringify (get_value);
+			s2 = gda_value_stringify ((GValue*) list->data);
+			g_print ("gda_data_model_get_value_at(%d,%d) returned '%s' when it should have returned '%s'\n", i, 
+				 newrow, s1, s2);
+			g_free (s1);
+			g_free (s2);
+#endif
+			return -1;
+		}
+	}
+
+	if (stmt) {
+		/* run the statement and compare it with @model */
+		GdaDataModel *rerun;
+		GdaDataComparator *cmp;
+		GError *error = NULL;
+		rerun = gda_connection_statement_execute_select (cnc, stmt, stmt_params, &error);
+		if (!rerun) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not re-run the SELECT statement: %s\n",
+				 error && error->message ? error->message : "No detail");
+#endif
+			return -1;
+		}
+
+		cmp = (GdaDataComparator*) gda_data_comparator_new (model, rerun);
+		if (! gda_data_comparator_compute_diff (cmp, &error)) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Could not compute the data model differences: %s\n",
+				 error && error->message ? error->message : "No detail");
+#endif
+			newrow = -1;
+		}
+		if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("There are some differences when re-running the SELECT statement...\n");
+#endif
+			newrow = -1;
+		}
+
+		g_object_unref (cmp);
+		g_object_unref (rerun);
+		return newrow;
+	}
+
+	return newrow;
+}
+
+/* 
+ * signals checking 
+ */
+static void
+monitor_model_signals (GdaDataModel *model)
+{
+	g_signal_connect (model, "row-updated", G_CALLBACK (signal_callback), "U");
+	g_signal_connect (model, "row-removed", G_CALLBACK (signal_callback), "D");
+	g_signal_connect (model, "row-inserted", G_CALLBACK (signal_callback), "I");
+}
+
+static void
+signal_callback (GdaDataModel *model, gint row, gchar *type)
+{
+	SigEvent *se;
+	se = g_new0 (SigEvent, 1);
+	se->model = model;
+	se->type = *type;
+	se->row = row;
+	signals = g_slist_append (signals, se);
+#ifdef CHECK_EXTRA_INFO
+	g_print ("Received '%s' signal for row %d from model %p\n",
+		 (*type == 'I') ? "row-inserted" : ((*type == 'D') ? "row-removed" : "row-updated"),
+		 row, model);
+#endif
+}
+
+static void
+clear_signals (void)
+{
+	if (signals) {
+		GSList *list;
+		for (list = signals; list; list = list->next) {
+			SigEvent *se = (SigEvent*) list->data;
+			g_free (se);
+		}
+		g_slist_free (signals);
+		signals = NULL;
+	}
+}
+
+static gboolean
+check_expected_signal (GdaDataModel *model, gchar type, gint row)
+{
+	SigEvent *se = NULL;
+	if (signals)
+		se = (SigEvent*) signals->data;
+	if (se && (se->model == model) && (se->row == row) && (se->type == type)) {
+		g_free (se);
+		signals = g_slist_remove (signals, se);
+		return TRUE;
+	}
+#ifdef CHECK_EXTRA_INFO
+	else {
+		g_print ("Expected signal '%s' for row %d from model %p and got ",
+			 (type == 'I') ? "row-inserted" : ((type == 'D') ? "row-removed" : "row-updated"),
+			 row, model);
+		if (se)
+			g_print ("signal '%s' for row %d from model %p\n",
+				 (se->type == 'I') ? "row-inserted" : ((se->type == 'D') ? "row-removed" : "row-updated"),
+				 se->row, se->model);
+		else
+			g_print ("no signal\n");
+	}
+#endif
+
+	return FALSE;
+}
+
+static gboolean
+check_no_expected_signal (GdaDataModel *model)
+{
+	SigEvent *se = NULL;
+	if (signals)
+		se = (SigEvent*) signals->data;
+	if (se && (se->model == model)) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("No signal expected and got ");
+		if (se)
+			g_print ("signal '%s' for row %d from model %p\n",
+				 (se->type == 'I') ? "row-inserted" : ((se->type == 'D') ? "row-removed" : "row-updated"),
+				 se->row, se->model);
+		else
+			g_print ("no signal\n");
+#endif
+		return FALSE;
+	}
+
+	return TRUE;
+}

Modified: trunk/tests/data-models/check_pmodel.c
==============================================================================
--- trunk/tests/data-models/check_pmodel.c	(original)
+++ trunk/tests/data-models/check_pmodel.c	Sun Sep  7 19:06:38 2008
@@ -34,6 +34,9 @@
 static gboolean check_set_value_at (GdaDataModel *model, gint col, gint row, 
 				    const GValue *set_value, 
 				    GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
+static gboolean check_set_value_at_ext (GdaDataModel *model, gint col, gint row, 
+					const GValue *set_value, 
+					GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params, GError **error);
 static gboolean check_set_values (GdaDataModel *model, gint row, GList *set_values,
 				  GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params);
 static gint check_append_values (GdaDataModel *model, GList *set_values,
@@ -47,6 +50,9 @@
 static gint test5 (GdaConnection *cnc);
 static gint test6 (GdaConnection *cnc);
 static gint test7 (GdaConnection *cnc);
+static gint test8 (GdaConnection *cnc);
+static gint test9 (GdaConnection *cnc);
+static gint test10 (GdaConnection *cnc);
 
 TestFunc tests[] = {
         test1,
@@ -55,7 +61,10 @@
 	test4,
 	test5,
 	test6,
-	test7
+	test7,
+	test8,
+	test9,
+	test10
 };
 
 int
@@ -353,7 +362,7 @@
 }
 
 /*
- * check gda_data_model_set_at()
+ * check gda_data_model_set_value_at()
  * 
  * Returns the number of failures 
  */
@@ -853,6 +862,240 @@
 	return nfailed;
 }
 
+/*
+ * check gda_data_model_set_value_at(), modifies the PK
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test8 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt;
+	gint nfailed = 0;
+	GValue *value;
+
+	clear_signals ();
+
+	/* create GdaDataSelect */
+	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_DATA_SELECT (model)) {
+		g_print ("Data model should be a GdaDataSelect!\n");
+		exit (EXIT_FAILURE);
+	}
+
+	/* test INSERT with undefined params */
+	if (!gda_data_select_compute_modification_statements (GDA_DATA_SELECT (model), &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_select_set_modification_statement() should have succedded, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+
+	/****/
+	monitor_model_signals (model);
+	g_value_set_int ((value = gda_value_new (G_TYPE_INT)), 102);
+	if (! check_set_value_at (model, 0, 4, value, cnc, stmt, NULL)) {
+		nfailed ++;
+		goto out;
+	}
+	if (! check_expected_signal (model, 'U', 4)) {
+		nfailed++;
+		goto out;
+	}
+	gda_value_free (value);
+	clear_signals ();
+
+ out:
+	g_object_unref (model);
+	g_object_unref (stmt);
+
+	return nfailed;
+}
+
+/*
+ * check modifications statements' setting, with external parameter
+ * the modification makes the row 'disappear' from the data model
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test9 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model;
+	GdaStatement *stmt;
+	gint nfailed = 0;
+	GdaSet *params;
+	GValue *value;
+
+	clear_signals ();
+
+	/* create GdaDataSelect */
+	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;
+	}
+	if (! gda_set_set_holder_value (params, &error, "country", "SP")) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not get SELECT's parameters!\n");
+#endif
+		goto out;
+	}
+	model = gda_connection_statement_execute_select (cnc, stmt, params, &error);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not execute SELECT!\n");
+#endif
+		goto out;
+	}
+	if (!GDA_IS_DATA_SELECT (model)) {
+		g_print ("Data model should be a GdaDataSelect!\n");
+		exit (EXIT_FAILURE);
+	}
+
+	
+	/* gda_data_select_compute_modification_statements() */
+	if (!gda_data_select_compute_modification_statements (GDA_DATA_SELECT (model), &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("gda_data_select_compute_modification_statements() should have succedded, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+
+	monitor_model_signals (model);
+	g_value_set_string ((value = gda_value_new (G_TYPE_STRING)), "UK");
+	if (! check_set_value_at_ext (model, 4, 1, value, cnc, stmt, params, &error)) {
+		if (error && (error->domain == 0) && (error->code == -1)) {
+#ifdef CHECK_EXTRA_INFO
+			g_print ("This error was expected (modified row would not have been in the SELECT)\n");
+#endif	
+		}
+		else {
+			nfailed ++;
+			goto out;
+		}
+	}
+	if (! check_expected_signal (model, 'U', 1)) {
+		nfailed++;
+		goto out;
+	}
+	gda_value_free (value);
+	clear_signals ();
+
+ out:	
+	g_object_unref (model);
+	g_object_unref (stmt);
+	g_object_unref (params);
+	
+	return nfailed;
+}
+
+
+/*
+ * Simple data model copy
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test10 (GdaConnection *cnc)
+{
+	GError *error = NULL;
+	GdaDataModel *model, *copy;
+	GdaStatement *stmt;
+	gint nfailed = 0;
+	GdaSet *params;
+
+	clear_signals ();
+
+	/* create GdaDataSelect */
+	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;
+	}
+	if (! gda_set_set_holder_value (params, &error, "country", "SP")) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not get SELECT's parameters!\n");
+#endif
+		goto out;
+	}
+	model = gda_connection_statement_execute_select (cnc, stmt, params, &error);
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not execute SELECT!\n");
+#endif
+		goto out;
+	}
+	if (!GDA_IS_DATA_SELECT (model)) {
+		g_print ("Data model should be a GdaDataSelect!\n");
+		exit (EXIT_FAILURE);
+	}
+
+	copy = (GdaDataModel*) gda_data_model_array_copy_model (model, &error);
+	if (!copy) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not copy GdaDataSelect, error: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+
+	GdaDataComparator *cmp;
+	cmp = (GdaDataComparator*) gda_data_comparator_new (model, copy);
+	if (! gda_data_comparator_compute_diff (cmp, &error)) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not compute the data model differences: %s\n",
+			 error && error->message ? error->message : "No detail");
+#endif
+		goto out;
+	}
+	if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("There are some differences whith the copied data model ...\n");
+#endif
+		gda_data_model_dump (model, stdout);
+		gda_data_model_dump (copy, stdout);
+		goto out;
+	}
+	g_object_unref (cmp);
+	g_object_unref (copy);
+
+ out:	
+	g_object_unref (model);
+	g_object_unref (stmt);
+	g_object_unref (params);
+	
+	return nfailed;
+}
+
 
 /*
  * Checking value function:
@@ -863,22 +1106,34 @@
 check_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *set_value, 
 		    GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params)
 {
-	const GValue *get_value;
 	GError *error = NULL;
+	gboolean retval;
+	retval = check_set_value_at_ext (model, col, row, set_value, cnc, stmt, stmt_params, &error);
+	if (error)
+		g_error_free (error);
+	return retval;
+}
 
-	if (! gda_data_model_set_value_at (model, col, row, set_value, &error)) {
+static gboolean
+check_set_value_at_ext (GdaDataModel *model, gint col, gint row, 
+			const GValue *set_value, 
+			GdaConnection *cnc, GdaStatement *stmt, GdaSet *stmt_params, GError **error)
+{
+	const GValue *get_value;
+
+	if (! gda_data_model_set_value_at (model, col, row, set_value, error)) {
 #ifdef CHECK_EXTRA_INFO
 		g_print ("gda_data_model_set_value_at(%d,%d) failed: %s\n",
 			 col, row, 
-			 error && error->message ? error->message : "No detail");
+			 error && *error && (*error)->message ? (*error)->message : "No detail");
 #endif
 		return FALSE;
 	}
-	get_value = gda_data_model_get_value_at (model, col, row, &error);
+	get_value = gda_data_model_get_value_at (model, col, row, error);
 	if (!get_value) {
 #ifdef CHECK_EXTRA_INFO
-		g_print ("Can't get data model's value: %s",
-			 error && error->message ? error->message : "No detail");
+		g_print ("Can't get data model's value: %s\n",
+			 error && *error && (*error)->message ? (*error)->message : "No detail");
 #endif
 		return FALSE;
 	}
@@ -898,22 +1153,21 @@
 		/* run the statement and compare it with @model */
 		GdaDataModel *rerun;
 		GdaDataComparator *cmp;
-		GError *error = NULL;
 		gboolean cmpres = TRUE;
-		rerun = gda_connection_statement_execute_select (cnc, stmt, stmt_params, &error);
+		rerun = gda_connection_statement_execute_select (cnc, stmt, stmt_params, error);
 		if (!rerun) {
 #ifdef CHECK_EXTRA_INFO
 			g_print ("Could not re-run the SELECT statement: %s\n",
-				 error && error->message ? error->message : "No detail");
+				 error && *error && (*error)->message ? (*error)->message : "No detail");
 #endif
 			return FALSE;
 		}
 
 		cmp = (GdaDataComparator*) gda_data_comparator_new (model, rerun);
-		if (! gda_data_comparator_compute_diff (cmp, &error)) {
+		if (! gda_data_comparator_compute_diff (cmp, error)) {
 #ifdef CHECK_EXTRA_INFO
 			g_print ("Could not compute the data model differences: %s\n",
-				 error && error->message ? error->message : "No detail");
+				 error && *error && (*error)->message ? (*error)->message : "No detail");
 #endif
 			cmpres = FALSE;
 		}
@@ -922,6 +1176,12 @@
 			g_print ("There are some differences when re-running the SELECT statement...\n");
 #endif
 			cmpres = FALSE;
+			gda_data_model_dump (model, stdout);
+			gda_data_model_dump (rerun, stdout);
+			if (error) {
+				g_set_error (error, 0, -1,
+					     "There are some differences when re-running the SELECT statement...");
+			}
 		}
 		g_object_unref (cmp);
 		g_object_unref (rerun);



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