[libgda] Added GdaRow and GdaHolder error reporting APIs



commit 564ac6708ebb04bd8c6ffa8137dcee9412b9be41
Author: Vivien Malerba <malerba gnome-db org>
Date:   Wed Sep 14 23:07:55 2011 +0200

    Added GdaRow and GdaHolder error reporting APIs
    
    gda_row_invalidate_value_e()
    gda_row_value_is_valid_e()
    gda_holder_force_invalid_e()
    gda_holder_is_valid_e()
    gda_data_model_iter_get_value_at_e()

 libgda/gda-data-model-iter.c                     |   66 ++++--
 libgda/gda-data-model-iter.h                     |    1 +
 libgda/gda-data-select.c                         |   39 ++--
 libgda/gda-holder.c                              |   91 ++++++++-
 libgda/gda-holder.h                              |    2 +
 libgda/gda-row.c                                 |   99 +++++++++-
 libgda/gda-row.h                                 |    2 +
 libgda/sql-parser/gda-statement-struct-parts.c   |    9 +-
 libgda/sqlite/gda-sqlite-recordset.c             |   65 ++++--
 libgda/sqlite/gda-sqlite.h                       |    2 +
 libgda/sqlite/virtual/gda-vprovider-data-model.c |   10 +-
 tests/data-models/check_model_errors.c           |  239 +++++++++++++++++++++-
 12 files changed, 536 insertions(+), 89 deletions(-)
---
diff --git a/libgda/gda-data-model-iter.c b/libgda/gda-data-model-iter.c
index 4214ea2..e2da293 100644
--- a/libgda/gda-data-model-iter.c
+++ b/libgda/gda-data-model-iter.c
@@ -667,7 +667,6 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
 	gint col;
 	GdaDataModel *test;
 	gboolean update_model;
-	gboolean retval = TRUE;
 	
 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
 
@@ -692,18 +691,17 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
 	g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
 	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);
+		GError *lerror = NULL;
+		cvalue = gda_data_model_get_value_at (model, col, row, &lerror);
 		if (!cvalue || 
-		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
-			gda_holder_force_invalid ((GdaHolder*) list->data);
-			retval = FALSE;
-		}
+		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, &lerror))
+			gda_holder_force_invalid_e ((GdaHolder*) list->data, lerror);
 		else
 			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 retval;
+	return TRUE;
 }
 
 
@@ -755,7 +753,6 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
 	gint row;
 	GdaDataModel *test;
 	gboolean update_model;
-	gboolean retval = TRUE;
 	
 	/* validity tests */
 	if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
@@ -780,19 +777,18 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
 	g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
 	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);
+		GError *lerror = NULL;
+		cvalue = gda_data_model_get_value_at (model, col, row, &lerror);
 		if (!cvalue || 
-		    !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) {
-			gda_holder_force_invalid ((GdaHolder *) list->data);
-			retval = FALSE;
-		}
+		    !gda_holder_set_value ((GdaHolder *) list->data, cvalue, &lerror))
+			gda_holder_force_invalid_e ((GdaHolder *) list->data, lerror);
 		else
 			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 retval;
+	return TRUE;
 }
 
 /**
@@ -844,7 +840,6 @@ gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *it
 	gint row;
 	GdaDataModel *test;
 	gboolean update_model;
-	gboolean retval = TRUE;
 	
 	/* validity tests */
 	if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
@@ -869,19 +864,18 @@ gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *it
 	g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
 	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);
+		GError *lerror = NULL;
+		cvalue = gda_data_model_get_value_at (model, col, row, &lerror);
 		if (!cvalue || 
-		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
-			gda_holder_force_invalid ((GdaHolder*) list->data);
-			retval = FALSE;
-		}
+		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, &lerror))
+			gda_holder_force_invalid_e ((GdaHolder*) list->data, lerror);
 		else
 			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 retval;
+	return TRUE;
 }
 
 
