[libgda/LIBGDA_4.0] Corrected GdaDataModelIter when errors occurred



commit 7f1af9fd1d8d0374bb3bd01f054eefca7746d8e3
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sun Jan 24 17:57:52 2010 +0100

    Corrected GdaDataModelIter when errors occurred
    
    
    the previous behaviour was to invalidate all the
    GdaHolders composing the iterator when an error
    occurred. The corrected behaviour is to invalidate
    only the GdaHolders for which an error occurred. In any
    case the returned value or other behaviours are unchanged.

 libgda/gda-data-model-iter.c           |   73 +++--
 libgda/gda-data-select.c               |    4 -
 libgda/gda-set.c                       |    7 +
 libgda/gda-set.h                       |    4 +
 tests/Makefile.am                      |    4 +-
 tests/data-model-errors.c              |  464 ++++++++++++++++++++++++++++++++
 tests/data-model-errors.h              |   54 ++++
 tests/data-models/Makefile.am          |   12 +-
 tests/data-models/check_model_errors.c |  245 +++++++++++++++++
 9 files changed, 832 insertions(+), 35 deletions(-)
---
diff --git a/libgda/gda-data-model-iter.c b/libgda/gda-data-model-iter.c
index 7f8eab3..567720f 100644
--- a/libgda/gda-data-model-iter.c
+++ b/libgda/gda-data-model-iter.c
@@ -572,7 +572,8 @@ gda_data_model_iter_get_property (GObject *object,
  * property is set to -1 (which means that gda_data_model_iter_is_valid() would return FALSE)
  *
  * If any other error occurred then the returned value is FALSE, but the "current-row"
- * property is set to the @row row.
+ * property is set to the @row row.  In this case
+ * each #GdaHolder composing @iter for which an error occurred will invalid (see gda_holder_is_valid()).
  *
  * Returns: TRUE if no error occurred
  */
@@ -595,9 +596,23 @@ gda_data_model_iter_move_to_row (GdaDataModelIter *iter, gint row)
 		GdaDataModel *model;
 		if ((gda_data_model_iter_get_row (iter) >= 0) &&
 		    (gda_data_model_iter_get_row (iter) != row) && 
-		    ! gda_set_is_valid ((GdaSet*) iter, NULL))
+		    ! _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;
 		if (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row)
 			return (GDA_DATA_MODEL_GET_CLASS (model)->i_iter_at_row) (model, iter, row);
@@ -631,6 +646,7 @@ 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);
 
@@ -658,16 +674,15 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
 		if (!cvalue || 
 		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
-			g_object_set (G_OBJECT (iter), "current-row", row, 
-				      "update-model", update_model, NULL);
-			gda_data_model_iter_invalidate_contents (iter);
-			return FALSE;
+			gda_holder_force_invalid ((GdaHolder*) list->data);
+			retval = FALSE;
 		}
-		set_param_attributes ((GdaHolder*) list->data, 
-				      gda_data_model_get_attributes_at (model, col, row));
+		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 TRUE;
+	return retval;
 }
 
 
@@ -683,7 +698,8 @@ gda_data_model_iter_move_to_row_default (GdaDataModel *model, GdaDataModelIter *
  * is set to -1 (which means that gda_data_model_iter_is_valid() would return FALSE)
  *
  * 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).
+ * 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 invalid (see gda_holder_is_valid()).
  *
  * Returns: TRUE if the iterator is now at the next row
  */
@@ -695,7 +711,7 @@ gda_data_model_iter_move_next (GdaDataModelIter *iter)
 	g_return_val_if_fail (iter->priv, FALSE);
 
 	if ((gda_data_model_iter_get_row (iter) >= 0) &&
-	    ! gda_set_is_valid ((GdaSet*) iter, NULL))
+	    ! _gda_set_validate ((GdaSet*) iter, NULL))
 		return FALSE;
 
 	model = iter->priv->data_model;
