[libgda] Cleaned GdaDataModelIter implementations



commit 751c7027d653716bfafdb9295691b78ad50cb93f
Author: Vivien Malerba <malerba gnome-db org>
Date:   Wed Sep 14 23:09:43 2011 +0200

    Cleaned GdaDataModelIter implementations

 doc/C/prov-writing-recordsets.xml                  |   19 ++-
 libgda/gda-data-model-import.c                     |   38 ++----
 libgda/gda-data-model-iter.c                       |  152 +++++++++++---------
 libgda/gda-data-select.c                           |  127 +++++++----------
 libgda/sqlite/gda-sqlite-recordset.c               |   37 ++---
 providers/firebird/gda-firebird-recordset.c        |   73 +++++-----
 providers/jdbc/gda-jdbc-recordset.c                |   39 +++---
 providers/ldap/gdaprov-data-model-ldap.c           |    6 +-
 providers/mysql/gda-mysql-recordset.c              |   96 +++++--------
 providers/oracle/gda-oracle-recordset.c            |  129 +++--------------
 providers/postgres/gda-postgres-recordset.c        |   35 +----
 .../skel-implementation/capi/gda-capi-recordset.c  |   74 +++++-----
 12 files changed, 332 insertions(+), 493 deletions(-)
---
diff --git a/doc/C/prov-writing-recordsets.xml b/doc/C/prov-writing-recordsets.xml
index 04bbd40..6231779 100644
--- a/doc/C/prov-writing-recordsets.xml
+++ b/doc/C/prov-writing-recordsets.xml
@@ -53,8 +53,11 @@
     <title>fetch_random()</title>
     <para>
       This method is called when the user calls <link linkend="gda-data-model-get-value-at">gda_data_model_get_value_at ()</link>,
-      and in available only when the data access mode for the data model is random (that is not cursor based). When data
-      access is cursor based, this method will not be called.
+      and is used only when the data access mode for the data model is random (that is not cursor based) (i.e. when data
+      access is cursor based, this method will not be called).
+    </para>
+    <para>Also it is safe to assume that when called, the
+    <parameter>prow</parameter> parameter will not be NULL, and that <parameter>*prow</parameter> is actually NULL.
     </para>
   </sect2>
 
@@ -75,6 +78,9 @@
       <parameter>rownum</parameter> is an indication of what the row number will be once the next row has been fetched (it can 
       safely be discarded if that information is not necessary).
     </para>
+    <para>This method is not used when data is accessed in a random way. Also it is safe to assume that when called, the
+    <parameter>prow</parameter> parameter will not be NULL, and that <parameter>*prow</parameter> is actually NULL.
+    </para>
   </sect2>
 
   <sect2>
@@ -82,14 +88,19 @@
     <para>
       This method is called when data access is cursor based and a data model iterator is moved one position backward. The
       <parameter>rownum</parameter> is an indication of what the row number will be once the previous row has been fetched (it can 
-      safely be discarded if that information is not necessary).
+      safely be discarded if that information is not necessary). Implementing this method is not necessary if the data model
+      does not support moving iterators backward.
+    </para>
+    <para>This method is not used when data is accessed in a random way. Also it is safe to assume that when called, the
+    <parameter>prow</parameter> parameter will not be NULL, and that <parameter>*prow</parameter> is actually NULL.
     </para>
   </sect2>
 
   <sect2>
     <title>fetch_at()</title>
     <para>
-      This method can be implemented when data access is cursor based and there is a shorter way of getting to a
+      This method can be implemented (but it is not required) when data access is cursor based
+      and there is a shorter way of getting to a
       specific row than having to call the fetch_next() or fetch_prev() methods several times.
     </para>
   </sect2>
diff --git a/libgda/gda-data-model-import.c b/libgda/gda-data-model-import.c
index 68f5b77..127f6a6 100644
--- a/libgda/gda-data-model-import.c
+++ b/libgda/gda-data-model-import.c
@@ -1932,8 +1932,10 @@ gda_data_model_import_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		break;
 
 	case FORMAT_CSV:
-		if (! imodel->priv->extract.csv.rows_read)
+		if (! imodel->priv->extract.csv.rows_read) {
+			gda_data_model_iter_invalidate_contents (iter);
 			return FALSE;
+		}
 
 		if (gda_data_model_iter_is_valid (iter) &&
 		    (imodel->priv->extract.csv.rows_read->len > 0)) {
@@ -1962,7 +1964,6 @@ gda_data_model_import_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		GSList *plist;
 		GSList *vlist;
 		gboolean update_model;
-		gboolean allok = TRUE;
 
 		g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
 		g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
@@ -1971,18 +1972,10 @@ gda_data_model_import_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		     plist = plist->next, vlist = vlist->next) {
 			GError *lerror = NULL;
 			if (! gda_holder_set_value (GDA_HOLDER (plist->data),
-						    (GValue *) vlist->data, &lerror)) {
-				gchar *tmp;
-				tmp = g_strdup_printf (_("Could not set iterator's value: %s"),
-						       lerror && lerror->message ? lerror->message : _("No detail"));
-				add_error (imodel, tmp);
-				g_free (tmp);
-				allok = FALSE;
-			}
+						    (GValue *) vlist->data, &lerror))
+				gda_holder_force_invalid_e (GDA_HOLDER (plist->data), lerror);
 		}
 		if (plist || vlist) {
-			if (imodel->priv->strict)
-				allok = FALSE;
 			if (plist) {
 				add_error_too_few_values (imodel);
 				for (; plist; plist = plist->next) {
@@ -2003,11 +1996,12 @@ gda_data_model_import_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		g_object_set (G_OBJECT (iter), "current-row", imodel->priv->iter_row,
 			      "update-model", update_model, NULL);
 
-		return allok;
+		return TRUE;
 	}
 	else {
-		g_signal_emit_by_name (iter, "end-of-data");
+		gda_data_model_iter_invalidate_contents (iter);
 		g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+		g_signal_emit_by_name (iter, "end-of-data");
 		return FALSE;
 	}
 }
@@ -2040,7 +2034,6 @@ gda_data_model_import_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
 		GSList *plist;
 		GSList *vlist;
 		gboolean update_model;
-		gboolean allok = TRUE;
 
 		g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
 		g_object_set (G_OBJECT (iter), "update-model", FALSE, NULL);
@@ -2049,18 +2042,10 @@ gda_data_model_import_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
 		     plist = plist->next, vlist = vlist->next) {
 			GError *lerror = NULL;
 			if (! gda_holder_set_value (GDA_HOLDER (plist->data),
-						    (GValue *) vlist->data, &lerror)) {
-				gchar *tmp;
-				tmp = g_strdup_printf (_("Could not set iterator's value: %s"),
-						       lerror && lerror->message ? lerror->message : _("No detail"));
-				add_error (imodel, tmp);
-				g_free (tmp);
-				allok = FALSE;
-			}
+						    (GValue *) vlist->data, &lerror))
+				gda_holder_force_invalid_e (GDA_HOLDER (plist->data), lerror);
 		}
 		if (plist || vlist) {
-			if (imodel->priv->strict)
-				allok = FALSE;
 			if (plist) {
 				add_error_too_few_values (imodel);
 				for (; plist; plist = plist->next) {
@@ -2080,9 +2065,10 @@ gda_data_model_import_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
 		g_assert (imodel->priv->iter_row >= 0);
 		g_object_set (G_OBJECT (iter), "current-row", imodel->priv->iter_row,
 			      "update-model", update_model, NULL);
-		return allok;
+		return TRUE;
 	}
 	else {
+		gda_data_model_iter_invalidate_contents (iter);
 		g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
 		return FALSE;
 	}
diff --git a/libgda/gda-data-model-iter.c b/libgda/gda-data-model-iter.c
index e2da293..087a635 100644
--- a/libgda/gda-data-model-iter.c
+++ b/libgda/gda-data-model-iter.c
@@ -577,14 +577,18 @@ gda_data_model_iter_get_property (GObject *object,
  *
  * Synchronizes the values of the parameters in @iter with the values at the @row row.
  *
- * If @row is not a valid row, then the returned value is FALSE, and the "current-row"
- * property is set to -1 (which means that gda_data_model_iter_is_valid() would return FALSE)
+ * If @row is not a valid row, then the returned value is %FALSE, and the "current-row"
+ * property is set to -1 (which means that gda_data_model_iter_is_valid() would return %FALSE),
+ * with the exception that if @row is -1, then the returned value is %TRUE.
  *
- * If any other error occurred then the returned value is FALSE, but the "current-row"
- * property is set to the @row row.  In this case
- * each #GdaHolder composing @iter for which an error occurred will be invalid (see gda_holder_is_valid()).
+ * This function can return %FALSE if it was not allowed to be moved (as it emits the
+ * "validate-set" signal before being moved).
  *
- * Returns: TRUE if no error occurred
+ * When this function returns %TRUE, then @iter has actually been moved to the next row,
+ * but some values may not have been read correctly in the row, in which case the
+ * correcsponding #GdaHolder will be left invalid.
+ *
+ * Returns: %TRUE if no error occurred
  */
 gboolean
 gda_data_model_iter_move_to_row (GdaDataModelIter *iter, gint row)
@@ -592,8 +596,15 @@ gda_data_model_iter_move_to_row (GdaDataModelIter *iter, gint row)
 	g_return_val_if_fail (GDA_IS_DATA_MODEL_ITER (iter), FALSE);
 	g_return_val_if_fail (iter->priv, FALSE);
 
+	if ((gda_data_model_iter_get_row (iter) >= 0) &&
+	    (gda_data_model_iter_get_row (iter) == row))
+		return TRUE; /* nothing to do! */
+
 	if (row < 0) {
-		if (iter->priv->row != -1) {
+		if (gda_data_model_iter_get_row (iter) >= 0) {
+			if (! _gda_set_validate ((GdaSet*) iter, NULL))
+				return FALSE;
+
 			iter->priv->row = -1;
 			g_signal_emit (G_OBJECT (iter),
 				       gda_data_model_iter_signals[ROW_CHANGED],
@@ -603,44 +614,18 @@ gda_data_model_iter_move_to_row (GdaDataModelIter *iter, gint row)
 	}
 	else {
 		GdaDataModel *model;
-		if ((gda_data_model_iter_get_row (iter) >= 0) &&
-		    (gda_data_model_iter_get_row (iter) != row) && 
-		    ! _gda_set_validate ((GdaSet*) iter, NULL)) {
-			return FALSE;
-		}
-
-		gboolean *null_oks = NULL;
-		if (GDA_SET (iter)->holders) {
-			gint i;
-			GSList *list;
-
-			null_oks = g_new (gboolean, g_slist_length (GDA_SET (iter)->holders));
-			for (i = 0, list = GDA_SET (iter)->holders; list; i++, list = list->next) {
-				null_oks[i] = gda_holder_get_not_null ((GdaHolder*) list->data);
-				gda_holder_set_not_null ((GdaHolder*) list->data, FALSE);
-			}
-		}
-
-		gboolean move_ok;
 		model = iter->priv->data_model;
 		g_return_val_if_fail (iter->priv->data_model, FALSE);
 
-		if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row)
-			move_ok = (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) (model, iter, row);
-		else {
-			/* default method */
-			move_ok = gda_data_model_iter_move_to_row_default (model, iter, row);
+		if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) {
+			if ((gda_data_model_iter_get_row (iter) >= 0) &&
+			    ! _gda_set_validate ((GdaSet*) iter, NULL))
+				return FALSE;
+			return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) (model, iter,
+										  row);
 		}
-		
-		if (null_oks) {
-			gint i;
-			GSList *list;
-			for (i = 0, list = GDA_SET (iter)->holders; list; i++, list = list->next)
-				gda_holder_set_not_null ((GdaHolder*) list->data, null_oks[i]);
-			g_free (null_oks);
-		}
-		
-		return move_ok;
+		else
+			return gda_data_model_iter_move_to_row_default (model, iter, row);
 	}
 }
 
@@ -658,6 +643,13 @@ set_param_attributes (GdaHolder *holder, GdaValueAttribute flags)
 
 /**
  * gda_data_model_iter_move_to_row_default: (skip)
+ * @model: a #GdaDataModel
+ * @iter: a #GdaDataModelIter iterating in @model
+ * @row: the requested row
+ * 
+ * Method reserved to #GdaDataModelIter implementations, should not be called directly.
+ *
+ * Returns: %TRUE if @iter was moved correctly.
  */
 gboolean
 gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *iter, gint row)
@@ -668,7 +660,9 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
 	GdaDataModel *test;
 	gboolean update_model;
 	
-	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), FALSE);
+	if ((gda_data_model_iter_get_row (iter) >= 0) &&
+	    ! _gda_set_validate ((GdaSet*) iter, NULL))
+		return FALSE;
 
 	if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
 		return FALSE;
@@ -713,14 +707,17 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
  * (synchronizes the values of the parameters in @iter with the values at the new row).
  *
  * 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 (which means that gda_data_model_iter_is_valid() would return FALSE)
+ * anymore, and the returned value is %FALSE; note also that the "current-row" property
+ * is set to -1 (which means that gda_data_model_iter_is_valid() would return %FALSE)
+ *
+ * This function can return %FALSE if it was not allowed to be moved (as it emits the
+ * "validate-set" signal before being moved).
  *
- * 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). In this case
- * each #GdaHolder composing @iter for which an error occurred will be invalid (see gda_holder_is_valid()).
+ * When this function returns %TRUE, then @iter has actually been moved to the next row,
+ * but some values may not have been read correctly in the row, in which case the
+ * correcsponding #GdaHolder will be left invalid.
  *