@@ -1006,6 +1000,36 @@ gda_data_model_iter_get_value_at (GdaDataModelIter *iter, gint col)
 }
 
 /**
+ * gda_data_model_iter_get_value_at:
+ * @iter: a #GdaDataModelIter object
+ * @col: the requested column
+ * @error: (allow-none): a place to store errors, or %NULL
+ *
+ * Get the value stored at the column @col in @iter. The returned value must not be modified.
+ *
+ * Returns: (transfer none): the #GValue, or %NULL if the value could not be fetched
+ *
+ * Since: 4.2.10
+ */
+const GValue *
+gda_data_model_iter_get_value_at_e (GdaDataModelIter *iter, gint col, GError **error)
+{
+	GdaHolder *param;
+
+	g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), NULL);
+	g_return_val_if_fail (iter->priv, NULL);
+
+	param = (GdaHolder *) g_slist_nth_data (((GdaSet *) iter)->holders, col);
+	if (param) {
+		if (error)
+			gda_holder_is_valid_e (param, error);
+		return gda_holder_get_value (param);
+	}
+	else
+		return NULL;
+}
+
+/**
  * gda_data_model_iter_set_value_at:
  * @iter: a #GdaDataModelIter object
  * @col: the column number
diff --git a/libgda/gda-data-model-iter.h b/libgda/gda-data-model-iter.h
index cdf2c89..ff12490 100644
--- a/libgda/gda-data-model-iter.h
+++ b/libgda/gda-data-model-iter.h
@@ -117,6 +117,7 @@ struct _GdaDataModelIterClass
 GType             gda_data_model_iter_get_type             (void) G_GNUC_CONST;
 
 const GValue     *gda_data_model_iter_get_value_at         (GdaDataModelIter *iter, gint col);
+const GValue     *gda_data_model_iter_get_value_at_e       (GdaDataModelIter *iter, gint col, GError **error);
 const GValue     *gda_data_model_iter_get_value_for_field  (GdaDataModelIter *iter, const gchar *field_name);
 gboolean          gda_data_model_iter_set_value_at         (GdaDataModelIter *iter, gint col, 
 							    const GValue *value, GError **error);
diff --git a/libgda/gda-data-select.c b/libgda/gda-data-select.c
index 23cb47c..83cdf72 100644
--- a/libgda/gda-data-select.c
+++ b/libgda/gda-data-select.c
@@ -1939,16 +1939,10 @@ gda_data_select_get_value_at (GdaDataModel *model, gint col, gint row, GError **
 	g_assert (prow);
 
 	GValue *retval = gda_row_get_value (prow, col);
-	if (gda_row_value_is_valid (prow, retval))
+	if (gda_row_value_is_valid_e (prow, retval, error))
 		return retval;
-	else {
-		gchar *str;
-		str = g_strdup_printf (_("Unable to get value for row %d and column %d"), row, col);
-		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
-			     "%s", str);
-		g_free (str);
+	else
 		return NULL;
-	}
 }
 
 static GdaValueAttribute
@@ -2205,22 +2199,24 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 	     plist;
 	     i++, plist = plist->next) {
 		GValue *value;
-		GError *error = NULL;
+		GError *lerror = NULL;
+		gboolean pok = TRUE;
 		value = gda_row_get_value (prow, i);
 
-		if (!gda_row_value_is_valid (prow, value)) {
-			retval = FALSE;
-			g_warning (_("Could not change iter's value for column %d: %s"), i,
-				   error && error->message ? error->message : _("No detail"));
+		if (!gda_row_value_is_valid_e (prow, value, &lerror)) {
+			g_warning (_("%s(%p) [%d] Could not change iter's value for column %d: %s"),
+				   __FUNCTION__, iter, imodel->priv->sh->iter_row, i,
+				   lerror && lerror->message ? lerror->message : _("No detail"));
+			gda_holder_force_invalid_e ((GdaHolder*) plist->data, lerror);
 		}
-		else if (! gda_holder_set_value ((GdaHolder*) plist->data, value, &error)) {
+		else if (! gda_holder_set_value ((GdaHolder*) plist->data, value, &lerror)) {
 			if (gda_holder_get_not_null ((GdaHolder*) plist->data) &&
 			    gda_value_is_null (value)) {
 				gda_holder_set_not_null ((GdaHolder*) plist->data, FALSE);
 				if (! gda_holder_set_value ((GdaHolder*) plist->data, value, NULL)) {
-					retval = FALSE;
+					pok = FALSE;
 					g_warning (_("Could not change iter's value for column %d: %s"), i,
-						   error && error->message ? error->message : _("No detail"));
+						   lerror && lerror->message ? lerror->message : _("No detail"));
 					gda_holder_set_not_null ((GdaHolder*) plist->data, TRUE);
 				}
 				else
@@ -2228,12 +2224,14 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 						     "to be updated"));
 			}
 			else {
-				retval = FALSE;
+				pok = FALSE;
 				g_warning (_("Could not change iter's value for column %d: %s"), i,
-					   error && error->message ? error->message : _("No detail"));
+					   lerror && lerror->message ? lerror->message : _("No detail"));
 			}
-			if (error)
-				g_error_free (error);
+		}
+		if (!pok) {
+			retval = FALSE;
+			gda_holder_force_invalid_e ((GdaHolder*) plist->data, lerror);
 		}
         }
 
@@ -2241,6 +2239,7 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 	if (update_model)
 		g_object_set (G_OBJECT (iter), "update-model", update_model, NULL);
 
+	g_print ("%s(%p) => %d, current-row =>%d advertized_nrows => %d\n", __FUNCTION__, imodel, retval, imodel->priv->sh->iter_row, imodel->advertized_nrows);
 	return retval;
 }
 
diff --git a/libgda/gda-holder.c b/libgda/gda-holder.c
index 14e977d..6784022 100644
--- a/libgda/gda-holder.c
+++ b/libgda/gda-holder.c
@@ -99,6 +99,7 @@ struct _GdaHolderPrivate
 	gulong           simple_bind_notify_signal_id;
 	
 	gboolean         invalid_forced;
+	GError          *invalid_error;
 	gboolean         valid;
 	gboolean         is_freeable;
 
@@ -311,6 +312,7 @@ gda_holder_init (GdaHolder *holder)
 	holder->priv->simple_bind_notify_signal_id = 0;
 
 	holder->priv->invalid_forced = FALSE;
+	holder->priv->invalid_error = NULL;
 	holder->priv->valid = TRUE;
 	holder->priv->default_forced = FALSE;
 	holder->priv->is_freeable = TRUE;
@@ -377,6 +379,8 @@ gda_holder_copy (GdaHolder *orig)
 	if (allok) {
 		/* direct settings */
 		holder->priv->invalid_forced = orig->priv->invalid_forced;
+		if (orig->priv->invalid_error)
+			holder->priv->invalid_error = g_error_copy (orig->priv->invalid_error);
 		holder->priv->valid = orig->priv->valid;
 		holder->priv->is_freeable = TRUE;
 		holder->priv->default_forced = orig->priv->default_forced;	
