1 /*
2  * Copyright (C) 2006 - 2008 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2006 - 2012 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
5  * Copyright (C) 2010 David King <davidk@openismus.com>
6  * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA  02110-1301, USA.
22  */
23 
24 #include "gda-data-access-wrapper.h"
25 
26 #include <string.h>
27 #include <glib/gi18n-lib.h>
28 #include <libgda/gda-enums.h>
29 #include <libgda/gda-data-model.h>
30 #include <libgda/gda-data-model-extra.h>
31 #include <libgda/gda-data-model-iter.h>
32 #include <libgda/gda-row.h>
33 #include <libgda/gda-holder.h>
34 
35 
36 #define ROWS_POOL_SIZE 50
37 struct _GdaDataAccessWrapperPrivate {
38 	GdaDataModel     *model;
39 	GdaDataModelAccessFlags model_access_flags;
40 
41 	GdaDataModelIter *iter;    /* iterator on @model, NULL if @model already is random access */
42 	gint              iter_row;/* current row of @iter, starting at 0 when created */
43 
44 	GHashTable       *rows;    /* NULL if @model already is random access */
45 	gint              nb_rows; /* number of rows of @wrapper */
46 	gint              nb_cols; /* number of columns of @wrapper */
47 	gint              last_row;/* row number of the last row which has been read */
48 	gboolean          end_of_data; /* TRUE if the end of the data model has been reached by the iterator */
49 
50 	GArray           *rows_buffer_array; /* Array of GdaRow */
51 	GArray           *rows_buffer_index; /* Array of indexes: GdaRow at index i in @rows_buffer_array
52 					      * is indexed in @rows with key rows_buffer_index[i] */
53 
54 	/* rows mapping */
55 	GSList           *columns; /* not NULL if a mapping exists */
56 	gint             *rows_mapping; /* @nb_cols is set when @rows_mapping is set, and @rows_mapping's size is @nb_cols */
57 };
58 
59 /* properties */
60 enum
61 {
62         PROP_0,
63 	PROP_MODEL,
64 };
65 
66 static void gda_data_access_wrapper_class_init (GdaDataAccessWrapperClass *klass);
67 static void gda_data_access_wrapper_init       (GdaDataAccessWrapper *model,
68 					      GdaDataAccessWrapperClass *klass);
69 static void gda_data_access_wrapper_dispose    (GObject *object);
70 static void gda_data_access_wrapper_finalize   (GObject *object);
71 
72 static void gda_data_access_wrapper_set_property (GObject *object,
73 						    guint param_id,
74 						    const GValue *value,
75 						    GParamSpec *pspec);
76 static void gda_data_access_wrapper_get_property (GObject *object,
77 						    guint param_id,
78 						    GValue *value,
79 						    GParamSpec *pspec);
80 
81 /* GdaDataModel interface */
82 static void                 gda_data_access_wrapper_data_model_init (GdaDataModelIface *iface);
83 static gint                 gda_data_access_wrapper_get_n_rows      (GdaDataModel *model);
84 static gint                 gda_data_access_wrapper_get_n_columns   (GdaDataModel *model);
85 static GdaColumn           *gda_data_access_wrapper_describe_column (GdaDataModel *model, gint col);
86 static GdaDataModelAccessFlags gda_data_access_wrapper_get_access_flags(GdaDataModel *model);
87 static const GValue        *gda_data_access_wrapper_get_value_at    (GdaDataModel *model, gint col, gint row, GError **error);
88 static GdaValueAttribute    gda_data_access_wrapper_get_attributes_at (GdaDataModel *model, gint col, gint row);
89 static GError             **gda_data_access_wrapper_get_exceptions  (GdaDataModel *model);
90 
91 static void iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdaDataAccessWrapper *model);
92 static void iter_end_of_data_cb (GdaDataModelIter *iter, GdaDataAccessWrapper *model);
93 
94 static GdaRow *create_new_row (GdaDataAccessWrapper *model);
95 
96 static GObjectClass *parent_class = NULL;
97 
98 /**
99  * gda_data_access_wrapper_get_type:
100  *
101  * Returns: the #GType of GdaDataAccessWrapper.
102  */
103 GType
gda_data_access_wrapper_get_type(void)104 gda_data_access_wrapper_get_type (void)
105 {
106 	static GType type = 0;
107 
108 	if (G_UNLIKELY (type == 0)) {
109 		static GMutex registering;
110 		static const GTypeInfo info = {
111 			sizeof (GdaDataAccessWrapperClass),
112 			(GBaseInitFunc) NULL,
113 			(GBaseFinalizeFunc) NULL,
114 			(GClassInitFunc) gda_data_access_wrapper_class_init,
115 			NULL,
116 			NULL,
117 			sizeof (GdaDataAccessWrapper),
118 			0,
119 			(GInstanceInitFunc) gda_data_access_wrapper_init,
120 			0
121 		};
122 
123 		static const GInterfaceInfo data_model_info = {
124 			(GInterfaceInitFunc) gda_data_access_wrapper_data_model_init,
125 			NULL,
126 			NULL
127 		};
128 
129 		g_mutex_lock (&registering);
130 		if (type == 0) {
131 			type = g_type_register_static (G_TYPE_OBJECT, "GdaDataAccessWrapper", &info, 0);
132 			g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
133 		}
134 		g_mutex_unlock (&registering);
135 	}
136 	return type;
137 }
138 
139 static void
gda_data_access_wrapper_class_init(GdaDataAccessWrapperClass * klass)140 gda_data_access_wrapper_class_init (GdaDataAccessWrapperClass *klass)
141 {
142 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
143 
144 	parent_class = g_type_class_peek_parent (klass);
145 
146 	/* properties */
147 	object_class->set_property = gda_data_access_wrapper_set_property;
148         object_class->get_property = gda_data_access_wrapper_get_property;
149 	g_object_class_install_property (object_class, PROP_MODEL,
150                                          g_param_spec_object ("model", NULL, "Data model being wrapped",
151                                                               GDA_TYPE_DATA_MODEL,
152 							      G_PARAM_READABLE | G_PARAM_WRITABLE |
153 							      G_PARAM_CONSTRUCT_ONLY));
154 
155 	/* virtual functions */
156 	object_class->dispose = gda_data_access_wrapper_dispose;
157 	object_class->finalize = gda_data_access_wrapper_finalize;
158 }
159 
160 static void
gda_data_access_wrapper_data_model_init(GdaDataModelIface * iface)161 gda_data_access_wrapper_data_model_init (GdaDataModelIface *iface)
162 {
163 	iface->i_get_n_rows = gda_data_access_wrapper_get_n_rows;
164 	iface->i_get_n_columns = gda_data_access_wrapper_get_n_columns;
165 	iface->i_describe_column = gda_data_access_wrapper_describe_column;
166         iface->i_get_access_flags = gda_data_access_wrapper_get_access_flags;
167 	iface->i_get_value_at = gda_data_access_wrapper_get_value_at;
168 	iface->i_get_attributes_at = gda_data_access_wrapper_get_attributes_at;
169 
170 	iface->i_create_iter = NULL;
171 	iface->i_iter_at_row = NULL;
172 	iface->i_iter_next = NULL;
173 	iface->i_iter_prev = NULL;
174 
175 	iface->i_set_value_at = NULL;
176 	iface->i_iter_set_value = NULL;
177 	iface->i_set_values = NULL;
178         iface->i_append_values = NULL;
179 	iface->i_append_row = NULL;
180 	iface->i_remove_row = NULL;
181 	iface->i_find_row = NULL;
182 
183 	iface->i_set_notify = NULL;
184 	iface->i_get_notify = NULL;
185 	iface->i_send_hint = NULL;
186 
187 	iface->i_get_exceptions = gda_data_access_wrapper_get_exceptions;
188 }
189 
190 static void
gda_data_access_wrapper_init(GdaDataAccessWrapper * model,G_GNUC_UNUSED GdaDataAccessWrapperClass * klass)191 gda_data_access_wrapper_init (GdaDataAccessWrapper *model, G_GNUC_UNUSED GdaDataAccessWrapperClass *klass)
192 {
193 	g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
194 	model->priv = g_new0 (GdaDataAccessWrapperPrivate, 1);
195 	model->priv->iter_row = -1; /* because model->priv->iter does not yet exist */
196 	model->priv->rows = NULL;
197 	model->priv->nb_rows = -1;
198 	model->priv->end_of_data = FALSE;
199 	model->priv->last_row = -1;
200 
201 	model->priv->rows_buffer_array = NULL;
202 	model->priv->rows_buffer_index = NULL;
203 }
204 
205 static void
model_row_inserted_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)206 model_row_inserted_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
207 {
208 	gda_data_model_row_inserted ((GdaDataModel*) model, row);
209 }
210 
211 static void
model_row_updated_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)212 model_row_updated_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
213 {
214 	gda_data_model_row_updated ((GdaDataModel*) model, row);
215 }
216 
217 static void
model_row_removed_cb(G_GNUC_UNUSED GdaDataModel * mod,gint row,GdaDataAccessWrapper * model)218 model_row_removed_cb (G_GNUC_UNUSED GdaDataModel *mod, gint row, GdaDataAccessWrapper *model)
219 {
220 	gda_data_model_row_removed ((GdaDataModel*) model, row);
221 }
222 
223 static void
model_reset_cb(G_GNUC_UNUSED GdaDataModel * mod,GdaDataAccessWrapper * model)224 model_reset_cb (G_GNUC_UNUSED GdaDataModel *mod, GdaDataAccessWrapper *model)
225 {
226 	gda_data_model_reset ((GdaDataModel*) model);
227 }
228 
229 static void
clear_internal_state(GdaDataAccessWrapper * model)230 clear_internal_state (GdaDataAccessWrapper *model)
231 {
232 	if (model->priv) {
233 		if (model->priv->columns) {
234 			g_slist_foreach (model->priv->columns, (GFunc) g_object_unref, NULL);
235 			g_slist_free (model->priv->columns);
236 			model->priv->columns = NULL;
237 		}
238 
239 		model->priv->nb_cols = 0;
240 
241 		if (model->priv->rows_buffer_array) {
242 			g_array_free (model->priv->rows_buffer_array, TRUE);
243 			model->priv->rows_buffer_array = NULL;
244 		}
245 
246 		if (model->priv->rows_buffer_index) {
247 			g_array_free (model->priv->rows_buffer_index, TRUE);
248 			model->priv->rows_buffer_index = NULL;
249 		}
250 	}
251 }
252 
253 static void
gda_data_access_wrapper_dispose(GObject * object)254 gda_data_access_wrapper_dispose (GObject *object)
255 {
256 	GdaDataAccessWrapper *model = (GdaDataAccessWrapper *) object;
257 
258 	g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
259 
260 	/* free memory */
261 	if (model->priv) {
262 		/* random access model free */
263 		clear_internal_state (model);
264 
265 		if (model->priv->iter) {
266 			g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->iter),
267 							      G_CALLBACK (iter_row_changed_cb), model);
268 			g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->iter),
269 							      G_CALLBACK (iter_end_of_data_cb), model);
270 
271 			g_object_unref (model->priv->iter);
272 			model->priv->iter = NULL;
273 		}
274 
275 		if (model->priv->model) {
276 			if (model->priv->rows) {
277 				g_hash_table_destroy (model->priv->rows);
278 				model->priv->rows = NULL;
279 			}
280 			else {
281 				g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
282 								      G_CALLBACK (model_row_inserted_cb), model);
283 				g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
284 								      G_CALLBACK (model_row_updated_cb), model);
285 				g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
286 								      G_CALLBACK (model_row_removed_cb), model);
287 				g_signal_handlers_disconnect_by_func (G_OBJECT (model->priv->model),
288 								      G_CALLBACK (model_reset_cb), model);
289 			}
290 			g_object_unref (model->priv->model);
291 			model->priv->model = NULL;
292 		}
293 	}
294 
295 	/* chain to parent class */
296 	parent_class->dispose (object);
297 }
298 
299 static void
gda_data_access_wrapper_finalize(GObject * object)300 gda_data_access_wrapper_finalize (GObject *object)
301 {
302 	GdaDataAccessWrapper *model = (GdaDataAccessWrapper *) object;
303 
304 	g_return_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model));
305 
306 	/* free memory */
307 	if (model->priv) {
308 		g_free (model->priv);
309 		model->priv = NULL;
310 	}
311 
312 	/* chain to parent class */
313 	parent_class->finalize (object);
314 }
315 
316 static void
compute_columns(GdaDataAccessWrapper * model)317 compute_columns (GdaDataAccessWrapper *model)
318 {
319 	if (model->priv->rows_mapping) {
320 		/* use model->priv->rows_mapping to create columns, and correct it if
321 		 * needed to remove out of range columns */
322 		gint *nmapping;
323 		gint i, j, nb_cols;
324 		g_assert (!model->priv->columns);
325 		nmapping = g_new (gint, model->priv->nb_cols);
326 		nb_cols = gda_data_model_get_n_columns (model->priv->model);
327 		for (i = 0, j = 0; i < model->priv->nb_cols; i++) {
328 			gint nb = model->priv->rows_mapping [i];
329 			if (nb >= nb_cols)
330 				continue;
331 			GdaColumn *column;
332 			column = gda_data_model_describe_column (model->priv->model, nb);
333 			if (!column)
334 				continue;
335 			model->priv->columns = g_slist_append (model->priv->columns,
336 							       gda_column_copy (column));
337 			nmapping [j] = nb;
338 			j++;
339 		}
340 		model->priv->nb_cols = j;
341 		g_free (model->priv->rows_mapping);
342 		model->priv->rows_mapping = nmapping;
343 	}
344 	else
345 		model->priv->nb_cols = gda_data_model_get_n_columns (model->priv->model);
346 }
347 
348 static void
gda_data_access_wrapper_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)349 gda_data_access_wrapper_set_property (GObject *object,
350 				      guint param_id,
351 				      const GValue *value,
352 				      GParamSpec *pspec)
353 {
354 	GdaDataAccessWrapper *model;
355 
356 	model = GDA_DATA_ACCESS_WRAPPER (object);
357 	if (model->priv) {
358 		switch (param_id) {
359 		case PROP_MODEL: {
360 			GdaDataModel *mod = g_value_get_object (value);
361 			if (mod) {
362 				g_return_if_fail (GDA_IS_DATA_MODEL (mod));
363 				model->priv->model_access_flags = gda_data_model_get_access_flags (mod);
364 
365 				if (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_RANDOM) {
366 					g_signal_connect (G_OBJECT (mod), "row-inserted",
367 							  G_CALLBACK (model_row_inserted_cb), model);
368 					g_signal_connect (G_OBJECT (mod), "row-updated",
369 							  G_CALLBACK (model_row_updated_cb), model);
370 					g_signal_connect (G_OBJECT (mod), "row-removed",
371 							  G_CALLBACK (model_row_removed_cb), model);
372 					g_signal_connect (G_OBJECT (mod), "reset",
373 							  G_CALLBACK (model_reset_cb), model);
374 				}
375 				else {
376 					model->priv->iter = gda_data_model_create_iter (mod);
377 					g_return_if_fail (model->priv->iter);
378 					g_object_set (model->priv->iter, "validate-changes", FALSE,
379 						      NULL);
380 					g_signal_connect (G_OBJECT (model->priv->iter), "row-changed",
381 							  G_CALLBACK (iter_row_changed_cb), model);
382 					g_signal_connect (G_OBJECT (model->priv->iter), "end-of-data",
383 							  G_CALLBACK (iter_end_of_data_cb), model);
384 					model->priv->iter_row = -1; /* because model->priv->iter is invalid */
385 					model->priv->rows = g_hash_table_new_full (g_int_hash, g_int_equal,
386 										   g_free,
387 										   (GDestroyNotify) g_object_unref);
388 				}
389 
390                                 if (model->priv->model)
391 					g_object_unref (model->priv->model);
392 
393 				model->priv->model = mod;
394 				g_object_ref (mod);
395 
396 				compute_columns (model);
397 			}
398 			break;
399 		}
400 		default:
401 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
402 			break;
403 		}
404 	}
405 }
406 
407 static void
gda_data_access_wrapper_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)408 gda_data_access_wrapper_get_property (GObject *object,
409 					guint param_id,
410 					GValue *value,
411 					GParamSpec *pspec)
412 {
413 	GdaDataAccessWrapper *model;
414 
415 	model = GDA_DATA_ACCESS_WRAPPER (object);
416 	if (model->priv) {
417 		switch (param_id) {
418 		case PROP_MODEL:
419 			g_value_set_object (value, G_OBJECT (model->priv->model));
420 			break;
421 		default:
422 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
423 			break;
424 		}
425 	}
426 }
427 
428 /**
429  * gda_data_access_wrapper_new:
430  * @model: a #GdaDataModel
431  *
432  * Creates a new #GdaDataModel object which buffers the rows of @model. This object is useful
433  * only if @model can only be accessed using cursor based method.
434  *
435  * Returns: (transfer full): a pointer to the newly created #GdaDataModel.
436  */
437 GdaDataModel *
gda_data_access_wrapper_new(GdaDataModel * model)438 gda_data_access_wrapper_new (GdaDataModel *model)
439 {
440 	GdaDataAccessWrapper *retmodel;
441 
442 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
443 
444 	retmodel = g_object_new (GDA_TYPE_DATA_ACCESS_WRAPPER,
445 				 "model", model, NULL);
446 
447 	return GDA_DATA_MODEL (retmodel);
448 }
449 
450 /**
451  * gda_data_access_wrapper_set_mapping:
452  * @wrapper: a #GdaDataAccessWrapper object
453  * @mapping: (allow-none) (array length=mapping_size): an array of #gint which represents the mapping between @wrapper's columns
454  * and the columns of the wrapped data model
455  * @mapping_size: the size of @mapping.
456  *
457  * @wrapper will report as many columns as @mapping_size, and for each value at position 'i' in @mapping,
458  * @wrapper will report the 'i'th column, mapped to the wrapped data model column at position mapping[i].
459  * For example if mapping is {3, 4, 0}, then @wrapper will report 3 columns, respectively mapped to the 4th,
460  * 5th and 1st columns of the wrapped data model (as column numbers start at 0).
461  *
462  * If @mapping is %NULL, then no mapping is done and @wrapper's columns will be the same as the wrapped
463  * data model.
464  *
465  * If a column in @mapping does not exist in the wrapped data model, then it is simply ignored (no error
466  * reported).
467  *
468  * Please note that if @wrapper has already been used and if the wrapped data model offers a cursor forward
469  * access mode, then this method will return %FALSE and no action will be done.
470  *
471  * If the mapping is applied, then any existing iterator will be invalid, and @wrapper is reset as if it
472  * had just been created.
473  *
474  * Returns: %TRUE if the mapping actually changed
475  *
476  * Since: 5.2
477  */
478 gboolean
gda_data_access_wrapper_set_mapping(GdaDataAccessWrapper * wrapper,const gint * mapping,gint mapping_size)479 gda_data_access_wrapper_set_mapping (GdaDataAccessWrapper *wrapper, const gint *mapping, gint mapping_size)
480 {
481 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (wrapper), FALSE);
482 
483 	if ((! (wrapper->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)) &&
484 	    (wrapper->priv->iter_row >= 0)) {
485 		/* error */
486 		return FALSE;
487 	}
488 
489 	clear_internal_state (wrapper);
490 
491 	if (mapping) {
492 		/* define mapping */
493 		wrapper->priv->rows_mapping = g_new (gint, mapping_size);
494 		memcpy (wrapper->priv->rows_mapping, mapping, mapping_size * sizeof (gint));
495 		wrapper->priv->nb_cols = mapping_size;
496 	}
497 	else {
498 		if (wrapper->priv->rows_mapping) {
499 			g_free (wrapper->priv->rows_mapping);
500 			wrapper->priv->rows_mapping = NULL;
501 		}
502 	}
503 
504 	compute_columns (wrapper);
505 	gda_data_model_reset ((GdaDataModel*) wrapper);
506 
507 	return TRUE;
508 }
509 
510 /*
511  * GdaDataModel interface implementation
512  */
513 static gint
gda_data_access_wrapper_get_n_rows(GdaDataModel * model)514 gda_data_access_wrapper_get_n_rows (GdaDataModel *model)
515 {
516 	GdaDataAccessWrapper *imodel;
517 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
518 	imodel = (GdaDataAccessWrapper*) model;
519 	g_return_val_if_fail (imodel->priv, 0);
520 
521 	if (imodel->priv->nb_rows >= 0)
522 		return imodel->priv->nb_rows;
523 
524 	if (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_RANDOM)
525 		/* imodel->priv->mode is a random access model, use it */
526 		imodel->priv->nb_rows = gda_data_model_get_n_rows (imodel->priv->model);
527 	else {
528 		/* go at the end */
529 		while (!imodel->priv->end_of_data) {
530 			if (! gda_data_model_iter_move_next (imodel->priv->iter))
531 				break;
532 		}
533 		if (imodel->priv->end_of_data)
534 			imodel->priv->nb_rows = imodel->priv->last_row +1;
535 		else
536 			imodel->priv->nb_rows = -1;
537 	}
538 
539 	return imodel->priv->nb_rows;
540 }
541 
542 static gint
gda_data_access_wrapper_get_n_columns(GdaDataModel * model)543 gda_data_access_wrapper_get_n_columns (GdaDataModel *model)
544 {
545 	GdaDataAccessWrapper *imodel;
546 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
547 	imodel = (GdaDataAccessWrapper*) model;
548 	g_return_val_if_fail (imodel->priv, 0);
549 
550 	if (imodel->priv->model)
551 		return imodel->priv->nb_cols;
552 	else
553 		return 0;
554 }
555 
556 static GdaColumn *
gda_data_access_wrapper_describe_column(GdaDataModel * model,gint col)557 gda_data_access_wrapper_describe_column (GdaDataModel *model, gint col)
558 {
559 	GdaDataAccessWrapper *imodel;
560 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
561 	imodel = (GdaDataAccessWrapper*) model;
562 	g_return_val_if_fail (imodel->priv, NULL);
563 
564 	if (imodel->priv->model) {
565 		if (imodel->priv->columns)
566 			return g_slist_nth_data (imodel->priv->columns, col);
567 		else
568 			return gda_data_model_describe_column (imodel->priv->model, col);
569 	}
570 	else
571 		return NULL;
572 }
573 
574 static GdaDataModelAccessFlags
gda_data_access_wrapper_get_access_flags(GdaDataModel * model)575 gda_data_access_wrapper_get_access_flags (GdaDataModel *model)
576 {
577 	GdaDataAccessWrapper *imodel;
578 
579 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
580 	imodel = (GdaDataAccessWrapper*) model;
581 	g_return_val_if_fail (imodel->priv, 0);
582 
583 	return GDA_DATA_MODEL_ACCESS_RANDOM;
584 }
585 
586 static GdaRow *
create_new_row(GdaDataAccessWrapper * model)587 create_new_row (GdaDataAccessWrapper *model)
588 {
589 	gint i;
590 	GdaRow *row;
591 
592 	row = gda_row_new (model->priv->nb_cols);
593 	for (i = 0; i < model->priv->nb_cols; i++) {
594 		GdaHolder *holder;
595 		GValue *dest;
596 		dest = gda_row_get_value (row, i);
597 		if (model->priv->rows_mapping)
598 			holder = gda_set_get_nth_holder ((GdaSet *) model->priv->iter, model->priv->rows_mapping [i]);
599 		else
600 			holder = gda_set_get_nth_holder ((GdaSet *) model->priv->iter, i);
601 		if (holder) {
602 			const GValue *cvalue = gda_holder_get_value (holder);
603 			if (cvalue) {
604 				gda_value_reset_with_type (dest, G_VALUE_TYPE ((GValue *) cvalue));
605 				g_value_copy (cvalue, dest);
606 			}
607 			else
608 				gda_value_set_null (dest);
609 		}
610 		else
611 			gda_row_invalidate_value (row, dest);
612 	}
613 
614 	gint *ptr;
615 	ptr = g_new (gint, 1);
616 	*ptr = model->priv->iter_row;
617 	g_hash_table_insert (model->priv->rows, ptr, row);
618 	/*g_print ("%s(%d)\n", __FUNCTION__, model->priv->iter_row);*/
619 
620 	return row;
621 }
622 
623 static const GValue *
gda_data_access_wrapper_get_value_at(GdaDataModel * model,gint col,gint row,GError ** error)624 gda_data_access_wrapper_get_value_at (GdaDataModel *model, gint col, gint row, GError **error)
625 {
626 	GdaDataAccessWrapper *imodel;
627 
628 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
629 	imodel = (GdaDataAccessWrapper*) model;
630 	g_return_val_if_fail (imodel->priv, NULL);
631 	g_return_val_if_fail (imodel->priv->model, NULL);
632 	g_return_val_if_fail (row >= 0, NULL);
633 
634 	if (col >= imodel->priv->nb_cols) {
635 		g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_COLUMN_OUT_OF_RANGE_ERROR,
636 			     _("Column %d out of range (0-%d)"), col, imodel->priv->nb_cols - 1);
637 		return NULL;
638 	}
639 
640 	if (!imodel->priv->rows) {
641 		/* imodel->priv->model is a random access model, use it */
642 		if (imodel->priv->rows_mapping)
643 			return gda_data_model_get_value_at (imodel->priv->model, imodel->priv->rows_mapping [col],
644 							    row, error);
645 		else
646 			return gda_data_model_get_value_at (imodel->priv->model, col, row, error);
647 	}
648 	else {
649 		GdaRow *gda_row;
650 		gint tmp;
651 		tmp = row;
652 		gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
653 		if (gda_row) {
654 			GValue *val = gda_row_get_value (gda_row, col);
655 			if (gda_row_value_is_valid (gda_row, val))
656 				return val;
657 			else
658 				return NULL;
659 		}
660 		else {
661 			g_assert (imodel->priv->iter);
662 			if (imodel->priv->iter_row < 0) {
663 				if (gda_data_model_iter_move_next (imodel->priv->iter)) {
664 					tmp = row;
665 					gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
666 					if (row == imodel->priv->iter_row) {
667 						GValue *val = gda_row_get_value (gda_row, col);
668 						if (gda_row_value_is_valid (gda_row, val))
669 							return val;
670 						else
671 							return NULL;
672 					}
673 				}
674 				else {
675 					g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
676 						      "%s", _("Can't set iterator's position"));
677 					return NULL;
678 				}
679 			}
680 
681 			gda_row = NULL;
682 			if (row != imodel->priv->iter_row) {
683 				if (row > imodel->priv->iter_row) {
684 					/* need to move forward */
685 					while ((imodel->priv->iter_row < row) &&
686 					       gda_data_model_iter_move_next (imodel->priv->iter));
687 				}
688 				else {
689 					/* need to move backwards */
690 					g_assert (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD);
691 					while ((imodel->priv->iter_row > row) &&
692 					       gda_data_model_iter_move_prev (imodel->priv->iter)) ;
693 				}
694 			}
695 
696 			if (! (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD) ||
697 			    ! (imodel->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD)) {
698 				tmp = row;
699 				gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
700 
701 				if (gda_row) {
702 					GValue *val = gda_row_get_value (gda_row, col);
703 					if (gda_row_value_is_valid (gda_row, val))
704 						return val;
705 					else
706 						return NULL;
707 				}
708 			}
709 			else {
710 				/* in this case iter can be moved forward and backward at will => we only
711 				 * need to keep a pool of GdaRow for performances reasons */
712 				tmp = row;
713 				gda_row = g_hash_table_lookup (imodel->priv->rows, &tmp);
714 
715 				if (!gda_row) {
716 					if (! imodel->priv->rows_buffer_array) {
717 						imodel->priv->rows_buffer_array = g_array_sized_new (FALSE, FALSE,
718 												     sizeof (GdaRow*),
719 												     ROWS_POOL_SIZE);
720 						imodel->priv->rows_buffer_index = g_array_sized_new (FALSE, FALSE,
721 												     sizeof (gint),
722 												     ROWS_POOL_SIZE);
723 					}
724 					else if (imodel->priv->rows_buffer_array->len == ROWS_POOL_SIZE) {
725 						/* get rid of the oldest row (was model's index_row row)*/
726 						gint index_row;
727 
728 						index_row = g_array_index (imodel->priv->rows_buffer_index, gint,
729 									   ROWS_POOL_SIZE - 1);
730 						g_array_remove_index (imodel->priv->rows_buffer_array,
731 								      ROWS_POOL_SIZE - 1);
732 						g_array_remove_index (imodel->priv->rows_buffer_index,
733 								      ROWS_POOL_SIZE - 1);
734 						g_hash_table_remove (imodel->priv->rows, &index_row);
735 					}
736 					if (gda_data_model_iter_move_to_row (imodel->priv->iter, row)) {
737 						gda_row = create_new_row (imodel);
738 						g_array_prepend_val (imodel->priv->rows_buffer_array, gda_row);
739 						g_array_prepend_val (imodel->priv->rows_buffer_index, imodel->priv->iter_row);
740 					}
741 				}
742 
743 				GValue *val;
744 				val = gda_row ? gda_row_get_value (gda_row, col) : NULL;
745 				if (gda_row && gda_row_value_is_valid (gda_row, val))
746 					return val;
747 				else
748 					return NULL;
749 			}
750 		}
751 	}
752 
753 	g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ACCESS_ERROR,
754 		      "%s", _("Can't access data"));
755 	return NULL;
756 }
757 
758 static void
iter_row_changed_cb(GdaDataModelIter * iter,gint row,GdaDataAccessWrapper * model)759 iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdaDataAccessWrapper *model)
760 {
761 	g_assert (model->priv->rows);
762 
763 	/*g_print ("%s(%d)\n", __FUNCTION__, row);*/
764 	if (gda_data_model_iter_is_valid (iter)) {
765 		model->priv->iter_row = row;
766 		if (model->priv->last_row < row)
767 			model->priv->last_row = row;
768 
769 		if (! (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD) ||
770 		    ! (model->priv->model_access_flags & GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD)) {
771 			/* keep the changes in rows */
772 			GdaRow *gda_row;
773 			gint tmp;
774 			tmp = row;
775 			gda_row = g_hash_table_lookup (model->priv->rows, &tmp);
776 			if (!gda_row)
777 				create_new_row (model);
778 		}
779 	}
780 }
781 
782 static void
iter_end_of_data_cb(G_GNUC_UNUSED GdaDataModelIter * iter,GdaDataAccessWrapper * model)783 iter_end_of_data_cb (G_GNUC_UNUSED GdaDataModelIter *iter, GdaDataAccessWrapper *model)
784 {
785 	g_assert (GDA_IS_DATA_ACCESS_WRAPPER (model));
786 	model->priv->end_of_data = TRUE;
787 }
788 
789 static GdaValueAttribute
gda_data_access_wrapper_get_attributes_at(GdaDataModel * model,gint col,gint row)790 gda_data_access_wrapper_get_attributes_at (GdaDataModel *model, gint col, gint row)
791 {
792 	GdaValueAttribute flags = 0;
793 	GdaDataAccessWrapper *imodel;
794 
795 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), 0);
796 	imodel = (GdaDataAccessWrapper *) model;
797 	g_return_val_if_fail (imodel->priv, 0);
798 
799 	if (imodel->priv->model) {
800 		if (imodel->priv->rows_mapping)
801 			flags = gda_data_model_get_attributes_at (imodel->priv->model, imodel->priv->rows_mapping [col],
802 								  row);
803 		else
804 			flags = gda_data_model_get_attributes_at (imodel->priv->model, col, row);
805 	}
806 
807 	flags |= GDA_VALUE_ATTR_NO_MODIF;
808 
809 	return flags;
810 }
811 
812 static GError **
gda_data_access_wrapper_get_exceptions(GdaDataModel * model)813 gda_data_access_wrapper_get_exceptions (GdaDataModel *model)
814 {
815 	GdaDataAccessWrapper *imodel;
816 
817 	g_return_val_if_fail (GDA_IS_DATA_ACCESS_WRAPPER (model), NULL);
818 	imodel = (GdaDataAccessWrapper *) model;
819 	g_return_val_if_fail (imodel->priv, NULL);
820 	return gda_data_model_get_exceptions (imodel->priv->model);
821 }
822