- * Returns: TRUE if the iterator is now at the next row
+ * Returns: %TRUE if the iterator is now at the next row
  */
 gboolean
 gda_data_model_iter_move_next (GdaDataModelIter *iter)
@@ -730,13 +727,13 @@ gda_data_model_iter_move_next (GdaDataModelIter *iter)
 	g_return_val_if_fail (iter->priv, FALSE);
 	g_return_val_if_fail (iter->priv->data_model, FALSE);
 
-	if ((gda_data_model_iter_get_row (iter) >= 0) &&
-	    ! _gda_set_validate ((GdaSet*) iter, NULL))
-		return FALSE;
-
 	model = iter->priv->data_model;
-	if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next)
+	if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next) {
+		if ((gda_data_model_iter_get_row (iter) >= 0) &&
+		    ! _gda_set_validate ((GdaSet*) iter, NULL))
+			return FALSE;
 		return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_next) (model, iter);
+	}
 	else 
 		/* default method */
 		return gda_data_model_iter_move_next_default (model, iter);
@@ -744,6 +741,12 @@ gda_data_model_iter_move_next (GdaDataModelIter *iter)
 
 /**
  * gda_data_model_iter_move_next_default: (skip)
+ * @model: a #GdaDataModel
+ * @iter: a #GdaDataModelIter iterating in @model
+ * 
+ * Method reserved to #GdaDataModelIter implementations, should not be called directly.
+ *
+ * Returns: %TRUE if @iter was moved correctly.
  */
 gboolean
 gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *iter)
@@ -753,8 +756,11 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
 	gint row;
 	GdaDataModel *test;
 	gboolean update_model;
-	
-	/* validity tests */
+
+	if ((gda_data_model_iter_get_row (iter) >= 0) &&
+	    ! _gda_set_validate ((GdaSet*) iter, NULL))
+		return FALSE;
+
 	if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
 		return FALSE;
 	
@@ -799,14 +805,17 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
  * with the values at the new row).
  *
  * 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; note also that the "current-row" property
- * is set to -1 (which means that gda_data_model_iter_is_valid() would return FALSE).
+ * anymore, and the returned value is %FALSE; note also that the "current-row" property
+ * is set to -1 (which means that gda_data_model_iter_is_valid() would return %FALSE).
+ *
+ * This function can return %FALSE if it was not allowed to be moved (as it emits the
+ * "validate-set" signal before being moved).
  *
- * 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).  In this case
- * each #GdaHolder composing @iter for which an error occurred will be invalid (see gda_holder_is_valid()).
+ * When this function returns %TRUE, then @iter has actually been moved to the next row,
+ * but some values may not have been read correctly in the row, in which case the
+ * correcsponding #GdaHolder will be left invalid.
  *
- * Returns: TRUE if the iterator is now at the previous row
+ * Returns: %TRUE if the iterator is now at the previous row
  */
 gboolean
 gda_data_model_iter_move_prev (GdaDataModelIter *iter)
@@ -817,13 +826,13 @@ gda_data_model_iter_move_prev (GdaDataModelIter *iter)
 	g_return_val_if_fail (iter->priv, FALSE);
 	g_return_val_if_fail (iter->priv->data_model, FALSE);
 
-	if ((gda_data_model_iter_get_row (iter) >= 0) &&
-	    ! _gda_set_validate ((GdaSet*) iter, NULL))
-		return FALSE;
-
 	model = iter->priv->data_model;
-	if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev)
+	if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev) {
+		if ((gda_data_model_iter_get_row (iter) >= 0) &&
+		    ! _gda_set_validate ((GdaSet*) iter, NULL))
+			return FALSE;
 		return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_prev) (model, iter);
+	}
 	else 
 		/* default method */
 		return gda_data_model_iter_move_prev_default (model, iter);
@@ -831,6 +840,12 @@ gda_data_model_iter_move_prev (GdaDataModelIter *iter)
 
 /**
  * gda_data_model_iter_move_prev_default: (skip)
+ * @model: a #GdaDataModel
+ * @iter: a #GdaDataModelIter iterating in @model
+ * 
+ * Method reserved to #GdaDataModelIter implementations, should not be called directly.
+ *
+ * Returns: %TRUE if @iter was moved correctly.
  */
 gboolean
 gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *iter)
@@ -841,7 +856,10 @@ gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *it
 	GdaDataModel *test;
 	gboolean update_model;
 	
-	/* validity tests */
+	if ((gda_data_model_iter_get_row (iter) >= 0) &&
+	    ! _gda_set_validate ((GdaSet*) iter, NULL))
+		return FALSE;
+
 	if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM))
 		return FALSE;
 	
diff --git a/libgda/gda-data-select.c b/libgda/gda-data-select.c
index 83cdf72..f45f5b9 100644
--- a/libgda/gda-data-select.c
+++ b/libgda/gda-data-select.c
@@ -1923,20 +1923,12 @@ gda_data_select_get_value_at (GdaDataModel *model, gint col, gint row, GError **
 			prow = dstmt->row;
 	}
 	else {
-		gint *ptr;
-		irow = int_row;
-		ptr = g_hash_table_lookup (imodel->priv->sh->index, &irow);
-		if (!ptr) {
-			prow = NULL;
-			if (CLASS (model)->fetch_random &&
-			    !CLASS (model)->fetch_random (imodel, &prow, int_row, error))
-				return NULL;
-		}
-		else
-			prow = g_array_index (imodel->priv->sh->rows, GdaRow *, *ptr);
+		prow = gda_data_select_get_stored_row (imodel, int_row);
+		if (!prow && CLASS (model)->fetch_random)
+			CLASS (model)->fetch_random (imodel, &prow, int_row, error);
 	}
-
-	g_assert (prow);
+	if (!prow)
+		return NULL;
 
 	GValue *retval = gda_row_get_value (prow, col);
 	if (gda_row_value_is_valid_e (prow, retval, error))
@@ -2012,7 +2004,7 @@ gda_data_select_create_iter (GdaDataModel *model)
 	}
 }
 