@@ -528,6 +532,11 @@ gda_holder_dispose (GObject *object)
 			gda_value_free (holder->priv->default_value);
 			holder->priv->default_value = NULL;
 		}
+
+		if (holder->priv->invalid_error) {
+			g_error_free (holder->priv->invalid_error);
+			holder->priv->invalid_error = NULL;
+		}
 	}
 
 	/* parent class */
@@ -973,6 +982,10 @@ real_gda_holder_set_value (GdaHolder *holder, GValue *value, gboolean do_copy, G
 		if (!do_copy && value)
 			gda_value_free (value);
 		holder->priv->invalid_forced = FALSE;
+		if (holder->priv->invalid_error) {
+			g_error_free (holder->priv->invalid_error);
+			holder->priv->invalid_error = NULL;
+		}
 		holder->priv->valid = newvalid;
 		return TRUE;
 	}
@@ -994,6 +1007,10 @@ real_gda_holder_set_value (GdaHolder *holder, GValue *value, gboolean do_copy, G
 
 	/* new valid status */
 	holder->priv->invalid_forced = FALSE;
+	if (holder->priv->invalid_error) {
+		g_error_free (holder->priv->invalid_error);
+		holder->priv->invalid_error = NULL;
+	}
 	holder->priv->valid = newvalid;
 	/* we're setting a non-static value, so be sure to flag is as freeable */
 	holder->priv->is_freeable = TRUE;
@@ -1103,6 +1120,10 @@ real_gda_holder_set_const_value (GdaHolder *holder, const GValue *value,
 	/* end of procedure if the value has not been changed, after calculating the holder's validity */
 	if (!changed) {
 		holder->priv->invalid_forced = FALSE;
+		if (holder->priv->invalid_error) {
+			g_error_free (holder->priv->invalid_error);
+			holder->priv->invalid_error = NULL;
+		}
 		holder->priv->valid = newvalid;
 #ifdef DEBUG_HOLDER		
 		g_print ("Holder is not changed");
@@ -1125,6 +1146,10 @@ real_gda_holder_set_const_value (GdaHolder *holder, const GValue *value,
 
 	/* new valid status */
 	holder->priv->invalid_forced = FALSE;
+	if (holder->priv->invalid_error) {
+		g_error_free (holder->priv->invalid_error);
+		holder->priv->invalid_error = NULL;
+	}
 	holder->priv->valid = newvalid;
 	/* we're setting a static value, so be sure to flag is as unfreeable */
 	holder->priv->is_freeable = FALSE;
@@ -1227,12 +1252,35 @@ void
 gda_holder_force_invalid (GdaHolder *holder)
 {
 	g_return_if_fail (GDA_IS_HOLDER (holder));
+	gda_holder_force_invalid_e (holder, NULL);
+}
+
+/**
+ * gda_holder_force_invalid_e:
+ * @holder: a #GdaHolder object
+ * @error: (allow-none) (transfer full): a #GError explaining why @holder is declared invalid, or %NULL
+ *
+ * Forces a holder to be invalid; to set it valid again, a new value must be assigned
+ * to it using gda_holder_set_value() or gda_holder_take_value().
+ *
+ * @holder's value is set to %NULL.
+ *
+ * Since: 4.2.10
+ */
+void
+gda_holder_force_invalid_e (GdaHolder *holder, GError *error)
+{
+	g_return_if_fail (GDA_IS_HOLDER (holder));
 	g_return_if_fail (holder->priv);
 
 #ifdef GDA_DEBUG_NO
 	g_print ("Holder %p (%s): declare invalid\n", holder, holder->priv->id);
 #endif
 
+	if (holder->priv->invalid_error)
+		g_error_free (holder->priv->invalid_error);
+	holder->priv->invalid_error = error;
+
 	if (holder->priv->invalid_forced)
 		return;
 
@@ -1251,7 +1299,6 @@ gda_holder_force_invalid (GdaHolder *holder)
 		g_signal_emit (holder, gda_holder_signals[CHANGED], 0);
 }
 
-
 /**
  * gda_holder_is_valid:
  * @holder: a #GdaHolder object
@@ -1264,18 +1311,41 @@ gboolean
 gda_holder_is_valid (GdaHolder *holder)
 {
 	g_return_val_if_fail (GDA_IS_HOLDER (holder), FALSE);
+	return gda_holder_is_valid_e (holder, NULL);
+}
+
+/**
+ * gda_holder_is_valid_e:
+ * @holder: a #GdaHolder object
+ * @error: (allow-none): a place to store invalid error, or %NULL
+ *
+ * Get the validity of @holder (that is, of the value held by @holder)
+ *
+ * Returns: TRUE if @holder's value can safely be used
+ *
+ * Since: 4.2.10
+ */
+gboolean
+gda_holder_is_valid_e (GdaHolder *holder, GError **error)
+{
+	g_return_val_if_fail (GDA_IS_HOLDER (holder), FALSE);
 	g_return_val_if_fail (holder->priv, FALSE);
 
 	if (holder->priv->full_bind)
-		return gda_holder_is_valid (holder->priv->full_bind);
+		return gda_holder_is_valid_e (holder->priv->full_bind, error);
 	else {
+		gboolean retval;
 		if (holder->priv->invalid_forced) 
-			return FALSE;
-
-		if (holder->priv->default_forced) 
-			return holder->priv->default_value ? TRUE : FALSE;
-		else 
-			return holder->priv->valid;
+			retval = FALSE;
+		else {
+			if (holder->priv->default_forced) 
+				retval = holder->priv->default_value ? TRUE : FALSE;
+			else 
+				retval = holder->priv->valid;
+		}
+		if (!retval && holder->priv->invalid_error)
+			g_propagate_error (error,  g_error_copy (holder->priv->invalid_error));
+		return retval;
 	}
 }
 
@@ -1301,6 +1371,11 @@ gda_holder_set_value_to_default (GdaHolder *holder)
 	else {
 		holder->priv->default_forced = TRUE;
 		holder->priv->invalid_forced = FALSE;
+		if (holder->priv->invalid_error) {
+			g_error_free (holder->priv->invalid_error);
+			holder->priv->invalid_error = NULL;
+		}
+
 		if (holder->priv->value) {
 			if (holder->priv->is_freeable)
 				gda_value_free (holder->priv->value);
diff --git a/libgda/gda-holder.h b/libgda/gda-holder.h
index ae928d1..70ec03a 100644
--- a/libgda/gda-holder.h
+++ b/libgda/gda-holder.h
@@ -138,7 +138,9 @@ gboolean            gda_holder_set_value_to_default    (GdaHolder *holder);
 gboolean            gda_holder_value_is_default        (GdaHolder *holder);
 
 void                gda_holder_force_invalid           (GdaHolder *holder);
+void                gda_holder_force_invalid_e         (GdaHolder *holder, GError *error);
 gboolean            gda_holder_is_valid                (GdaHolder *holder);
+gboolean            gda_holder_is_valid_e              (GdaHolder *holder, GError **error);
 
 
 void                gda_holder_set_not_null            (GdaHolder *holder, gboolean not_null);
diff --git a/libgda/gda-row.c b/libgda/gda-row.c
index 9fda297..cb1d83d 100644
--- a/libgda/gda-row.c
+++ b/libgda/gda-row.c
@@ -7,7 +7,7 @@
  * Copyright (C) 2003 Xabier Rodríez Calvar <xrcalvar igalia com>
  * Copyright (C) 2004 Paisa  Seeluangsawat <paisa users sf net>
  * Copyright (C) 2004 Szalai Ferenc <szferi einstein ki iif hu>
- * Copyright (C) 2005 - 2010 Vivien Malerba <malerba gnome-db org>
+ * Copyright (C) 2005 - 2011 Vivien Malerba <malerba gnome-db org>
  * Copyright (C) 2006 - 2008 Murray Cumming <murrayc murrayc com>
  * Copyright (C) 2007 Leonardo Boshell <lb kmc com co>
  * Copyright (C) 2010 David King <davidk openismus com>
@@ -30,6 +30,7 @@
 
 #include "gda-row.h"
 #include <string.h>
+#include <glib/gi18n-lib.h>
 #include "gda-data-model.h"
 
 #define PARENT_TYPE G_TYPE_OBJECT
@@ -37,7 +38,8 @@
 struct _GdaRowPrivate {
 	GdaDataModel *model; /* can be NULL */
 
-        GValue       *fields;        /* GValue for each column */
+        GValue       *fields; /* GValues */
+	GError      **errors; /* GError for each invalid value at the same position */
         gint          nfields;
 };
 
@@ -92,6 +94,7 @@ gda_row_init (GdaRow *row, G_GNUC_UNUSED GdaRowClass *klass)
 	row->priv = g_new0 (GdaRowPrivate, 1);
 	row->priv->model = NULL;
 	row->priv->fields = NULL;
+	row->priv->errors = NULL;
 	row->priv->nfields = 0;
 }
 
@@ -115,9 +118,13 @@ gda_row_finalize (GObject *object)
 	if (row->priv) {
 		gint i;
 
-		for (i = 0; i < row->priv->nfields; i++)
+		for (i = 0; i < row->priv->nfields; i++) {
 			gda_value_set_null (&(row->priv->fields [i]));
+			if (row->priv->errors && row->priv->errors [i])
+				g_error_free (row->priv->errors [i]);
+		}
 		g_free (row->priv->fields);
+		g_free (row->priv->errors);
 
 		g_free (row->priv);
 		row->priv = NULL;
@@ -251,8 +258,56 @@ gda_row_get_value (GdaRow *row, gint num)
 void
 gda_row_invalidate_value (G_GNUC_UNUSED GdaRow *row, GValue *value)
 {
+	return gda_row_invalidate_value_e (row, value, NULL);
+}
+
+/**
+ * gda_row_invalidate_value_e:
+ * @row: a #GdaRow
+ * @value: a #GValue belonging to @row (obtained with gda_row_get_value()).
+ * @error: (allow-none) (transfer full): the error which lead to the invalidation
+ * 
+ * Marks @value as being invalid. This method is mainly used by database
+ * providers' implementations to report any error while reading a value from the database.
+ *
+ * Since: 4.2.10
+ */
+void
+gda_row_invalidate_value_e (GdaRow *row, GValue *value, GError *error)
+{
 	gda_value_set_null (value);
 	G_VALUE_TYPE (value) = G_TYPE_INVALID;
+	if (error) {
+		guint i;
+		if (! row->priv->errors)
+			row->priv->errors = g_new0 (GError*, row->priv->nfields);
+		for (i = 0; i < row->priv->nfields; i++) {
+			if (& (row->priv->fields[i]) == value) {
+				if (row->priv->errors [i])
+					g_error_free (row->priv->errors [i]);
+				row->priv->errors [i] = error;
+				break;
+			}
+		}
+		if (i == row->priv->nfields) {
+			g_error_free (error);
+			g_warning (_("Value not found in row!"));
+		}
+	}
+	else if (row->priv->errors) {
+		guint i;
+		for (i = 0; i < row->priv->nfields; i++) {
+			if (& (row->priv->fields[i]) == value) {
+				if (row->priv->errors [i]) {
+					g_error_free (row->priv->errors [i]);
+					row->priv->errors [i] = NULL;
+				}
+				break;
+			}
+		}
+		if (i == row->priv->nfields)
+			g_warning (_("Value not found in row!"));
+	}
 }
 
 /**
@@ -267,9 +322,43 @@ gda_row_invalidate_value (G_GNUC_UNUSED GdaRow *row, GValue *value)
  * Returns: %TRUE if @value is valid
  */
 gboolean
-gda_row_value_is_valid (G_GNUC_UNUSED GdaRow *row, GValue *value)
+gda_row_value_is_valid (GdaRow *row, GValue *value)
 {
-	return (G_VALUE_TYPE (value) == G_TYPE_INVALID) ? FALSE : TRUE;
+	return gda_row_value_is_valid_e (row, value, NULL);
+}
+
+/**
+ * gda_row_value_is_valid:
+ * @row: a #GdaRow.
+ * @value: a #GValue belonging to @row (obtained with gda_row_get_value()).
+ * @error: (allow-none): a place to store the invalid error, or %NULL
+ *
+ * Tells if @value has been marked as being invalid by gda_row_invalidate_value().
+ * This method is mainly used by database
+ * providers' implementations to report any error while reading a value from the database.
+ *
+ * Returns: %TRUE if @value is valid
+ *
+ * Since: 4.2.10
+ */
+gboolean
+gda_row_value_is_valid_e (GdaRow *row, GValue *value, GError **error)
+{
+	gboolean valid;
+	valid = (G_VALUE_TYPE (value) == G_TYPE_INVALID) ? FALSE : TRUE;
+	if (!valid && row->priv->errors && error) {
+		guint i;
+		for (i = 0; i < row->priv->nfields; i++) {
+			if (& (row->priv->fields[i]) == value) {
+				if (row->priv->errors [i])
+					g_propagate_error (error, g_error_copy (row->priv->errors [i]));
+				break;
+			}
+		}
+		if (i == row->priv->nfields)
+			g_warning (_("Value not found in row!"));
+	}
+	return valid;
 }
 
 /**
diff --git a/libgda/gda-row.h b/libgda/gda-row.h
index b293f0b..3e601d6 100644
--- a/libgda/gda-row.h
+++ b/libgda/gda-row.h
@@ -80,7 +80,9 @@ GValue       *gda_row_get_value      (GdaRow *row, gint num);
 
 /* for database providers mainly */
 void          gda_row_invalidate_value (GdaRow *row, GValue *value);
+void          gda_row_invalidate_value_e (GdaRow *row, GValue *value, GError *error);
 gboolean      gda_row_value_is_valid (GdaRow *row, GValue *value);
+gboolean      gda_row_value_is_valid_e (GdaRow *row, GValue *value, GError **error);
 
 G_END_DECLS
 
diff --git a/libgda/sql-parser/gda-statement-struct-parts.c b/libgda/sql-parser/gda-statement-struct-parts.c
index 11c9f52..a2bd13b 100644
--- a/libgda/sql-parser/gda-statement-struct-parts.c
+++ b/libgda/sql-parser/gda-statement-struct-parts.c
@@ -1131,10 +1131,15 @@ void
 gda_sql_select_field_take_expr (GdaSqlSelectField *field, GdaSqlExpr *expr)
 {
 	field->expr = expr;
+	g_assert (GDA_SQL_ANY_PART (expr)->type == GDA_SQL_ANY_EXPR);
 	gda_sql_any_part_set_parent (field->expr, field);
 
-	if (expr && expr->value)
-		_split_identifier_string (g_value_dup_string (expr->value), &(field->table_name), &(field->field_name));
+	if (expr && expr->value) {
+		const gchar *dup;
+		dup = g_value_get_string (expr->value);
+		if (dup && *dup)
+			_split_identifier_string (g_strdup (dup), &(field->table_name), &(field->field_name));
+	}
 }
 
 /**
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index 8e6f539..c07945c 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -59,6 +59,7 @@ struct _GdaSqliteRecordsetPrivate {
 	GdaRow       *tmp_row; /* used in cursor mode */
 };
 static GObjectClass *parent_class = NULL;
+GHashTable *error_blobs_hash = NULL;
 
 /*
  * Object init and finalize
@@ -88,6 +89,9 @@ gda_sqlite_recordset_class_init (GdaSqliteRecordsetClass *klass)
 	pmodel_class->fetch_next = gda_sqlite_recordset_fetch_next;
 	pmodel_class->fetch_prev = NULL;
 	pmodel_class->fetch_at = NULL;
+
+	g_assert (!error_blobs_hash);
+	error_blobs_hash = g_hash_table_new (NULL, NULL);
 }
 
 static void
@@ -333,7 +337,6 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 	switch (rc) {
 	case  SQLITE_ROW: {
 		gint col, real_col;
-		
 		prow = gda_row_new (_GDA_PSTMT (ps)->ncols);
 		for (col = 0; col < _GDA_PSTMT (ps)->ncols; col++) {
 			GValue *value;
@@ -377,7 +380,14 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 			
 			/* fill GValue */
 			value = gda_row_get_value (prow, col);
-			if (SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, real_col) == NULL) {
+			GError *may_error;
+			may_error = (GError*) SQLITE3_CALL (sqlite3_column_blob) (ps->sqlite_stmt, real_col);
+			if (may_error && g_hash_table_lookup (error_blobs_hash, may_error)) {
+				g_print ("[[[%s]]]\n", may_error->message);
+				gda_row_invalidate_value_e (prow, value, may_error);
+				g_hash_table_remove (error_blobs_hash, may_error);
+			}
+			else if (SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, real_col) == NULL) {
 				/* we have a NULL value */
 				gda_value_set_null (value);
 			}
@@ -390,10 +400,11 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					gint64 i;
 					i = SQLITE3_CALL (sqlite3_column_int64) (ps->sqlite_stmt, real_col);
 					if ((i > G_MAXINT) || (i < G_MININT)) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     "%s", _("Integer value is too big"));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						g_value_set_int (value, (gint) i);
@@ -402,10 +413,11 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					guint64 i;
 					i = (gint64) SQLITE3_CALL (sqlite3_column_int64) (ps->sqlite_stmt, real_col);
 					if (i > G_MAXUINT) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     "%s", _("Integer value is too big"));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						g_value_set_uint (value, (gint) i);
@@ -467,10 +479,11 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 									      rowid);
 					}
 					if (!bop) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     "%s", _("Unable to open BLOB"));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else {
 						GdaBlob *blob;
@@ -487,11 +500,12 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					if (!gda_parse_iso8601_date (&date, 
 								     (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, 
 												    real_col))) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     _("Invalid date '%s' (date format should be YYYY-MM-DD)"), 
 							     (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, real_col));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						g_value_set_boxed (value, &date);