@@ -717,6 +733,7 @@ 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))
@@ -744,17 +761,16 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
 		if (!cvalue || 
 		    !gda_holder_set_value ((GdaHolder *) list->data, cvalue, NULL)) {
-			g_object_set (G_OBJECT (iter), "current-row", row, 
-				      "update-model", update_model, NULL);
-			gda_data_model_iter_invalidate_contents (iter);
-			return FALSE;
+			gda_holder_force_invalid ((GdaHolder *) list->data);
+			retval = FALSE;
 		}
-		set_param_attributes ((GdaHolder *) list->data, 
-				      gda_data_model_get_attributes_at (model, col, row));
+		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 TRUE;
+	return retval;
 }
 
 /**
@@ -769,7 +785,8 @@ gda_data_model_iter_move_next_default (GdaDataModel *model, GdaDataModelIter *it
  * is set to -1 (which means that gda_data_model_iter_is_valid() would return FALSE).
  *
  * 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).
+ * 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 invalid (see gda_holder_is_valid()).
  *
  * Returns: TRUE if the iterator is now at the previous row
  */
@@ -782,7 +799,7 @@ gda_data_model_iter_move_prev (GdaDataModelIter *iter)
 	g_return_val_if_fail (iter->priv, FALSE);
 
 	if ((gda_data_model_iter_get_row (iter) >= 0) &&
-	    ! gda_set_is_valid ((GdaSet*) iter, NULL))
+	    ! _gda_set_validate ((GdaSet*) iter, NULL))
 		return FALSE;
 
 	model = iter->priv->data_model;
@@ -804,6 +821,7 @@ 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))
@@ -831,17 +849,16 @@ gda_data_model_iter_move_prev_default (GdaDataModel *model, GdaDataModelIter *it
 		cvalue = gda_data_model_get_value_at (model, col, row, NULL);
 		if (!cvalue || 
 		    !gda_holder_set_value ((GdaHolder*) list->data, cvalue, NULL)) {
-			g_object_set (G_OBJECT (iter), "current-row", row, 
-				      "update-model", update_model, NULL);
-			gda_data_model_iter_invalidate_contents (iter);
-			return FALSE;
+			gda_holder_force_invalid ((GdaHolder*) list->data);
+			retval = FALSE;
 		}
-		set_param_attributes ((GdaHolder*) list->data,
-				      gda_data_model_get_attributes_at (model, col, row));
+		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 TRUE;
+	return retval;
 }
 
 
diff --git a/libgda/gda-data-select.c b/libgda/gda-data-select.c
index a9e9bf6..7209c51 100644
--- a/libgda/gda-data-select.c
+++ b/libgda/gda-data-select.c
@@ -1858,7 +1858,6 @@ gda_data_select_iter_next (GdaDataModel *model, GdaDataModelIter *iter)
 	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);
-		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
 	}
 	
@@ -1910,7 +1909,6 @@ gda_data_select_iter_prev (GdaDataModel *model, GdaDataModelIter *iter)
 	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);
-		gda_data_model_iter_invalidate_contents (iter);
 		return FALSE;
 	}
 
@@ -1952,7 +1950,6 @@ gda_data_select_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint r
 		if (!CLASS (model)->fetch_at (imodel, &prow, int_row, NULL)) {
 			/* an error occurred */
 			g_object_set (G_OBJECT (iter), "current-row", row, NULL);
-			gda_data_model_iter_invalidate_contents (iter);
 			return FALSE;
 		}
 
@@ -1975,7 +1972,6 @@ gda_data_select_iter_at_row (GdaDataModel *model, GdaDataModelIter *iter, gint r
 			/* implementation of fetch_at() is optional */
 			TO_IMPLEMENT; /* iter back or forward the right number of times */
 			g_object_set (G_OBJECT (iter), "current-row", row, NULL);
-			gda_data_model_iter_invalidate_contents (iter);
 			return FALSE;
 		}
 	}
diff --git a/libgda/gda-set.c b/libgda/gda-set.c
index dfdc97c..473be5a 100644
--- a/libgda/gda-set.c
+++ b/libgda/gda-set.c
@@ -1275,6 +1275,12 @@ gda_set_is_valid (GdaSet *set, GError **error)
 		}
 	}
 