-static gboolean update_iter (GdaDataSelect *imodel, GdaRow *prow);
+static void update_iter (GdaDataSelect *imodel, GdaRow *prow);
 static gboolean
 gda_data_select_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 {
@@ -2028,37 +2020,33 @@ gda_data_select_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		return gda_data_model_iter_move_next_default (model, iter);
 
 	g_return_val_if_fail (CLASS (model)->fetch_next, FALSE);
-
 	g_return_val_if_fail (iter, FALSE);
         g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
 
-	if (imodel->priv->sh->iter_row == G_MAXINT)
+	if (imodel->priv->sh->iter_row == G_MAXINT) {
+		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
+	}
 	else if (imodel->priv->sh->iter_row == G_MININT)
 		target_iter_row = 0;
 	else
 		target_iter_row = imodel->priv->sh->iter_row + 1;
-	int_row = external_to_internal_row (imodel, target_iter_row, NULL);
 
-	gint *ptr;
-	irow = int_row;
-	ptr = g_hash_table_lookup (imodel->priv->sh->index, &irow);
-	if (ptr)
-		prow = g_array_index (imodel->priv->sh->rows, GdaRow *, *ptr);
-	else if (!CLASS (model)->fetch_next (imodel, &prow, int_row, NULL)) {
-		/* an error occurred */
-		g_object_set (G_OBJECT (iter), "current-row", target_iter_row, NULL);
-		return FALSE;
-	}
+	int_row = external_to_internal_row (imodel, target_iter_row, NULL);
+	prow = gda_data_select_get_stored_row (model, int_row);
+	if (!prow)
+		CLASS (model)->fetch_next (imodel, &prow, int_row, NULL);
 
 	if (prow) {
 		imodel->priv->sh->iter_row = target_iter_row;
-                return update_iter (imodel, prow);
+                update_iter (imodel, prow);
+		return TRUE;
 	}
 	else {
-		g_signal_emit_by_name (iter, "end-of-data");
-                g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+		gda_data_model_iter_invalidate_contents (iter);
                 imodel->priv->sh->iter_row = G_MAXINT;
+                g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+		g_signal_emit_by_name (iter, "end-of-data");
                 return FALSE;
 	}
 }
@@ -2077,8 +2065,10 @@ gda_data_select_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
 	if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
 		return gda_data_model_iter_move_prev_default (model, iter);
 
-	if (! CLASS (model)->fetch_prev)
+	if (! CLASS (model)->fetch_prev) {
+		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
+	}
 
         g_return_val_if_fail (iter, FALSE);
         g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
@@ -2093,26 +2083,21 @@ gda_data_select_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
         else
                 target_iter_row = imodel->priv->sh->iter_row - 1;
 
-	gint *ptr;
 	int_row = external_to_internal_row (imodel, target_iter_row, NULL);
-	irow = int_row;
-	ptr = g_hash_table_lookup (imodel->priv->sh->index, &irow);
-	if (ptr)
-		prow = g_array_index (imodel->priv->sh->rows, GdaRow *, *ptr);
-	else if (!CLASS (model)->fetch_prev (imodel, &prow, int_row, NULL)) {
-		/* an error occurred */
-		g_object_set (G_OBJECT (iter), "current-row", target_iter_row, NULL);
-		return FALSE;
-	}
+	prow = gda_data_select_get_stored_row (model, int_row);
+	if (!prow)
+		CLASS (model)->fetch_prev (imodel, &prow, int_row, NULL);
 
 	if (prow) {
 		imodel->priv->sh->iter_row = target_iter_row;
-                return update_iter (imodel, prow);
+                update_iter (imodel, prow);
+		return TRUE;
 	}
 
  prev_error:
         g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
         imodel->priv->sh->iter_row = G_MININT;
+	gda_data_model_iter_invalidate_contents (iter);
         return FALSE;
 }
 
@@ -2126,39 +2111,34 @@ gda_data_select_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint r
 	imodel = (GdaDataSelect *) model;
 	g_return_val_if_fail (imodel->priv, FALSE);
 
-	int_row = external_to_internal_row (imodel, row, NULL);
 	if (imodel->priv->sh->usage_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
 		return gda_data_model_iter_move_to_row_default (model, iter, row);
 
         g_return_val_if_fail (iter, FALSE);
         g_return_val_if_fail (imodel->priv->iter == iter, FALSE);
 
-	irow = int_row;
-	ptr = g_hash_table_lookup (imodel->priv->sh->index, &irow);
-	if (ptr)
-		prow = g_array_index (imodel->priv->sh->rows, GdaRow *, *ptr);
-
-	if (CLASS (model)->fetch_at) {
-		if (!CLASS (model)->fetch_at (imodel, &prow, int_row, NULL)) {
-			/* an error occurred */
-			g_object_set (G_OBJECT (iter), "current-row", row, NULL);
-			return FALSE;
-		}
+	int_row = external_to_internal_row (imodel, row, NULL);
+	prow = gda_data_select_get_stored_row (model, int_row);
 
-		if (prow) {
-			imodel->priv->sh->iter_row = row;
-			return update_iter (imodel, prow);
-		}
-		else {
-			g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
-			imodel->priv->sh->iter_row = G_MININT;
-			return FALSE;
-		}
+	if (prow) {
+		imodel->priv->sh->iter_row = row;
+		update_iter (imodel, prow);
+		return TRUE;
 	}
 	else {
-		if (prow) {
-			imodel->priv->sh->iter_row = row;
-			return update_iter (imodel, prow);
+		if (CLASS (model)->fetch_at) {
+			CLASS (model)->fetch_at (imodel, &prow, int_row, NULL);
+			if (prow) {
+				imodel->priv->sh->iter_row = row;
+				update_iter (imodel, prow);
+				return TRUE;
+			}
+			else {
+				g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
+				imodel->priv->sh->iter_row = G_MININT;
+				gda_data_model_iter_invalidate_contents (iter);
+				return FALSE;
+			}
 		}
 		else {
 			/* implementation of fetch_at() is optional */
@@ -2182,14 +2162,13 @@ gda_data_select_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint r
 	}
 }
 
-static gboolean
+static void
 update_iter (GdaDataSelect *imodel, GdaRow *prow)
 {
         gint i;
 	GdaDataModelIter *iter = imodel->priv->iter;
 	GSList *plist;
 	gboolean update_model;
-	gboolean retval = TRUE;
 
 	g_object_get (G_OBJECT (iter), "update-model", &update_model, NULL);
 	if (update_model)
@@ -2200,7 +2179,6 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 	     i++, plist = plist->next) {
 		GValue *value;
 		GError *lerror = NULL;
-		gboolean pok = TRUE;
 		value = gda_row_get_value (prow, i);
 
 		if (!gda_row_value_is_valid_e (prow, value, &lerror)) {
@@ -2214,7 +2192,7 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 			    gda_value_is_null (value)) {
 				gda_holder_set_not_null ((GdaHolder*) plist->data, FALSE);
 				if (! gda_holder_set_value ((GdaHolder*) plist->data, value, NULL)) {
-					pok = FALSE;
+					gda_holder_force_invalid_e ((GdaHolder*) plist->data, lerror);
 					g_warning (_("Could not change iter's value for column %d: %s"), i,
 						   lerror && lerror->message ? lerror->message : _("No detail"));
 					gda_holder_set_not_null ((GdaHolder*) plist->data, TRUE);
@@ -2224,23 +2202,18 @@ update_iter (GdaDataSelect *imodel, GdaRow *prow)
 						     "to be updated"));
 			}
 			else {
-				pok = FALSE;
+				gda_holder_force_invalid_e ((GdaHolder*) plist->data, lerror);
 				g_warning (_("Could not change iter's value for column %d: %s"), i,
 					   lerror && lerror->message ? lerror->message : _("No detail"));
 			}
 		}
-		if (!pok) {
-			retval = FALSE;
-			gda_holder_force_invalid_e ((GdaHolder*) plist->data, lerror);
-		}
         }
 
 	g_object_set (G_OBJECT (iter), "current-row", imodel->priv->sh->iter_row, NULL);
 	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;
+	g_print ("%s(%p), current-row =>%d advertized_nrows => %d\n", __FUNCTION__, imodel, imodel->priv->sh->iter_row, imodel->advertized_nrows);
 }
 
 /*
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index c07945c..5fa4b35 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -641,35 +641,24 @@ gda_sqlite_recordset_fetch_nb_rows (GdaDataSelect *model)
 /*
  * Create a new filled #GdaRow object for the row at position @rownum.
  *
- * Each new #GdaRow created is "given" to the #GdaDataSelect implementation using gda_data_select_take_row ().
+ * Each new #GdaRow created needs to be "given" to the #GdaDataSelect implementation using
+ * gda_data_select_take_row() because backward iterating is not supported.
  */
 static gboolean
 gda_sqlite_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
 	GdaSqliteRecordset *imodel;
 