@@ -501,11 +515,12 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					if (!gda_parse_iso8601_time (&timegda, 
 								     (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, 
 												    real_col))) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     _("Invalid time '%s' (time format should be HH:MM:SS[.ms])"), 
 							     (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, real_col));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						gda_value_set_time (value, &timegda);
@@ -515,11 +530,12 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					if (!gda_parse_iso8601_timestamp (&timestamp, 
 									  (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt,
 													 real_col))) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     _("Invalid timestamp '%s' (format should be YYYY-MM-DD HH:MM:SS[.ms])"), 
 							     (gchar *) SQLITE3_CALL (sqlite3_column_text) (ps->sqlite_stmt, real_col));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						gda_value_set_timestamp (value, &timestamp);
@@ -528,10 +544,11 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					gint64 i;
 					i = SQLITE3_CALL (sqlite3_column_int64) (ps->sqlite_stmt, real_col);
 					if ((i > G_MAXINT8) || (i < G_MININT8)) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     "%s", _("Integer value is too big"));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						g_value_set_char (value, (gchar) i);
@@ -540,17 +557,23 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
 					guint64 i;
 					i = (gint64) SQLITE3_CALL (sqlite3_column_int64) (ps->sqlite_stmt, real_col);
 					if (i > G_MAXUINT8) {
-						g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+						GError *lerror = NULL;
+						g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
 							     GDA_SERVER_PROVIDER_DATA_ERROR,
 							     "%s", _("Integer value is too big"));