+	return _gda_set_validate (set, error);
+}
+
+gboolean
+_gda_set_validate (GdaSet *set, GError **error)
+{
 	/* signal the holder validate-set */
 	GError *lerror = NULL;
 #ifdef GDA_DEBUG_signal
@@ -1291,6 +1297,7 @@ gda_set_is_valid (GdaSet *set, GError **error)
 	return TRUE;
 }
 
+
 /**
  * gda_set_get_holder
  * @set: a #GdaSet object
diff --git a/libgda/gda-set.h b/libgda/gda-set.h
index 5cf4272..45a26f7 100644
--- a/libgda/gda-set.h
+++ b/libgda/gda-set.h
@@ -132,6 +132,10 @@ GdaSetSource *gda_set_get_source_for_model     (GdaSet *set, GdaDataModel *model
 GdaSetSource *gda_set_get_source               (GdaSet *set, GdaHolder *holder);
 GdaSetGroup  *gda_set_get_group                (GdaSet *set, GdaHolder *holder);
 
+/* private */
+gboolean      _gda_set_validate                (GdaSet *set, GError **error);
+
+
 G_END_DECLS
 
 #endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 30e475a..ddcb879 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -20,7 +20,9 @@ libgda_test_4_0_la_SOURCES = \
         $(test_headers) \
         gda-ddl-creator.c \
 	test-cnc-utils.h \
-	test-cnc-utils.c
+	test-cnc-utils.c \
+	data-model-errors.h \
+	data-model-errors.c
 
 libgda_test_4_0_la_LDFLAGS = -version-info $(GDA_CURRENT):$(GDA_REVISION):$(GDA_AGE) $(NO_UNDEFINED)
 