-	if (*prow)
-		return TRUE;
-
 	imodel = GDA_SQLITE_RECORDSET (model);
-	for (; imodel->priv->next_row_num <= rownum; ) {
-		*prow = fetch_next_sqlite_row (imodel, TRUE, error);
-		if (!*prow) {
-			/*if (GDA_DATA_SELECT (model)->advertized_nrows >= 0), it's not an error */
-			if ((GDA_DATA_SELECT (model)->advertized_nrows >= 0) && 
-			    (imodel->priv->next_row_num < rownum)) {
-				g_set_error (error, 0,
-					     GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
-					     _("Row %d not found"), rownum);
-			}
-			return FALSE;
-		}
-	}
-	if (! *prow) {
-		*prow = gda_data_select_get_stored_row (model, rownum);
-		if (!*prow)
-			return FALSE;
+	if (imodel->priv->next_row_num >= rownum) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, 
+			     "%s", _("Requested row could not be found"));
+		return TRUE;
 	}
+	for (*prow = fetch_next_sqlite_row (imodel, TRUE, error);
+	     *prow && (imodel->priv->next_row_num < rownum);
+	     *prow = fetch_next_sqlite_row (imodel, TRUE, error));
 
 	return TRUE;
 }
@@ -678,16 +667,16 @@ gda_sqlite_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint row
  * Create a new filled #GdaRow object for the next cursor row
  *
  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
- * never keeps a reference to it). Before a new #GdaRow gets created, the previous one, if set, is discarded.
+ * never keeps a reference to it).
+ * Before a new #GdaRow gets created, the previous one, if set, is discarded.
  */
 static gboolean
 gda_sqlite_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, G_GNUC_UNUSED gint rownum, GError **error)
 {
 	GdaSqliteRecordset *imodel = (GdaSqliteRecordset*) model;
 
-	if (imodel->priv->tmp_row) 
+	if (imodel->priv->tmp_row)
 		g_object_unref (imodel->priv->tmp_row);
-
 	*prow = fetch_next_sqlite_row (imodel, FALSE, error);
 	imodel->priv->tmp_row = *prow;
 
diff --git a/providers/firebird/gda-firebird-recordset.c b/providers/firebird/gda-firebird-recordset.c
index cfd8745..9ccce03 100644
--- a/providers/firebird/gda-firebird-recordset.c
+++ b/providers/firebird/gda-firebird-recordset.c
@@ -241,17 +241,15 @@ gda_firebird_recordset_fetch_nb_rows (GdaDataSelect *model)
 /*
  * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
- * using that method, then this method should detect when all the data model rows have been analyzed
- * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
- * as it won't be used anymore to fetch rows.
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is MANDATORY if the data model supports random access
+ * - this method is only called when data model is used in random access mode
  */
 static gboolean 
 gda_firebird_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
@@ -288,14 +286,15 @@ gda_firebird_recordset_store_all (GdaDataSelect *model, GError **error)
 /*
  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is MANDATORY
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_firebird_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
@@ -310,14 +309,16 @@ gda_firebird_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint row
 /*
  * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is OPTIONAL (in this case the data model is assumed not to
+ *   support moving iterators backward)
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_firebird_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
@@ -332,14 +333,16 @@ gda_firebird_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint row
 /*
  * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is OPTIONAL and usefull only if there is a method quicker
+ *   than iterating one step at a time to the correct position.
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_firebird_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
diff --git a/providers/jdbc/gda-jdbc-recordset.c b/providers/jdbc/gda-jdbc-recordset.c
index bb0722d..ceb7760 100644
--- a/providers/jdbc/gda-jdbc-recordset.c
+++ b/providers/jdbc/gda-jdbc-recordset.c
@@ -413,11 +413,13 @@ fetch_next_jdbc_row (GdaJdbcRecordset *model, JNIEnv *jenv, gboolean do_store, G
 	prow = gda_row_new (_GDA_PSTMT (ps)->ncols);
 
 	jexec_res = jni_wrapper_method_call (jenv, GdaJResultSet__fillNextRow,
-					     model->priv->rs_value, &error_code, &sql_state, &lerror, (jlong)GPOINTER_TO_INT(prow));
+					     model->priv->rs_value, &error_code, &sql_state, &lerror,
+					     (jlong)GPOINTER_TO_INT(prow));
 	if (!jexec_res) {
 		if (error && lerror)
 			*error = g_error_copy (lerror);
 		_gda_jdbc_make_error (model->priv->cnc, error_code, sql_state, lerror);
+		g_object_unref ((GObject*) prow);
 		return NULL;
 	}
 
@@ -425,6 +427,7 @@ fetch_next_jdbc_row (GdaJdbcRecordset *model, JNIEnv *jenv, gboolean do_store, G
 	gda_value_free (jexec_res);
 	if (! row_found) {
 		GDA_DATA_SELECT (model)->advertized_nrows = model->priv->next_row_num;
+		g_object_unref ((GObject*) prow);
 		return NULL;
 	}
 
@@ -468,7 +471,8 @@ gda_jdbc_recordset_fetch_nb_rows (GdaDataSelect *model)
 /*
  * Create a new filled #GdaRow object for the row at position @rownum.
  *
- * Each new #GdaRow created is "given" to the #GdaDataSelect implementation using gda_data_select_take_row ().
+ * Each new #GdaRow created is "given" to the #GdaDataSelect implementation
+ * using gda_data_select_take_row ().
  */
 static gboolean 
 gda_jdbc_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
@@ -479,23 +483,18 @@ gda_jdbc_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownu
 
 	jenv = _gda_jdbc_get_jenv (&jni_detach, NULL);
 	if (!jenv)
-		return FALSE;
+		return TRUE;
 
 	imodel = GDA_JDBC_RECORDSET (model);
-        for (; imodel->priv->next_row_num <= rownum; ) {
-                *prow = fetch_next_jdbc_row (imodel, jenv, TRUE, error);
-                if (!*prow) {
-                        /*if (GDA_DATA_SELECT (model)->advertized_nrows >= 0), it's not an error */
-                        if ((GDA_DATA_SELECT (model)->advertized_nrows >= 0) &&
-                            (imodel->priv->next_row_num < rownum)) {
-                                g_set_error (error, 0,
-                                             GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
-                                             _("Row %d not found"), rownum);
-                        }
-			_gda_jdbc_release_jenv (jni_detach);
-                        return FALSE;
-                }
-        }
+	if (imodel->priv->next_row_num >= rownum) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, 
+			     "%s", _("Requested row could not be found"));
+		return TRUE;
+	}
+	for (*prow = fetch_next_jdbc_row (imodel, jenv, TRUE, error);
+	     *prow && (imodel->priv->next_row_num < rownum);
+	     *prow = fetch_next_jdbc_row (imodel, jenv, TRUE, error));
 
 	_gda_jdbc_release_jenv (jni_detach);
         return TRUE;