-						gda_row_invalidate_value (prow, value);
+						gda_row_invalidate_value_e (prow, value, lerror);
 					}
 					else
 						g_value_set_uchar (value, (guchar) i);
 				}
-				else 
-					g_error ("Unhandled GDA type %s in SQLite recordset", 
-						 gda_g_type_to_string (_GDA_PSTMT (ps)->types [col]));
+				else {
+					GError *lerror = NULL;
+					g_set_error (&lerror, GDA_SERVER_PROVIDER_ERROR,
+						     GDA_SERVER_PROVIDER_DATA_ERROR,
+						     "Unhandled type '%s' in SQLite recordset",
+						     gda_g_type_to_string (_GDA_PSTMT (ps)->types [col]));
+					gda_row_invalidate_value_e (prow, value, lerror);
+				}
 			}
 		}
 		
diff --git a/libgda/sqlite/gda-sqlite.h b/libgda/sqlite/gda-sqlite.h
index 3605faf..5bf569d 100644
--- a/libgda/sqlite/gda-sqlite.h
+++ b/libgda/sqlite/gda-sqlite.h
@@ -62,4 +62,6 @@ typedef struct {
 	GType        *types_array;/* holds GType values, pointed by @types_hash */
 } SqliteConnectionData;
 
+extern GHashTable *error_blobs_hash;
+
 #endif