diff --git a/tests/data-model-errors.c b/tests/data-model-errors.c
new file mode 100644
index 0000000..f02427c
--- /dev/null
+++ b/tests/data-model-errors.c
@@ -0,0 +1,464 @@
+/* GDA common library
+ * Copyright (C) 2010 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib/gstdio.h>
+#include <libgda/gda-data-model.h>
+#include <libgda/gda-data-model-extra.h>
+#include <libgda/gda-row.h>
+#include <data-model-errors.h>
+
+#define NCOLS 4
+typedef struct {
+	gchar *col0;
+	gchar *col1;
+	gchar *col2;
+	gchar *col3;
+} ARow;
+
+ARow data[] = {
+	{"-",        "Cell 0,1", "Cell 0,2", "Cell 0,3"},
+	{"Cell 1,0", "-",        NULL,       "Cell 1,3"},
+	{"Cell 2,0", "Cell 2,1", NULL,       "Cell 2,3"},
+	{"Cell 3,0", "Cell 3,1", "-",        NULL      },
+};
+
+struct _DataModelErrorsPrivate {
+	GSList    *columns; /* list of GdaColumn objects */
+	GPtrArray *rows; /* array of GdaRow pointers */
+};
+
+static void data_model_errors_class_init (DataModelErrorsClass *klass);
+static void data_model_errors_init       (DataModelErrors *model,
+					  DataModelErrorsClass *klass);
+static void data_model_errors_dispose    (GObject *object);
+
+/* GdaDataModel interface */
+static void                 data_model_errors_data_model_init (GdaDataModelIface *iface);
+static gint                 data_model_errors_get_n_rows      (GdaDataModel *model);
+static gint                 data_model_errors_get_n_columns   (GdaDataModel *model);
+static GdaColumn           *data_model_errors_describe_column (GdaDataModel *model, gint col);
+static GdaDataModelAccessFlags data_model_errors_get_access_flags(GdaDataModel *model);
+static const GValue        *data_model_errors_get_value_at    (GdaDataModel *model, gint col, gint row, GError **error);
+static GdaValueAttribute    data_model_errors_get_attributes_at (GdaDataModel *model, gint col, gint row);
+
+static gboolean             data_model_errors_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *value, GError **error);
+static gint                 data_model_errors_append_values (GdaDataModel *model, const GList *values, GError **error);
+static gboolean             data_model_errors_remove_row (GdaDataModel *model, gint row, GError **error);
+
+static GObjectClass *parent_class = NULL;
+#define CLASS(model) (DATA_MODEL_ERRORS_CLASS (G_OBJECT_GET_CLASS (model)))
+
+/*
+ * Object init and dispose
+ */
+static void
+data_model_errors_data_model_init (GdaDataModelIface *iface)
+{
+        iface->i_get_n_rows = data_model_errors_get_n_rows;
+        iface->i_get_n_columns = data_model_errors_get_n_columns;
+        iface->i_describe_column = data_model_errors_describe_column;
+        iface->i_get_access_flags = data_model_errors_get_access_flags;
+        iface->i_get_value_at = data_model_errors_get_value_at;
+        iface->i_get_attributes_at = data_model_errors_get_attributes_at;
+
+        iface->i_create_iter = NULL;
+        iface->i_iter_at_row = NULL;
+        iface->i_iter_next = NULL;
+        iface->i_iter_prev = NULL;
+
+        iface->i_set_value_at = data_model_errors_set_value_at;
+	iface->i_iter_set_value = NULL;
+        iface->i_set_values = NULL;
+        iface->i_append_values = data_model_errors_append_values;
+        iface->i_append_row = NULL;
+        iface->i_remove_row = data_model_errors_remove_row;
+        iface->i_find_row = NULL;
+
+        iface->i_set_notify = NULL;
+        iface->i_get_notify = NULL;
+        iface->i_send_hint = NULL;
+}
+
+static void
+data_model_errors_init (DataModelErrors *model,
+			DataModelErrorsClass *klass)
+{
+	gint i;
+	g_return_if_fail (IS_DATA_MODEL_ERRORS (model));
+
+	model->priv = g_new0 (DataModelErrorsPrivate, 1);
+	
+	/* columns */
+	model->priv->columns = NULL;
+	for (i = 0; i < NCOLS; i++) {
+		GdaColumn *col;
+		gchar *str;
+		col = gda_column_new ();
+		gda_column_set_g_type (col, G_TYPE_STRING);
+		str = g_strdup_printf ("col%d", i);
+		gda_column_set_name (col, str);
+		gda_column_set_description (col, str);
+		g_object_set (G_OBJECT (col), "id", str, NULL);
+		g_free (str);
+
+		model->priv->columns = g_slist_append (model->priv->columns, col);
+	}
+
+	/* rows */
+	model->priv->rows = g_ptr_array_new (); /* array of GdaRow pointers */
+	for (i = 0; i < (sizeof (data) / sizeof (ARow)); i++) {
+		ARow *arow = &(data[i]);
+		GdaRow *row = gda_row_new (NCOLS);
+		GValue *value;
+		value = gda_row_get_value (row, 0);
+		if (arow->col0) {
+			if (*arow->col0 == '-')
+				G_VALUE_TYPE (value) = G_MAXINT;
+			else {
+				g_value_init (value, G_TYPE_STRING);
+				g_value_set_string (value, arow->col0);
+			}
+		}
+
+		value = gda_row_get_value (row, 1);
+		if (arow->col1) {
+			if (*arow->col1 == '-')
+				G_VALUE_TYPE (value) = G_MAXINT;
+			else {
+				g_value_init (value, G_TYPE_STRING);
+				g_value_set_string (value, arow->col1);
+			}
+		}
+
+		value = gda_row_get_value (row, 2);
+		if (arow->col2) {
+			if (*arow->col2 == '-')
+				G_VALUE_TYPE (value) = G_MAXINT;
+			else {
+				g_value_init (value, G_TYPE_STRING);
+				g_value_set_string (value, arow->col2);
+			}
+		}
+
+		value = gda_row_get_value (row, 3);
+		if (arow->col3) {
+			if (*arow->col3 == '-')
+				G_VALUE_TYPE (value) = G_MAXINT;
+			else {
+				g_value_init (value, G_TYPE_STRING);
+				g_value_set_string (value, arow->col3);
+			}
+		}
+
+
+		g_ptr_array_add (model->priv->rows, row);
+	}
+}
+
+static void
+data_model_errors_class_init (DataModelErrorsClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	/* virtual functions */
+	object_class->dispose = data_model_errors_dispose;
+}
+
+static void
+data_model_errors_dispose (GObject * object)
+{
+	DataModelErrors *model = (DataModelErrors *) object;
+
+	g_return_if_fail (IS_DATA_MODEL_ERRORS (model));
+
+	if (model->priv) {
+		if (model->priv->columns) {
+                        g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
+                        g_slist_free (model->priv->columns);
+                        model->priv->columns = NULL;
+                }
+
+		/* DONT: g_ptr_array_foreach (model->priv->rows, (GFunc) g_object_unref, NULL);
+		 * because we use the convention that G_VALUE_TYPE() == G_MAXINT for errors */
+		g_ptr_array_free (model->priv->rows, TRUE);
+		g_free (model->priv);
+		model->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+GType
+data_model_errors_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static GStaticMutex registering = G_STATIC_MUTEX_INIT;
+		static const GTypeInfo info = {
+			sizeof (DataModelErrorsClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) data_model_errors_class_init,
+			NULL,
+			NULL,
+			sizeof (DataModelErrors),
+			0,
+			(GInstanceInitFunc) data_model_errors_init
+		};
+		static const GInterfaceInfo data_model_info = {
+                        (GInterfaceInitFunc) data_model_errors_data_model_init,
+                        NULL,
+                        NULL
+                };
+
+		g_static_mutex_lock (&registering);
+		if (type == 0) {
+			type = g_type_register_static (G_TYPE_OBJECT, "DataModelErrors", &info, 0);
+			g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
+		}
+		g_static_mutex_unlock (&registering);
+	}
+	return type;
+}
+
+/*
+ * data_model_errors_new
+ *
+ * Creates a new #GdaDataModel object
+ *
+ * Returns: a new #GdaDataModel
+ */
+GdaDataModel *
+data_model_errors_new (void)
+{
+	GdaDataModel *model;
+	model = (GdaDataModel *) g_object_new (TYPE_DATA_MODEL_ERRORS, NULL); 
+
+	return model;
+}
+
+static gint
+data_model_errors_get_n_rows (GdaDataModel *model)
+{
+	DataModelErrors *imodel = (DataModelErrors *) model;
+
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (imodel), 0);
+	g_return_val_if_fail (imodel->priv != NULL, 0);
+
+	return imodel->priv->rows->len;
+}
+
+static gint
+data_model_errors_get_n_columns (GdaDataModel *model)
+{
+	DataModelErrors *imodel;
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), 0);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, 0);
+
+	return NCOLS;
+}
+
+static GdaColumn *
+data_model_errors_describe_column (GdaDataModel *model, gint col)
+{
+	DataModelErrors *imodel;
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), NULL);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, NULL);
+
+	return g_slist_nth_data (imodel->priv->columns, col);
+}
+
+static GdaDataModelAccessFlags
+data_model_errors_get_access_flags (GdaDataModel *model)
+{
+	DataModelErrors *imodel;
+	GdaDataModelAccessFlags flags;
+
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), 0);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, 0);
+
+	flags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD | 
+		GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD |
+		GDA_DATA_MODEL_ACCESS_RANDOM |
+		GDA_DATA_MODEL_ACCESS_WRITE;
+
+	return flags;
+}
+
+static const GValue *
+data_model_errors_get_value_at (GdaDataModel *model, gint col, gint row, GError **error)
+{
+	DataModelErrors *imodel;
+	GValue *value = NULL;
+	GdaRow *drow;
+
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), NULL);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, NULL);
+
+	if ((col < 0) || (col > NCOLS)) {
+		gchar *tmp;
+		tmp = g_strdup_printf ("Column %d out of range (0-%d)", col, NCOLS-1);
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_COLUMN_OUT_OF_RANGE_ERROR,
+			      "%s", tmp);
+		g_free (tmp);
+		return NULL;
+	}
+
+	if (row >= imodel->priv->rows->len) {
+		gchar *str;
+		if (imodel->priv->rows->len > 0)
+			str = g_strdup_printf ("Row %d out of range (0-%d)", row,
+					       imodel->priv->rows->len - 1);
+		else
+			str = g_strdup_printf ("Row %d not found (empty data model)", row);
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
+			      "%s", str);
+		g_free (str);
+                return NULL;
+        }
+
+	drow =  g_ptr_array_index (imodel->priv->rows, row);
+	if (drow) {
+		GValue *val = gda_row_get_value (drow, col);
+		if (G_VALUE_TYPE (val) == G_MAXINT) {
+			/* simulates an error */
+			g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
+				     "%s", "Simulated error");
+		}
+		else
+			value = val;
+	}
+	else
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_NOT_FOUND_ERROR,
+			      "%s", "Row not found");
+
+	return value;
+}
+
+static GdaValueAttribute
+data_model_errors_get_attributes_at (GdaDataModel *model, gint col, gint row)
+{
+	DataModelErrors *imodel;
+	GdaValueAttribute flags = 0;
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), 0);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, 0);
+
+	if ((col < 0) || (col > NCOLS)) {
+		gchar *tmp;
+		tmp = g_strdup_printf ("Column %d out of range (0-%d)", col, NCOLS-1);
+		g_free (tmp);
+		return 0;
+	}
+
+	flags = GDA_VALUE_ATTR_NO_MODIF;
+	return flags;
+}
+
+static gboolean
+data_model_errors_set_value_at (GdaDataModel *model, gint col, gint row, const GValue *value, GError **error)
+{
+	gboolean retval = TRUE;
+	DataModelErrors *imodel;
+
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), FALSE);
+	imodel = DATA_MODEL_ERRORS (model);
+	g_return_val_if_fail (imodel->priv, FALSE);
+
+	if ((col < 0) || (col > NCOLS)) {
+		gchar *tmp;
+		tmp = g_strdup_printf ("Column %d out of range (0-%d)", col, NCOLS-1);
+		g_set_error (error, 0, 0, "%s", tmp);
+		g_free (tmp);
+		return FALSE;
+	}
+
+	GdaRow *drow;
+	drow =  g_ptr_array_index (imodel->priv->rows, row);
+	if (drow) {
+		GValue *dvalue;
+		dvalue = gda_row_get_value (drow, row);
+		g_value_copy (value, dvalue);
+	}
+	else
+		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_NOT_FOUND_ERROR,
+			      "%s", "Row not found");
+
+	return retval;
+}
+
+
+
+static gint
+data_model_errors_append_values (GdaDataModel *model, const GList *values, GError **error)
+{
+	DataModelErrors *imodel;
+	
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), -1);
+	imodel = (DataModelErrors *) model;
+	g_return_val_if_fail (imodel->priv, -1);
+
+	TO_IMPLEMENT;
+	g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_FEATURE_NON_SUPPORTED_ERROR,
+			      "%s", "Not implemented");
+	return -1;
+}
+
+static gboolean
+data_model_errors_remove_row (GdaDataModel *model, gint row, GError **error)
+{
+	DataModelErrors *imodel;
+
+	g_return_val_if_fail (IS_DATA_MODEL_ERRORS (model), FALSE);
+	imodel = (DataModelErrors *) model;
+	g_return_val_if_fail (imodel->priv, FALSE);
+
+	if (row >= imodel->priv->rows->len) {
+		gchar *str;
+		if (imodel->priv->rows->len > 0)
+			str = g_strdup_printf ("Row %d out of range (0-%d)", row,
+					       imodel->priv->rows->len - 1);
+		else
+			str = g_strdup_printf ("Row %d not found (empty data model)", row);
+		g_set_error (error, 0, 0, "%s", str);
+		g_free (str);
+                return FALSE;
+        }
+
+	GdaRow *drow;
+	drow =  g_ptr_array_index (imodel->priv->rows, row);
+	/* remove row from data model */
+	g_object_unref (drow);
+	g_ptr_array_remove_index (imodel->priv->rows, row);
+	gda_data_model_row_removed (model, row);
+	
+	return TRUE;
+}
+
diff --git a/tests/data-model-errors.h b/tests/data-model-errors.h
new file mode 100644
index 0000000..dec3a14
--- /dev/null
+++ b/tests/data-model-errors.h
@@ -0,0 +1,54 @@
+/* GDA common library
+ * Copyright (C) 2010 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DATA_MODEL_ERRORS_H__
+#define __DATA_MODEL_ERRORS_H__
+
+#include <libgda/gda-data-model.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_DATA_MODEL_ERRORS            (data_model_errors_get_type())
+#define DATA_MODEL_ERRORS(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_DATA_MODEL_ERRORS, DataModelErrors))
+#define DATA_MODEL_ERRORS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, TYPE_DATA_MODEL_ERRORS, DataModelErrorsClass))
+#define IS_DATA_MODEL_ERRORS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, TYPE_DATA_MODEL_ERRORS))
+#define IS_DATA_MODEL_ERRORS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_DATA_MODEL_ERRORS))
+
+typedef struct _DataModelErrors        DataModelErrors;
+typedef struct _DataModelErrorsClass   DataModelErrorsClass;
+typedef struct _DataModelErrorsPrivate DataModelErrorsPrivate;
+
+struct _DataModelErrors {
+	GObject                 object;
+	DataModelErrorsPrivate *priv;
+};
+
+struct _DataModelErrorsClass {
+	GObjectClass            parent_class;
+};
+
+GType         data_model_errors_get_type     (void) G_GNUC_CONST;
+GdaDataModel *data_model_errors_new          (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/data-models/Makefile.am b/tests/data-models/Makefile.am
index 7b80f01..c9b7b69 100644
--- a/tests/data-models/Makefile.am
+++ b/tests/data-models/Makefile.am
@@ -7,8 +7,8 @@ AM_CPPFLAGS = \
 	-DTOP_SRC_DIR=\""$(top_srcdir)"\" \
 	-DTOP_BUILD_DIR=\""$(top_builddir)"\"
 
-check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs
-TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs
+check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors
+TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors
 
 common_sources = 
 
@@ -48,6 +48,14 @@ check_empty_rs_LDADD = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(LIBGDA_LIBS)
 
+check_model_errors_SOURCES = $(common_sources) check_model_errors.c
+check_model_errors_CFLAGS = \
+	-I$(top_srcdir)/libgda/sqlite
+check_model_errors_LDADD = \
+	$(top_builddir)/libgda/libgda-4.0.la \
+	$(top_builddir)/tests/libgda-test-4.0.la \
+	$(LIBGDA_LIBS)
+
 EXTRA_DIST = \
 	check_virtual.csv \
 	city.csv \
diff --git a/tests/data-models/check_model_errors.c b/tests/data-models/check_model_errors.c
new file mode 100644
index 0000000..f471092
--- /dev/null
+++ b/tests/data-models/check_model_errors.c
@@ -0,0 +1,245 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <sql-parser/gda-sql-statement.h>
+#include <tests/gda-ddl-creator.h>
+#include <glib/gstdio.h>
+#include "../test-cnc-utils.h"
+#include "../data-model-errors.h"
+
+#define fail(x) g_warning (x)
+#define fail_if(x,y) if (x) g_warning (y)
+#define fail_unless(x,y) if (!(x)) g_warning (y)
+
+#define CHECK_EXTRA_INFO
+
+static void dump_data_model (GdaDataModel *model);
+
+typedef gboolean (*TestFunc) (GdaConnection *);
+static gint test1 (GdaConnection *cnc);
+
+TestFunc tests[] = {
+	test1,
+};
+
+int
+main (int argc, char **argv)
+{
+	gint i, ntests = 0, number_failed = 0;
+
+	/* set up test environment */
+        g_setenv ("GDA_TOP_BUILD_DIR", TOP_BUILD_DIR, 0);
+	g_setenv ("GDA_TOP_SRC_DIR", TOP_SRC_DIR, TRUE);
+	gda_init ();
+	
+	for (i = 0; i < sizeof (tests) / sizeof (TestFunc); i++) {
+		g_print ("---------- test %d ----------\n", i+1);
+		gint n = tests[i] (NULL);
+		number_failed += n;
+		if (n > 0) 
+			g_print ("Test %d failed\n", i+1);
+		ntests ++;
+	}
+
+	g_print ("TESTS COUNT: %d\n", i);
+	g_print ("FAILURES: %d\n", number_failed);
+
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+
+static GdaStatement *
+stmt_from_string (const gchar *sql)
+{
+	GdaStatement *stmt;
+	GError *error = NULL;
+
+	static GdaSqlParser *parser = NULL;
+	if (!parser)
+		parser = gda_sql_parser_new ();
+
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, &error);
+	if (!stmt) {
+		g_print ("Cound not parse SQL: %s\nSQL was: %s\n",
+			 error && error->message ? error->message : "No detail",
+			 sql);
+		exit (EXIT_FAILURE);
+	}
+	return stmt;
+}
+
+static gboolean
+check_iter_contents (GdaDataModel *model, GdaDataModelIter *iter)
+{
+	gint i, row;
+	row = gda_data_model_iter_get_row (iter);
+	for (i = 0; i < gda_data_model_get_n_columns (model); i++) {
+		GdaHolder *holder;
+		const GValue *vi, *vm;
+
+		holder = gda_set_get_nth_holder (GDA_SET (iter), i);
+		if (gda_holder_is_valid (holder))
+			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 {
+#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 TRUE;
+}
+
+/*
+ * check modifications statements' setting
+ * 
+ * Returns the number of failures 
+ */
+static gint
+test1 (GdaConnection *cnc)
+{
+	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);
+
+	/* iterate forward */
+	GdaDataModelIter *iter;
+	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;
+		}
+	}
+
+	/* iterate backward */
+	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);
+#endif
+		goto out;
+	}
+	row = gda_data_model_iter_get_row (iter);
+	if (row != i) {
+		nfailed++;
+#ifdef CHECK_EXTRA_INFO
+		g_print ("Error iterating: move to last row (%d) should have set row to %d and is set to %d\n",
+			 i, i, row);
+#endif
+		goto out;
+	}
+	for (;
+	     gda_data_model_iter_is_valid (iter);
+	     i--, gda_data_model_iter_move_prev (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;
+		}
+	}
+
+ out:
+	g_object_unref (model);
+	
+	return nfailed;
+}
+
+static void
+dump_data_model (GdaDataModel *model)
+{
+        gint i, ncols;
+        g_print ("=== Data Model Dump ===\n");
+        ncols = gda_data_model_get_n_columns (model);
+        for (i = 0; i < ncols; i++) {
+                GdaColumn *col;
+                col = gda_data_model_describe_column (model, i);
+                if (!col)
+                        g_print ("Missing column %d\n", i);
+                g_print ("Column %d: ptr=>%p type=>%s\n", i, col, g_type_name (gda_column_get_g_type (col)));
+        }
+        gda_data_model_dump (model, stdout);
+}
+
+static gboolean
+compare_data_models (GdaDataModel *model1, GdaDataModel *model2, GError **error)
+{
+        GdaDataComparator *cmp;
+        GError *lerror = NULL;
+        cmp = (GdaDataComparator*) gda_data_comparator_new (model1, model2);
+        if (! gda_data_comparator_compute_diff (cmp, &lerror)) {
+#ifdef CHECK_EXTRA_INFO
+                g_print ("Could not compute the data model differences: %s\n",
+                         lerror && lerror->message ? lerror->message : "No detail");
+		g_print ("Model1 is:\n");
+                gda_data_model_dump (model1, stdout);
+                g_print ("Model2 is:\n");
+                gda_data_model_dump (model2, stdout);
+#endif
+                goto onerror;
+        }
+        if (gda_data_comparator_get_n_diffs (cmp) != 0) {
+#ifdef CHECK_EXTRA_INFO
+                g_print ("There are some differences when comparing data models...\n");
+                g_print ("Model1 is:\n");
+                gda_data_model_dump (model1, stdout);
+                g_print ("Model2 is:\n");
+                gda_data_model_dump (model2, stdout);
+#endif
+		g_set_error (&lerror, 0, 0,
+			     "%s", "There are some differences when comparing data models...");
+                goto onerror;
+        }
+        g_object_unref (cmp);
+
+        return TRUE;
+
+ onerror:
+        g_propagate_error (error, lerror);
+        return FALSE;
+}



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