@@ -505,7 +504,8 @@ gda_jdbc_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownu
  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
  *
  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
- * never keeps a reference to it). Before a new #GdaRow gets created, the previous one, if set, is discarded.
+ * never keeps a reference to it). Before a new #GdaRow gets created, the previous one,
+ * if set, is discarded.
  */
 static gboolean 
 gda_jdbc_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, G_GNUC_UNUSED gint rownum, GError **error)
@@ -518,9 +518,6 @@ gda_jdbc_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, G_GNUC_UNUSE
 	if (!jenv)
 		return FALSE;
 
-	if (*prow)
-                return TRUE;
-
 	if (imodel->priv->tmp_row)
                 g_object_unref (imodel->priv->tmp_row);
 
diff --git a/providers/ldap/gdaprov-data-model-ldap.c b/providers/ldap/gdaprov-data-model-ldap.c
index 4c6f8a3..a4ff290 100644
--- a/providers/ldap/gdaprov-data-model-ldap.c
+++ b/providers/ldap/gdaprov-data-model-ldap.c
@@ -1045,12 +1045,14 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 
 	if (! imodel->priv->cnc) {
 		/* error */
+		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
 	}
 
 	cdata = (LdapConnectionData*) gda_virtual_connection_internal_get_provider_data (GDA_VIRTUAL_CONNECTION (imodel->priv->cnc));
 	if (!cdata) {
 		/* error */
+		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
 	}
 
@@ -1071,6 +1073,7 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 		cpart = imodel->priv->current_exec;
 		if (! cpart->ldap_msg) {
 			/* error somewhere */
+			gda_data_model_iter_invalidate_contents (iter);
 			return FALSE;
 		}
 
@@ -1101,7 +1104,7 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 
 	if (!imodel->priv->current_exec) {
 		/* execution is over */
-		g_signal_emit_by_name (iter, "end-of-data");
+		gda_data_model_iter_invalidate_contents (iter);
                 g_object_set (G_OBJECT (iter), "current-row", -1, NULL);
 		if (imodel->priv->truncated) {
 			GError *e;
@@ -1110,6 +1113,7 @@ gda_data_model_ldap_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 				     _("Truncated result because LDAP server limit encountered"));
 			add_exception (imodel, e);
 		}
+		g_signal_emit_by_name (iter, "end-of-data");
                 return FALSE;
 	}
 