diff --git a/libgda/sqlite/virtual/gda-vprovider-data-model.c b/libgda/sqlite/virtual/gda-vprovider-data-model.c
index 26ef901..80ea89a 100644
--- a/libgda/sqlite/virtual/gda-vprovider-data-model.c
+++ b/libgda/sqlite/virtual/gda-vprovider-data-model.c
@@ -691,14 +691,18 @@ virtualColumn (sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i)
 
 	param = gda_data_model_iter_get_holder_for_field (cursor->iter, i);
 	if (!param) {
-		SQLITE3_CALL (sqlite3_result_error) (ctx, _("Column not found"), -1);
+		SQLITE3_CALL (sqlite3_result_text) (ctx, _("Column not found"), -1, SQLITE_TRANSIENT);
 		return SQLITE_EMPTY;
 	}
 	else {
 		const GValue *value;
+		GError *lerror = NULL;
 		value = gda_holder_get_value (param);
-
-		if (!value || gda_value_is_null (value))
+		if (! gda_holder_is_valid_e (param, &lerror)) {
+			g_hash_table_insert (error_blobs_hash, lerror, GINT_TO_POINTER (1));
+			SQLITE3_CALL (sqlite3_result_blob) (ctx, lerror, sizeof (GError), NULL);
+		}
+		else if (!value || gda_value_is_null (value))
 			SQLITE3_CALL (sqlite3_result_null) (ctx);
 		else  if (G_VALUE_TYPE (value) == G_TYPE_INT) 
 			SQLITE3_CALL (sqlite3_result_int) (ctx, g_value_get_int (value));
diff --git a/tests/data-models/check_model_errors.c b/tests/data-models/check_model_errors.c
index d19e138..e879ea2 100644
--- a/tests/data-models/check_model_errors.c
+++ b/tests/data-models/check_model_errors.c
@@ -25,6 +25,7 @@
 #include <glib/gstdio.h>
 #include "../test-cnc-utils.h"
 #include "../data-model-errors.h"
+#include <virtual/libgda-virtual.h>
 
 #define fail(x) g_warning (x)
 #define fail_if(x,y) if (x) g_warning (y)
@@ -36,9 +37,11 @@ static void dump_data_model (GdaDataModel *model);
 
 typedef gboolean (*TestFunc) (GdaConnection *);
 static gint test1 (GdaConnection *cnc);
+static gint test2 (GdaConnection *cnc);
 
 TestFunc tests[] = {
 	test1,
+	test2
 };
 
 int
@@ -93,24 +96,66 @@ check_iter_contents (GdaDataModel *model, GdaDataModelIter *iter)
 	for (i = 0; i < gda_data_model_get_n_columns (model); i++) {
 		GdaHolder *holder;
 		const GValue *vi, *vm;
+		GError *lerror1 = NULL, *lerror2 = NULL;
 
 		holder = gda_set_get_nth_holder (GDA_SET (iter), i);
-		if (gda_holder_is_valid (holder))
+		if (gda_holder_is_valid_e (holder, &lerror1))
 			vi = gda_holder_get_value (holder);
 		else
 			vi = NULL;
-		vm = gda_data_model_get_value_at (model, i, row, NULL);
-		if ((vi == vm) ||
-		    (vi && vm && !gda_value_differ (vi, vm)))
-			return TRUE;
-		else {
+		vm = gda_data_model_get_value_at (model, i, row, &lerror2);
+		if (vi == vm) {
+			if (!vi) {
+				if (! lerror1 || !lerror1->message) {
+#ifdef CHECK_EXTRA_INFO
+					g_print ("Iterator reported invalid value without any error set\n");
+#endif
+					return FALSE;
+				}
+				else if (! lerror2 || !lerror2->message) {
+#ifdef CHECK_EXTRA_INFO
+					g_print ("Data model reported invalid value without any error set\n");
+#endif
+					return FALSE;
+				}
+				else if (lerror1->code != lerror2->code) {
+#ifdef CHECK_EXTRA_INFO
+					g_print ("Error codes differ!\n");
+#endif
+					return FALSE;
+				}
+				else if (lerror1->domain != lerror2->domain) {
+#ifdef CHECK_EXTRA_INFO
+					g_print ("Error domains differ!\n");
+#endif
+					return FALSE;
+				}
+				else if (strcmp (lerror1->message, lerror2->message)) {
+#ifdef CHECK_EXTRA_INFO
+					g_print ("Error messages differ:\n\t%s\t%s",
+						 lerror1->message, lerror2->message);
+#endif
+					return FALSE;
+				}
+			}
+			else if (gda_value_differ (vi, vm)) {
+#ifdef CHECK_EXTRA_INFO
+				g_print ("Iter's contents at (%d,%d) is wrong: expected [%s], got [%s]\n",
+					 i, row, 
+					 vm ? gda_value_stringify (vm) : "ERROR",
+					 vi ? gda_value_stringify (vi) : "ERROR");
+#endif
+				return FALSE;
+			}
+		}
+		else if (gda_value_differ (vi, vm)) {
 #ifdef CHECK_EXTRA_INFO
 			g_print ("Iter's contents at (%d,%d) is wrong: expected [%s], got [%s]\n",
 				 i, row, 
 				 vm ? gda_value_stringify (vm) : "ERROR",
 				 vi ? gda_value_stringify (vi) : "ERROR");
 #endif
-			return FALSE;
+			return FALSE;	
 		}
 	}
 	return TRUE;
@@ -160,13 +205,14 @@ test1 (GdaConnection *cnc)
 			goto out;
 		}
 	}