diff --git a/providers/mysql/gda-mysql-recordset.c b/providers/mysql/gda-mysql-recordset.c
index 3211dae..4b86109 100644
--- a/providers/mysql/gda-mysql-recordset.c
+++ b/providers/mysql/gda-mysql-recordset.c
@@ -1004,17 +1004,7 @@ new_row_from_mysql_stmt (GdaMysqlRecordset *imodel, G_GNUC_UNUSED gint rownum, G
 /*
  * Create a new filled #GdaRow object for the row at position @rownum, and put it into *row.
  *
- * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
- *  -  If *row is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *row contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
- * using that method, then this method should detect when all the data model rows have been analyzed
- * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
- * as it won't be used anymore to fetch rows.
+ * Each new GdaRow is given to @model using gda_data_select_take_row().
  */
 static gboolean 
 gda_mysql_recordset_fetch_random (GdaDataSelect  *model,
@@ -1026,12 +1016,9 @@ gda_mysql_recordset_fetch_random (GdaDataSelect  *model,
 
 	imodel = GDA_MYSQL_RECORDSET (model);
 
-	if (*row)
-		return TRUE;
-	
 	*row = new_row_from_mysql_stmt (imodel, rownum, error);
 	if (!*row)
-		return FALSE;
+		return TRUE;
 
 	gda_data_select_take_row (model, *row, rownum);
 	
@@ -1047,14 +1034,9 @@ gda_mysql_recordset_fetch_random (GdaDataSelect  *model,
 /*
  * Create a new filled #GdaRow object for the next cursor row, and put it into *row.
  *
- * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
- *  -  If *row is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *row contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
+ * never keeps a reference to it).
+ * Before a new #GdaRow gets created, the previous one, if set, is discarded.
  */
 static gboolean 
 gda_mysql_recordset_fetch_next (GdaDataSelect  *model,
@@ -1064,43 +1046,33 @@ gda_mysql_recordset_fetch_next (GdaDataSelect  *model,
 {
 	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
 
-	// TO_IMPLEMENT;
-
-	// gda_data_select_iter_next () increments rownum
-
-	if (imodel->priv->tmp_row != NULL) {
+	if (imodel->priv->tmp_row)
 		g_object_unref (G_OBJECT(imodel->priv->tmp_row));
-		imodel->priv->tmp_row = NULL;
-	}
-	
 	*row = new_row_from_mysql_stmt (imodel, rownum, error);
-
 	imodel->priv->tmp_row = *row;
 
-	return *row ? TRUE : FALSE;
+	return TRUE;
 }
 
 /*
- * Create a new filled #GdaRow object for the previous cursor row, and put it into *row.
- *
- * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
- *  -  If *row is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *row contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
+ * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
  *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
+ * never keeps a reference to it).
+ * Before a new #GdaRow gets created, the previous one, if set, is discarded.
  */
 static gboolean 
-gda_mysql_recordset_fetch_prev (G_GNUC_UNUSED GdaDataSelect  *model,
-				G_GNUC_UNUSED GdaRow        **row,
-				G_GNUC_UNUSED gint            rownum,
-				G_GNUC_UNUSED GError        **error)
+gda_mysql_recordset_fetch_prev (GdaDataSelect  *model,
+				GdaRow        **row,
+				gint            rownum,
+				GError        **error)
 {
-	/*GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;*/
+	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
 
-	TO_IMPLEMENT;
+	if (imodel->priv->tmp_row)
+		g_object_unref (G_OBJECT(imodel->priv->tmp_row));
+	*row = new_row_from_mysql_stmt (imodel, rownum, error);
+	imodel->priv->tmp_row = *row;
 
 	return TRUE;
 }
@@ -1108,24 +1080,22 @@ gda_mysql_recordset_fetch_prev (G_GNUC_UNUSED GdaDataSelect  *model,
 /*
  * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *row.
  *
- * WARNING: @row will NOT be NULL, but *row may or may not be NULL:
- *  -  If *row is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *row contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
+ * never keeps a reference to it).
+ * Before a new #GdaRow gets created, the previous one, if set, is discarded.
  */
 static gboolean 
-gda_mysql_recordset_fetch_at (G_GNUC_UNUSED GdaDataSelect  *model,
-			      G_GNUC_UNUSED GdaRow        **row,
-			      G_GNUC_UNUSED gint            rownum,
-			      G_GNUC_UNUSED GError        **error)
+gda_mysql_recordset_fetch_at (GdaDataSelect  *model,
+			      GdaRow        **row,
+			      gint            rownum,
+			      GError        **error)
 {
-	/*GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;*/
-	
-	TO_IMPLEMENT;
+	GdaMysqlRecordset *imodel = (GdaMysqlRecordset*) model;
+
+	if (imodel->priv->tmp_row)
+		g_object_unref (G_OBJECT(imodel->priv->tmp_row));
+	*row = new_row_from_mysql_stmt (imodel, rownum, error);
+	imodel->priv->tmp_row = *row;
 
 	return TRUE;
 }
diff --git a/providers/oracle/gda-oracle-recordset.c b/providers/oracle/gda-oracle-recordset.c
index f91b3c0..54f2a8c 100644
--- a/providers/oracle/gda-oracle-recordset.c
+++ b/providers/oracle/gda-oracle-recordset.c
@@ -48,8 +48,6 @@ static void gda_oracle_recordset_dispose   (GObject *object);
 static gint     gda_oracle_recordset_fetch_nb_rows (GdaDataSelect *model);
 static gboolean gda_oracle_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
 static gboolean gda_oracle_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
-static gboolean gda_oracle_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
-static gboolean gda_oracle_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
 
 static GdaRow  *new_row (GdaDataSelect *imodel, GdaConnection *cnc, GdaOraclePStmt *ps);
 
@@ -88,8 +86,8 @@ gda_oracle_recordset_class_init (GdaOracleRecordsetClass *klass)
 	pmodel_class->fetch_random = gda_oracle_recordset_fetch_random;
 
 	pmodel_class->fetch_next = gda_oracle_recordset_fetch_next;
-	pmodel_class->fetch_prev = gda_oracle_recordset_fetch_prev;
-	pmodel_class->fetch_at = gda_oracle_recordset_fetch_at;
+	pmodel_class->fetch_prev = NULL;
+	pmodel_class->fetch_at = NULL;
 }
 
 static void
@@ -505,7 +503,7 @@ gda_oracle_recordset_new (GdaConnection *cnc, GdaOraclePStmt *ps, GdaSet *exec_p
 }
 
 static GdaRow *
-fetch_next_oracle_row (GdaOracleRecordset *model, gboolean do_store, GError **error)
+fetch_next_oracle_row (GdaOracleRecordset *model, G_GNUC_UNUSED gboolean do_store, GError **error)
 {
 	int result;
 	GdaRow *prow = NULL;
@@ -586,17 +584,8 @@ gda_oracle_recordset_fetch_nb_rows (GdaDataSelect *model)
 /*
  * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
- * using that method, then this method should detect when all the data model rows have been analyzed
- * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
- * as it won't be used anymore to fetch rows.
+ * Each new #GdaRow created needs to be "given" to the #GdaDataSelect implementation using
+ * gda_data_select_take_row() because backward iterating is not supported.
  */
 static gboolean 
 gda_oracle_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
@@ -605,120 +594,38 @@ gda_oracle_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint row
 	OracleConnectionData *cdata;
 	GdaConnection *cnc;
 
-	if (*prow)
-                return TRUE;
-
 	imodel = GDA_ORACLE_RECORDSET (model);
 	cnc = gda_data_select_get_connection (model);
 	cdata = (OracleConnectionData*)	gda_connection_internal_get_provider_data (cnc);
 	if (!cdata)
-		return FALSE;
-
-	for (; imodel->priv->next_row_num <= rownum; ) {
-		*prow = fetch_next_oracle_row (imodel, TRUE, error);
-		if (!*prow) {
-			/*if (GDA_DATA_SELECT (model)->advertized_nrows >= 0), it's not an error */
-			if ((GDA_DATA_SELECT (model)->advertized_nrows >= 0) &&
-			    (imodel->priv->next_row_num < rownum)) {
-				g_set_error (error, 0,
-					     GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
-					     _("Row %d not found"), rownum);
-			}
-			return FALSE;
-		}
-	}
-	return TRUE;
-}
+		return TRUE;
 
-/*
- * Create and "give" filled #GdaRow object for all the rows in the model
- */
-static gboolean
-gda_oracle_recordset_store_all (GdaDataSelect *model, GError **error)
-{
-	GdaOracleRecordset *imodel;
-	gint i;
-
-	imodel = GDA_ORACLE_RECORDSET (model);
-
-	/* default implementation */
-	for (i = 0; i < model->advertized_nrows; i++) {
-		GdaRow *prow;
-		if (! gda_oracle_recordset_fetch_random (model, &prow, i, error))
-			return FALSE;
+	if (imodel->priv->next_row_num >= rownum) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_INTERNAL_ERROR, 
+			     "%s", _("Requested row could not be found"));
+		return TRUE;
 	}
+	for (*prow = fetch_next_oracle_row (imodel, TRUE, error);
+	     *prow && (imodel->priv->next_row_num < rownum);
+	     *prow = fetch_next_oracle_row (imodel, TRUE, error));
+
 	return TRUE;
 }
 
 /*
  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * Each new #GdaRow created needs to be "given" to the #GdaDataSelect implementation using
+ * gda_data_select_take_row() because backward iterating is not supported.
  */
 static gboolean 
 gda_oracle_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
-	if (*prow)
-                return TRUE;
-
 	*prow = fetch_next_oracle_row ((GdaOracleRecordset*) model, TRUE, error);
-	return *prow ? TRUE : FALSE;
-}
-
-/*
- * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
- *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
- */
-static gboolean 
-gda_oracle_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
-{
-	OracleConnectionData *cdata;
-	GdaConnection *cnc;
-
-	if (*prow)
-                return TRUE;
-
-	cnc = gda_data_select_get_connection (model);
-	cdata = (OracleConnectionData*)	gda_connection_internal_get_provider_data (cnc);
-	if (!cdata)
-		return FALSE;
-
-	g_warning ("Internal implementation error: non scrollable cursor requested to go backward!\n");
-	return FALSE;
+	return TRUE;
 }
 
-/*
- * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
- *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
- */
-static gboolean 
-gda_oracle_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
-{
-	return gda_oracle_recordset_fetch_random (model, prow, rownum, error);
-}
 
 /* create a new GdaRow from the last read row */
 static GdaRow *
diff --git a/providers/postgres/gda-postgres-recordset.c b/providers/postgres/gda-postgres-recordset.c
index 524b0a3..087048d 100644
--- a/providers/postgres/gda-postgres-recordset.c
+++ b/providers/postgres/gda-postgres-recordset.c
@@ -440,13 +440,10 @@ gda_postgres_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint r
 {
 	GdaPostgresRecordset *imodel = (GdaPostgresRecordset *) model;
 
-	if (*prow)
-		return TRUE;
-
 	if (!imodel->priv->pg_res) {
 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
 			     "%s", _("Internal error"));
-		return FALSE;
+		return TRUE;
 	}
 
 	*prow = new_row_from_pg_res (imodel, rownum, error);
@@ -489,23 +486,19 @@ gda_postgres_recordset_store_all (GdaDataSelect *model, GError **error)
  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
  *
  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
- * never keeps a reference to it). Before a new #GdaRow gets created, the previous one, if set, is discarded.
+ * never keeps a reference to it).
  */
 static gboolean
 gda_postgres_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
 	GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
 
-	if (*prow)
-		return TRUE;
-
 	if (row_is_in_current_pg_res (imodel, rownum)) {
 		if (imodel->priv->tmp_row)
 			set_prow_with_pg_res (imodel, imodel->priv->tmp_row, rownum - imodel->priv->pg_res_inf, error);
 		else
 			imodel->priv->tmp_row = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 		*prow = imodel->priv->tmp_row;
-		return TRUE;
 	}
 	else {
 		gboolean fetch_error = FALSE;
@@ -515,34 +508,28 @@ gda_postgres_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint row
 			else
 				imodel->priv->tmp_row = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 			*prow = imodel->priv->tmp_row;
-			return TRUE;
 		}
-		else
-			return !fetch_error;
 	}
+	return TRUE;
 }
 
 /*
  * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
  *
  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
- * never keeps a reference to it). Before a new #GdaRow gets created, the previous one, if set, is discarded.
+ * never keeps a reference to it).
  */
 static gboolean
 gda_postgres_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
 	GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
 
-	if (*prow)
-		return TRUE;
-
 	if (row_is_in_current_pg_res (imodel, rownum)) {
 		if (imodel->priv->tmp_row)
 			set_prow_with_pg_res (imodel, imodel->priv->tmp_row, rownum - imodel->priv->pg_res_inf, error);
 		else
 			imodel->priv->tmp_row = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 		*prow = imodel->priv->tmp_row;
-		return TRUE;
 	}
 	else {
 		gboolean fetch_error = FALSE;
@@ -552,27 +539,22 @@ gda_postgres_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint row
 			else
 				imodel->priv->tmp_row = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 			*prow = imodel->priv->tmp_row;
-			return TRUE;
 		}
-		else
-			return !fetch_error;
 	}
+	return TRUE;
 }
 
 /*
  * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
  *
  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
- * never keeps a reference to it). Before a new #GdaRow gets created, the previous one, if set, is discarded.
+ * never keeps a reference to it).
  */
 static gboolean
 gda_postgres_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
 {
 	GdaPostgresRecordset *imodel = (GdaPostgresRecordset*) model;
 
-	if (*prow)
-		return TRUE;
-
 	if (imodel->priv->tmp_row) {
 		g_object_unref (imodel->priv->tmp_row);
 		imodel->priv->tmp_row = NULL;
@@ -581,18 +563,15 @@ gda_postgres_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownu
 	if (row_is_in_current_pg_res (imodel, rownum)) {
 		*prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 		imodel->priv->tmp_row = *prow;
-		return TRUE;
 	}
 	else {
 		gboolean fetch_error = FALSE;
 		if (fetch_row_number_chunk (imodel, rownum, &fetch_error, error)) {
 			*prow = new_row_from_pg_res (imodel, rownum - imodel->priv->pg_res_inf, error);
 			imodel->priv->tmp_row = *prow;
-			return TRUE;
 		}
-		else
-			return !fetch_error;
 	}
+	return TRUE;
 }
 
 
diff --git a/providers/skel-implementation/capi/gda-capi-recordset.c b/providers/skel-implementation/capi/gda-capi-recordset.c
index 2334b2b..5079737 100644
--- a/providers/skel-implementation/capi/gda-capi-recordset.c
+++ b/providers/skel-implementation/capi/gda-capi-recordset.c
@@ -247,17 +247,15 @@ gda_capi_recordset_fetch_nb_rows (GdaDataSelect *model)
 /*
  * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row(). If new row objects are "given" to the GdaDataSelect implemantation
- * using that method, then this method should detect when all the data model rows have been analyzed
- * (when model->nb_stored_rows == model->advertized_nrows) and then possibly discard the API handle
- * as it won't be used anymore to fetch rows.
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is MANDATORY if the data model supports random access
+ * - this method is only called when data model is used in random access mode
  */
 static gboolean 
 gda_capi_recordset_fetch_random (GdaDataSelect *model, G_GNUC_UNUSED GdaRow **prow, G_GNUC_UNUSED gint rownum,
@@ -298,14 +296,15 @@ gda_capi_recordset_store_all (GdaDataSelect *model, GError **error)
 /*
  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is MANDATORY
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_capi_recordset_fetch_next (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED GdaRow **prow,
@@ -321,14 +320,16 @@ gda_capi_recordset_fetch_next (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED
 /*
  * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is OPTIONAL (in this case the data model is assumed not to
+ *   support moving iterators backward)
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_capi_recordset_fetch_prev (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED GdaRow **prow,
@@ -344,14 +345,16 @@ gda_capi_recordset_fetch_prev (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED
 /*
  * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
  *
- * WARNING: @prow will NOT be NULL, but *prow may or may not be NULL:
- *  -  If *prow is NULL then a new #GdaRow object has to be created, 
- *  -  and otherwise *prow contains a #GdaRow object which has already been created 
- *     (through a call to this very function), and in this case it should not be modified
- *     but the function may return FALSE if an error occurred.
- *
- * Memory management for that new GdaRow object is left to the implementation, which
- * can use gda_data_select_take_row().
+ * NOTES:
+ * - @prow will NOT be NULL, but *prow WILL be NULL.
+ * - a new #GdaRow object has to be created.
+ * - memory management for that new GdaRow object is left to the implementation, which
+ *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
+ *   this method won't be called anymore for the same @rownum), or may decide to
+ *   keep a cache of GdaRow object and "recycle" them.
+ * - implementing this method is OPTIONAL and usefull only if there is a method quicker
+ *   than iterating one step at a time to the correct position.
+ * - this method is only called when data model is used in cursor access mode
  */
 static gboolean 
 gda_capi_recordset_fetch_at (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED GdaRow **prow,
@@ -363,4 +366,3 @@ gda_capi_recordset_fetch_at (G_GNUC_UNUSED GdaDataSelect *model, G_GNUC_UNUSED G
 
 	return TRUE;
 }
-



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