+	g_print ("Now at row %d\n", gda_data_model_iter_get_row (iter));
 
 	/* iterate backward */
 	i--;
-	if (gda_data_model_iter_move_to_row (iter, i)) {
+	if (!gda_data_model_iter_move_to_row (iter, i)) {
 		nfailed++;
 #ifdef CHECK_EXTRA_INFO
-		g_print ("Error iterating: move to last row (%d) should have reported an error\n", i);
+		g_print ("Error iterating: move to last row (%d) failed\n", i);
 #endif
 		goto out;
 	}
@@ -198,6 +244,7 @@ test1 (GdaConnection *cnc)
 			goto out;
 		}
 	}
+	g_print ("Now at row %d\n", gda_data_model_iter_get_row (iter));
 
  out:
 	g_object_unref (model);
@@ -260,3 +307,177 @@ compare_data_models (GdaDataModel *model1, GdaDataModel *model2, GError **error)
         return FALSE;
 }
 */
+
+/*
+ * check modifications statements' setting
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test2 (GdaConnection *cnc)
+{
+#define TABLE_NAME "data"
+	GdaDataModel *model;
+	gint nfailed = 0;
+
+	model = data_model_errors_new ();
+	if (!model) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Could not create DataModelErrors!\n");
+#endif
+		goto out;
+	}
+	dump_data_model (model);
+
+	GdaVirtualProvider *virtual_provider;
+	GError *lerror = NULL;
+	GdaConnection *vcnc;
+	GdaDataModelIter *iter = NULL;
+	GdaStatement *stmt;
+	virtual_provider = gda_vprovider_data_model_new ();
+	vcnc = gda_virtual_connection_open (virtual_provider, &lerror);
+	if (!vcnc) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Virtual ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+#endif
+		nfailed++;
+		g_clear_error (&lerror);
+		goto out;
+	}
+
+	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (vcnc), model,
+						   TABLE_NAME, &lerror)) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Add model as table ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+#endif
+		nfailed++;
+		g_clear_error (&lerror);
+		goto out;
+	}
+	g_object_unref (model);
+
+	/* Execute SELECT */
+	stmt = gda_connection_parse_sql_string (vcnc, "SELECT * FROM " TABLE_NAME, NULL, NULL);
+	g_assert (stmt);
+	model = gda_connection_statement_execute_select (vcnc, stmt, NULL, &lerror);
+	g_object_unref (stmt);
+	if (!model) {
+		#ifdef CHECK_EXTRA_INFO
+		g_print ("SELECT ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+#endif
+		nfailed++;
+		g_clear_error (&lerror);
+		goto out;
+	}
+
+	/* iterate forward */
+	gint i, row;
+	iter = gda_data_model_create_iter (model);
+	for (i = 0, gda_data_model_iter_move_next (iter);
+	     gda_data_model_iter_is_valid (iter);
+	     i++, gda_data_model_iter_move_next (iter)) {
+		row = gda_data_model_iter_get_row (iter);
+		if (i != row) {
+			nfailed++;
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Error iterating: expected to be at row %d and reported row is %d!\n",
+				 i, row);
+#endif
+			goto out;
+		}
+		g_print ("Now at row %d\n", i);
+
+		if (! check_iter_contents (model, iter)) {
+			nfailed++;
+			goto out;
+		}
+	}
+	if (i != 4) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Error iterating: expected to have read %d rows, and read %d\n", 3,
+			 i - 1);
+		nfailed++;
+		goto out;
+#endif
+	}
+	if (gda_data_model_iter_get_row (iter) != -1) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Error iterating: expected to be at row -1 and reported row is %d!\n",
+			 gda_data_model_iter_get_row (iter));
+#endif
+		nfailed++;
+		goto out;
+	}
+
+	/* bind as another table */
+	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (vcnc), model,
+						   TABLE_NAME "2", &lerror)) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Add model as table2 ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+#endif
+		nfailed++;
+		g_clear_error (&lerror);
+		goto out;
+	}
+	g_object_unref (model);
+
+	stmt = gda_connection_parse_sql_string (vcnc, "SELECT * FROM " TABLE_NAME "2", NULL, NULL);
+	g_assert (stmt);
+	model = gda_connection_statement_execute_select (vcnc, stmt, NULL, &lerror);
+	g_object_unref (stmt);
+	if (!model) {
+		#ifdef CHECK_EXTRA_INFO
+		g_print ("SELECT ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
+#endif
+		nfailed++;
+		g_clear_error (&lerror);
+		goto out;
+	}
+	
+	/* iterate forward */
+	iter = gda_data_model_create_iter (model);
+	for (i = 0, gda_data_model_iter_move_next (iter);
+	     gda_data_model_iter_is_valid (iter);
+	     i++, gda_data_model_iter_move_next (iter)) {
+		row = gda_data_model_iter_get_row (iter);
+		if (i != row) {
+			nfailed++;
+#ifdef CHECK_EXTRA_INFO
+			g_print ("Error iterating: expected to be at row %d and reported row is %d!\n",
+				 i, row);
+#endif
+			goto out;
+		}
+		g_print ("Now at row %d\n", i);
+
+		if (! check_iter_contents (model, iter)) {
+			nfailed++;
+			goto out;
+		}
+	}
+	if (i != 4) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Error iterating: expected to have read %d rows, and read %d\n", 3,
+			 i - 1);
+		nfailed++;
+		goto out;
+#endif
+	}
+	if (gda_data_model_iter_get_row (iter) != -1) {
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Error iterating: expected to be at row -1 and reported row is %d!\n",
+			 gda_data_model_iter_get_row (iter));
+#endif
+		nfailed++;
+		goto out;
+	}
+
+ out:
+	g_object_unref (iter);
+	g_object_unref (model);
+	g_object_unref (vcnc);
+	g_object_unref (virtual_provider);
+	
+	return nfailed;
+}



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