1 /*
2  * Copyright (C) 2006 - 2011 Murray Cumming <murrayc@murrayc.com>
3  * Copyright (C) 2006 - 2012 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2007 Armin Burgmeier <arminb@src.gnome.org>
5  * Copyright (C) 2007 Leonardo Boshell <lb@kmc.com.co>
6  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
7  * Copyright (C) 2010 David King <davidk@openismus.com>
8  * Copyright (C) 2010 Jonh Wendell <jwendell@gnome.org>
9  * Copyright (C) 2011 - 2012 Daniel Espinosa <despinosa@src.gnome.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
24  * Boston, MA  02110-1301, USA.
25  */
26 
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include "gda-data-proxy.h"
30 #include "gda-data-model.h"
31 #include "gda-data-model-array.h"
32 #include "gda-data-model-extra.h"
33 #include "gda-data-model-iter.h"
34 #include "gda-data-select.h"
35 #include "gda-holder.h"
36 #include "gda-set.h"
37 #include "gda-marshal.h"
38 #include "gda-enums.h"
39 #include "gda-util.h"
40 #include "gda-marshal.h"
41 #include "gda-data-access-wrapper.h"
42 #include "gda-enum-types.h"
43 #include <virtual/libgda-virtual.h>
44 #include <sql-parser/gda-sql-parser.h>
45 #include <sql-parser/gda-sql-statement.h>
46 #include <sql-parser/gda-statement-struct-util.h>
47 #include <libgda/gda-custom-marshal.h>
48 #include <libgda/gda-types.h>
49 #include <gda-mutex.h>
50 
51 /*
52  * Main static functions
53  */
54 static void gda_data_proxy_class_init (GdaDataProxyClass * class);
55 static void gda_data_proxy_init (GdaDataProxy *srv);
56 static void gda_data_proxy_dispose (GObject *object);
57 static void gda_data_proxy_finalize (GObject *object);
58 
59 static void gda_data_proxy_set_property (GObject *object,
60 					 guint param_id,
61 					 const GValue *value,
62 					 GParamSpec *pspec);
63 static void gda_data_proxy_get_property (GObject *object,
64 					 guint param_id,
65 					 GValue *value,
66 					 GParamSpec *pspec);
67 /* GdaDataModel interface */
68 static void                 gda_data_proxy_data_model_init (GdaDataModelIface *iface);
69 
70 static gint                 gda_data_proxy_get_n_rows      (GdaDataModel *model);
71 static gint                 gda_data_proxy_get_n_columns   (GdaDataModel *model);
72 static GdaColumn           *gda_data_proxy_describe_column (GdaDataModel *model, gint col);
73 static const GValue        *gda_data_proxy_get_value_at    (GdaDataModel *model, gint col, gint row, GError **error);
74 static GdaValueAttribute    gda_data_proxy_get_attributes_at (GdaDataModel *model, gint col, gint row);
75 
76 
77 static GdaDataModelAccessFlags gda_data_proxy_get_access_flags(GdaDataModel *model);
78 
79 static gboolean             gda_data_proxy_set_value_at    (GdaDataModel *model, gint col, gint row,
80 							    const GValue *value, GError **error);
81 static gboolean             gda_data_proxy_set_values      (GdaDataModel *model, gint row,
82 							    GList *values, GError **error);
83 static gint                 gda_data_proxy_find_row_from_values (GdaDataModel *model, GSList *values,
84 						           gint *cols_index);
85 
86 static gint                 gda_data_proxy_append_values   (GdaDataModel *model, const GList *values, GError **error);
87 static gint                 gda_data_proxy_append_row      (GdaDataModel *model, GError **error);
88 static gboolean             gda_data_proxy_remove_row      (GdaDataModel *model, gint row, GError **error);
89 
90 static void                 gda_data_proxy_set_notify      (GdaDataModel *model, gboolean do_notify_changes);
91 static gboolean             gda_data_proxy_get_notify      (GdaDataModel *model);
92 static void                 gda_data_proxy_send_hint       (GdaDataModel *model, GdaDataModelHint hint,
93 							    const GValue *hint_value);
94 
95 /* cache management */
96 static void clean_cached_changes (GdaDataProxy *proxy);
97 static void migrate_current_changes_to_cache (GdaDataProxy *proxy);
98 static void fetch_current_cached_changes (GdaDataProxy *proxy);
99 
100 #define DEBUG_SYNC
101 #undef DEBUG_SYNC
102 
103 /* get a pointer to the parents to be able to call their destructor */
104 static GObjectClass  *parent_class = NULL;
105 extern GdaAttributesManager *gda_holder_attributes_manager;
106 
107 static GMutex parser_mutex;
108 static GdaSqlParser *internal_parser;
109 static GMutex provider_mutex;
110 static GdaVirtualProvider *virtual_provider = NULL;
111 
112 /* signals */
113 enum
114 {
115         ROW_DELETE_CHANGED,
116 	SAMPLE_SIZE_CHANGED,
117 	SAMPLE_CHANGED,
118 	VALIDATE_ROW_CHANGES,
119 	ROW_CHANGES_APPLIED,
120 	FILTER_CHANGED,
121         LAST_SIGNAL
122 };
123 
124 static gint gda_data_proxy_signals[LAST_SIGNAL] = { 0, 0, 0, 0, 0, 0 };
125 
126 
127 /* properties */
128 enum
129 {
130 	PROP_0,
131 	PROP_MODEL,
132 	PROP_ADD_NULL_ENTRY,
133 	PROP_DEFER_SYNC,
134 	PROP_SAMPLE_SIZE,
135 	PROP_CACHE_CHANGES
136 };
137 
138 /*
139  * Structure to hold the status and all the modifications made to a single
140  * row. It is not possible to have ((model_row < 0) && !modify_values)
141  */
142 typedef struct
143 {
144 	gint           model_row;    /* row index in the proxied GdaDataModel, -1 if new row */
145 	gboolean       to_be_deleted;/* TRUE if row is to be deleted */
146 	GSList        *modify_values; /* list of RowValue structures */
147 	GValue       **orig_values;  /* array of the original GValues, indexed on the column numbers */
148 	gint           orig_values_size; /* size of @orig_values, OR (for new rows) the number of columns of the proxied data model the new row is for */
149 } RowModif;
150 #define ROW_MODIF(x) ((RowModif *)(x))
151 
152 /*
153  * Structure to hold the modifications made to a single value
154  */
155 typedef struct
156 {
157 	RowModif      *row_modif;    /* RowModif in which this structure instance appears */
158 	gint           model_column; /* column index in the GdaDataModel */
159         GValue        *value;        /* values are owned here */
160 	GdaValueAttribute attributes;   /* holds flags */
161 } RowValue;
162 #define ROW_VALUE(x) ((RowValue *)(x))
163 
164 /*
165  * Structure to hold which part of the whole data is displayed
166  */
167 typedef struct {
168 	GArray *mapping; /* for proxy's row nb i (or i+1 if
169 			  * there is a NULL rows first), mapping[i] points to
170 			  * the absolute row.
171 			  *
172 			  * If mapping is empty, then either there are no
173 			  * rows to display, or the number of rows is unknown
174 			  * (then it's not filled until row existance can be testes
175 			  */
176 } DisplayChunk;
177 static DisplayChunk *display_chunk_new (gint reserved_size);
178 static void          display_chunk_free (DisplayChunk *chunk);
179 
180 /*
181  * NOTE about the row numbers:
182  *
183  * There may be more rows in the GdaDataProxy than in the GdaDataModel if:
184  *  - some rows have been added in the proxy
185  *    and are not yet in the GdaDataModel (RowModif->model_row = -1 in this case); or
186  *  - there is a NULL row at the beginning
187  *
188  * There are 3 kinds of row numbers:
189  *  - the absolute row numbers which unify under a single numbering scheme all the possible proxy's rows
190  *    which can either be new rows, or data model rows (which may or may not have been modified),
191  *    absolute row numbers are strictly private to the proxy object
192  *  - the row numbers as identified in the proxy (named "proxy_row"), which are the row numbers used
193  *    by the GdaDataModel interface
194  *  - the row numbers in the GdaDataModel being proxied (named "model_row")
195  *
196  * Absolute row numbers are assigned as:
197  *  - rows 0 to N-1 included corresponding to the N rows of the proxied data model
198  *  - rows N to N+M-1 included corresponding to the M added rows
199  *
200  * The rule to go from one row numbering to the other is to use the row conversion functions.
201  *
202  */
203 struct _GdaDataProxyPrivate
204 {
205 	GdaMutex          *mutex;
206 
207 	GdaDataModel      *model; /* Gda model which is proxied */
208 
209 	GdaConnection     *filter_vcnc;   /* virtual connection used for filtering */
210 	gchar             *filter_expr;   /* NULL if no filter applied */
211 	GdaStatement      *filter_stmt;   /* NULL if no filter applied */
212 	GdaDataModel      *filtered_rows; /* NULL if no filter applied. Lists rows (by their number) which must be displayed */
213 
214 	GdaValueAttribute *columns_attrs; /* Each GValue holds a flag of GdaValueAttribute to proxy. cols. attributes */
215 
216 	gint               model_nb_cols; /* = gda_data_model_get_n_columns (model) */
217 	gint               model_nb_rows; /* = gda_data_model_get_n_rows (model) */
218 	gboolean           notify_changes;
219 
220 	GSList            *all_modifs; /* list of RowModif structures, for memory management */
221 	GSList            *new_rows;   /* list of RowModif, no data allocated in this list */
222 	GHashTable        *modify_rows;  /* key = model_row number, value = RowModif, NOT for new rows */
223 
224 	gboolean           defer_proxied_model_insert;
225 	gint               catched_inserted_row;
226 
227 	gboolean           add_null_entry; /* artificially add a NULL entry at the beginning of the tree model */
228 
229 	gboolean           defer_sync;
230 
231 	/* chunking */
232 	gboolean           force_direct_mapping;
233 	gint               sample_first_row;
234 	gint               sample_last_row;
235 	gint               sample_size;
236 	guint              chunk_sync_idle_id;
237 	DisplayChunk      *chunk; /* number of proxy_rows depends directly on chunk->mapping->len */
238 	DisplayChunk      *chunk_to; /* NULL if nothing to do */
239 	gint               chunk_sep;
240 	gint               chunk_proxy_nb_rows;
241 
242 	/* for ALL the columns of proxy */
243 	GdaColumn        **columns;
244 
245 	/* modifications cache */
246 	gboolean           cache_changes;
247 	GSList            *cached_modifs;
248 	GSList            *cached_inserts;
249 };
250 
251 
252 /*
253  * Row conversion functions
254  */
255 
256 /*
257  * May return -1 if:
258  *  - @model_row < 0
259  *  - @model_row is out of bounds (only checked if @proxy's number of rows is known)
260  */
261 static gint
model_row_to_absolute_row(GdaDataProxy * proxy,gint model_row)262 model_row_to_absolute_row (GdaDataProxy *proxy, gint model_row)
263 {
264 	if (model_row < 0)
265 		return -1;
266 	if ((model_row >= proxy->priv->model_nb_rows) && (proxy->priv->model_nb_rows >= 0))
267 		return -1;
268 	else
269 		return model_row;
270 }
271 
272 /*
273  * May return -1 if:
274  *  - @rm is NULL
275  *  - @rm is a new row and @proxy's number of rows is NOT know
276  *  - @rm is not a new row and @rm->model_row == -1
277  */
278 static gint
row_modif_to_absolute_row(GdaDataProxy * proxy,RowModif * rm)279 row_modif_to_absolute_row (GdaDataProxy *proxy, RowModif *rm)
280 {
281 	if (rm == NULL)
282 		return -1;
283 	if (rm->model_row == -1) {
284 		gint index;
285 		if (proxy->priv->model_nb_rows == -1)
286 			return -1;
287 		index = g_slist_index (proxy->priv->new_rows, rm);
288 		if (index < 0)
289 			return -1;
290 		return proxy->priv->model_nb_rows + index;
291 	}
292 	else
293 		return rm->model_row;
294 }
295 
296 /*
297  * if @rm is not %NULL, then it will contain a pointer to the RowModif structure associated to @row
298  *
299  * May return -1 if:
300  *  - absolute row is not a model row
301  */
302 static gint
absolute_row_to_model_row(GdaDataProxy * proxy,gint abs_row,RowModif ** rm)303 absolute_row_to_model_row (GdaDataProxy *proxy, gint abs_row, RowModif **rm)
304 {
305 	if (abs_row < 0)
306 		return -1;
307 	if ((abs_row < proxy->priv->model_nb_rows) || (proxy->priv->model_nb_rows < 0)) {
308 		if (rm) {
309 			gint tmp;
310 			tmp = abs_row;
311 			*rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
312 		}
313 		return abs_row;
314 	}
315 	else {
316 		if (rm)
317 			*rm = g_slist_nth_data (proxy->priv->new_rows, abs_row - proxy->priv->model_nb_rows);
318 		return -1;
319 	}
320 }
321 
322 /*
323  * May return -1 if:
324  *  - @row is not an absolute row
325  */
326 static gint
proxy_row_to_absolute_row(GdaDataProxy * proxy,gint proxy_row)327 proxy_row_to_absolute_row (GdaDataProxy *proxy, gint proxy_row)
328 {
329 	if (proxy_row < 0)
330 		return -1;
331 	if (proxy->priv->force_direct_mapping)
332 		return proxy_row;
333 
334 	if (proxy->priv->add_null_entry) {
335 		if (proxy_row == 0)
336 			return -1;
337 		else
338 			proxy_row--;
339 	}
340 	if (proxy->priv->chunk) {
341 		if ((guint)proxy_row < proxy->priv->chunk->mapping->len)
342 			return g_array_index (proxy->priv->chunk->mapping, gint, proxy_row);
343 		else
344 			return -1;
345 	}
346 	else {
347 		if (proxy->priv->chunk_to &&
348 		    proxy->priv->chunk_to->mapping &&
349 		    (proxy_row < proxy->priv->chunk_sep) &&
350 		    ((guint)proxy_row < proxy->priv->chunk_to->mapping->len))
351 			return g_array_index (proxy->priv->chunk_to->mapping, gint, proxy_row);
352 		else
353 			return proxy_row;
354 	}
355 }
356 
357 #define proxy_row_to_model_row(proxy, proxy_row) \
358 	absolute_row_to_model_row ((proxy), proxy_row_to_absolute_row ((proxy), (proxy_row)), NULL);
359 #define row_modif_to_proxy_row(proxy, rm) \
360 	absolute_row_to_proxy_row ((proxy), row_modif_to_absolute_row ((proxy), (rm)))
361 
362 
363 static RowModif *
proxy_row_to_row_modif(GdaDataProxy * proxy,gint proxy_row)364 proxy_row_to_row_modif (GdaDataProxy *proxy, gint proxy_row)
365 {
366 	RowModif *rm = NULL;
367 	absolute_row_to_model_row (proxy, proxy_row_to_absolute_row (proxy, proxy_row), &rm);
368 
369 	return rm;
370 }
371 
372 /*
373  * May return -1 if:
374  *  - @abs_row is not a proxy row
375  *  - @abs_row is out of bounds (only checked if @proxy's number of rows is known)
376  */
377 static gint
absolute_row_to_proxy_row(GdaDataProxy * proxy,gint abs_row)378 absolute_row_to_proxy_row (GdaDataProxy *proxy, gint abs_row)
379 {
380 	gint proxy_row = -1;
381 	if (abs_row < 0)
382 		return -1;
383 
384 	if (proxy->priv->force_direct_mapping) {
385 		gint proxy_n_rows;
386 		proxy_row = abs_row;
387 		proxy_n_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
388 		if ((proxy_row >= proxy_n_rows) && (proxy_n_rows >= 0))
389 			proxy_row = -1;
390 		return proxy_row;
391 	}
392 
393 	if (proxy->priv->chunk) {
394 		gsize i;
395 		for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
396 			if (g_array_index (proxy->priv->chunk->mapping, gint, i) == abs_row) {
397 				proxy_row = i;
398 				break;
399 			}
400 		}
401 		if ((proxy_row >= 0) && proxy->priv->add_null_entry)
402 			proxy_row ++;
403 	}
404 	else {
405 		if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping) {
406 			/* search in the proxy->priv->chunk_sep first rows of proxy->priv->chunk_to */
407 			gint i;
408 			for (i = 0; i < MIN ((gint)proxy->priv->chunk->mapping->len, proxy->priv->chunk_sep); i++) {
409 				if (g_array_index (proxy->priv->chunk_to->mapping, gint, i) == abs_row) {
410 					proxy_row = i;
411 					break;
412 				}
413 			}
414 			if ((proxy_row >= 0) && proxy->priv->add_null_entry)
415 				proxy_row ++;
416 		}
417 		if (proxy_row < 0) {
418 			gint proxy_n_rows;
419 			proxy_row = abs_row;
420 			if (proxy->priv->add_null_entry)
421 				proxy_row ++;
422 			proxy_n_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
423 			if ((proxy_row >= proxy_n_rows) && (proxy_n_rows >= 0))
424 				proxy_row = -1;
425 		}
426 	}
427 
428 	return proxy_row;
429 }
430 
431 
432 /*
433  * Free a RowModif structure
434  *
435  * Warning: only the allocated RowModif is freed, it's not removed from any list or hash table.
436  */
437 static void
row_modifs_free(RowModif * rm)438 row_modifs_free (RowModif *rm)
439 {
440 	GSList *list;
441 	gint i;
442 
443 	list = rm->modify_values;
444 	while (list) {
445 		if (ROW_VALUE (list->data)->value)
446 			gda_value_free (ROW_VALUE (list->data)->value);
447 		g_free (list->data);
448 		list = g_slist_next (list);
449 	}
450 	g_slist_free (rm->modify_values);
451 
452 	if (rm->orig_values) {
453 		for (i = 0; i < rm->orig_values_size; i++) {
454 			if (rm->orig_values [i])
455 				gda_value_free (rm->orig_values [i]);
456 		}
457 		g_free (rm->orig_values);
458 	}
459 
460 	g_free (rm);
461 }
462 
463 /*
464  * Allocates a new RowModif
465  *
466  * WARNING: the new RowModif is not inserted in any list or hash table.
467  */
468 static RowModif *
row_modifs_new(GdaDataProxy * proxy,gint proxy_row)469 row_modifs_new (GdaDataProxy *proxy, gint proxy_row)
470 {
471 	RowModif *rm;
472 
473 #ifdef GDA_DEBUG
474 	rm = proxy_row_to_row_modif (proxy, proxy_row);
475 	if (rm)
476 		g_warning ("%s(): RowModif already exists for that proxy_row", __FUNCTION__);
477 #endif
478 
479 	rm = g_new0 (RowModif, 1);
480 	if (proxy_row >= 0) {
481 		gint i, model_row;
482 
483 		rm->orig_values = g_new0 (GValue *, proxy->priv->model_nb_cols);
484 		rm->orig_values_size = proxy->priv->model_nb_cols;
485 		model_row = proxy_row_to_model_row (proxy, proxy_row);
486 
487 		if (model_row >= 0) {
488 			for (i=0; i<proxy->priv->model_nb_cols; i++) {
489 				const GValue *oval;
490 
491 				oval = gda_data_model_get_value_at (proxy->priv->model, i, model_row, NULL);
492 				if (oval)
493 					rm->orig_values [i] = gda_value_copy ((GValue *) oval);
494 			}
495 		}
496 	}
497 
498 	return rm;
499 }
500 
501 /* module error */
gda_data_proxy_error_quark(void)502 GQuark gda_data_proxy_error_quark (void)
503 {
504 	static GQuark quark;
505 	if (!quark)
506 		quark = g_quark_from_static_string ("gda_data_proxy_error");
507 	return quark;
508 }
509 
510 GType
gda_data_proxy_get_type(void)511 gda_data_proxy_get_type (void)
512 {
513 	static GType type = 0;
514 
515 	if (G_UNLIKELY (type == 0)) {
516 		static GMutex registering;
517 		static const GTypeInfo info = {
518 			sizeof (GdaDataProxyClass),
519 			(GBaseInitFunc) NULL,
520 			(GBaseFinalizeFunc) NULL,
521 			(GClassInitFunc) gda_data_proxy_class_init,
522 			NULL,
523 			NULL,
524 			sizeof (GdaDataProxy),
525 			0,
526 			(GInstanceInitFunc) gda_data_proxy_init,
527 			0
528 		};
529 
530 		static const GInterfaceInfo data_model_info = {
531 			(GInterfaceInitFunc) gda_data_proxy_data_model_init,
532 			NULL,
533 			NULL
534 		};
535 
536 		g_mutex_lock (&registering);
537 		if (type == 0) {
538 			type = g_type_register_static (G_TYPE_OBJECT, "GdaDataProxy", &info, 0);
539 			g_type_add_interface_static (type, GDA_TYPE_DATA_MODEL, &data_model_info);
540 		}
541 		g_mutex_unlock (&registering);
542 	}
543 	return type;
544 }
545 
546 static gboolean
validate_row_changes_accumulator(G_GNUC_UNUSED GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,G_GNUC_UNUSED gpointer data)547 validate_row_changes_accumulator (G_GNUC_UNUSED GSignalInvocationHint *ihint,
548 				  GValue *return_accu,
549 				  const GValue *handler_return,
550 				  G_GNUC_UNUSED gpointer data)
551 {
552 	GError *error;
553 
554         error = g_value_get_boxed (handler_return);
555         g_value_set_boxed (return_accu, error);
556 
557         return error ? FALSE : TRUE; /* stop signal if 'thisvalue' is FALSE */
558 }
559 
560 static GError *
m_validate_row_changes(G_GNUC_UNUSED GdaDataProxy * proxy,G_GNUC_UNUSED gint row,G_GNUC_UNUSED gint proxied_row)561 m_validate_row_changes (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint row,
562 			G_GNUC_UNUSED gint proxied_row)
563 {
564         return NULL; /* defaults allows changes */
565 }
566 
567 static void
gda_data_proxy_class_init(GdaDataProxyClass * klass)568 gda_data_proxy_class_init (GdaDataProxyClass *klass)
569 {
570 	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
571 
572 	parent_class = g_type_class_peek_parent (klass);
573 
574 	/* signals */
575 	/**
576 	 * GdaDataProxy::row-delete-changed:
577 	 * @proxy: the #GdaDataProxy
578 	 * @row: the concerned @proxy's row
579 	 * @to_be_deleted: tells if the @row is marked to be deleted
580 	 *
581 	 * Gets emitted whenever a row has been marked to be deleted, or has been unmarked to be deleted
582 	 */
583 	gda_data_proxy_signals [ROW_DELETE_CHANGED] =
584 		g_signal_new ("row-delete-changed",
585                               G_TYPE_FROM_CLASS (object_class),
586                               G_SIGNAL_RUN_FIRST,
587                               G_STRUCT_OFFSET (GdaDataProxyClass, row_delete_changed),
588                               NULL, NULL,
589 			      _gda_marshal_VOID__INT_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BOOLEAN);
590 	/**
591 	 * GdaDataProxy::sample-size-changed:
592 	 * @proxy: the #GdaDataProxy
593 	 * @sample_size: the new sample size
594 	 *
595 	 * Gets emitted whenever @proxy's sample size has been changed
596 	 */
597 	gda_data_proxy_signals [SAMPLE_SIZE_CHANGED] =
598 		g_signal_new ("sample-size-changed",
599                               G_TYPE_FROM_CLASS (object_class),
600                               G_SIGNAL_RUN_FIRST,
601                               G_STRUCT_OFFSET (GdaDataProxyClass, sample_size_changed),
602                               NULL, NULL,
603 			      g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
604 	/**
605 	 * GdaDataProxy::sample-changed:
606 	 * @proxy: the #GdaDataProxy
607 	 * @sample_start: the first row of the sample
608 	 * @sample_end: the last row of the sample
609 	 *
610 	 * Gets emitted whenever @proxy's sample size has been changed. @sample_start and @sample_end are
611 	 * in reference to the proxied data model.
612 	 */
613 	gda_data_proxy_signals [SAMPLE_CHANGED] =
614 		g_signal_new ("sample-changed",
615                               G_TYPE_FROM_CLASS (object_class),
616                               G_SIGNAL_RUN_FIRST,
617                               G_STRUCT_OFFSET (GdaDataProxyClass, sample_changed),
618                               NULL, NULL,
619 			      _gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
620 	/**
621 	 * GdaDataProxy::validate-row-changes:
622 	 * @proxy: the #GdaDataProxy
623 	 * @row: the proxy's row
624 	 * @proxied_row: the proxied data model's row
625 	 *
626 	 * Gets emitted when @proxy is about to commit a row change to the proxied data model. If any
627 	 * callback returns a non %NULL value, then the change commit fails with the returned #GError
628 	 *
629 	 * Returns: a new #GError if validation failed, or %NULL
630 	 */
631 	gda_data_proxy_signals [VALIDATE_ROW_CHANGES] =
632 		g_signal_new ("validate-row-changes",
633                               G_TYPE_FROM_CLASS (object_class),
634                               G_SIGNAL_RUN_LAST,
635                               G_STRUCT_OFFSET (GdaDataProxyClass, validate_row_changes),
636                               validate_row_changes_accumulator, NULL,
637                               _gda_marshal_ERROR__INT_INT, G_TYPE_ERROR, 2, G_TYPE_INT, G_TYPE_INT);
638 	/**
639 	 * GdaDataProxy::row-changes-applied:
640 	 * @proxy: the #GdaDataProxy
641 	 * @row: the proxy's row
642 	 * @proxied_row: the proxied data model's row
643 	 *
644 	 * Gets emitted when @proxy has committed a row change to the proxied data model.
645 	 */
646 	gda_data_proxy_signals [ROW_CHANGES_APPLIED] =
647 		g_signal_new ("row-changes-applied",
648                               G_TYPE_FROM_CLASS (object_class),
649                               G_SIGNAL_RUN_FIRST,
650                               G_STRUCT_OFFSET (GdaDataProxyClass, row_changes_applied),
651                               NULL, NULL,
652 			      _gda_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
653 	/**
654 	 * GdaDataProxy::filter-changed:
655 	 * @proxy: the #GdaDataProxy
656 	 *
657 	 * Gets emitted when @proxy's filter has been changed
658 	 */
659 	gda_data_proxy_signals [FILTER_CHANGED] =
660 		g_signal_new ("filter-changed",
661                               G_TYPE_FROM_CLASS (object_class),
662                               G_SIGNAL_RUN_FIRST,
663 			      G_STRUCT_OFFSET (GdaDataProxyClass, filter_changed),
664                               NULL, NULL,
665 			      _gda_marshal_VOID__VOID, G_TYPE_NONE, 0);
666 
667 	klass->row_delete_changed = NULL;
668 	klass->sample_size_changed = NULL;
669 	klass->sample_changed = NULL;
670 	klass->validate_row_changes = m_validate_row_changes;
671 	klass->row_changes_applied = NULL;
672 	klass->filter_changed = NULL;
673 
674 	/* virtual functions */
675 	object_class->dispose = gda_data_proxy_dispose;
676 	object_class->finalize = gda_data_proxy_finalize;
677 
678 	/* Properties */
679 	object_class->set_property = gda_data_proxy_set_property;
680 	object_class->get_property = gda_data_proxy_get_property;
681 
682 	g_object_class_install_property (object_class, PROP_MODEL,
683 					 g_param_spec_object ("model", NULL, "Proxied data model",
684                                                                GDA_TYPE_DATA_MODEL,
685 							       (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
686 	g_object_class_install_property (object_class, PROP_ADD_NULL_ENTRY,
687 					 g_param_spec_boolean ("prepend-null-entry", NULL,
688 							       "Tells if a row composed of NULL values is inserted "
689 							       "as the proxy's first row", FALSE,
690 							       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
691 	g_object_class_install_property (object_class, PROP_DEFER_SYNC,
692 					 g_param_spec_boolean ("defer-sync", NULL,
693 							       "Tells if changes to the sample of rows displayed "
694 							       "is done in background in several steps or if it's "
695 							       "done in one step.", TRUE,
696 							       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
697 	g_object_class_install_property (object_class, PROP_SAMPLE_SIZE,
698 					 g_param_spec_int ("sample-size", NULL,
699 							   "Number of rows which the proxy will contain at any time, "
700 							   "like a sliding window on the proxied data model",
701 							   0, G_MAXINT - 1, 300,
702 							   (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)));
703 
704 	/**
705 	 * GdaDataProxy:cache-changes:
706 	 *
707 	 * Defines how changes kept in the data proxy are handled when the proxied data model
708 	 * is changed (using the "model" property). The default is to silently discard all the
709 	 * changes, but if this property is set to %TRUE, then the changes are cached.
710 	 *
711 	 * If set to %TRUE, each cached change will be re-applied to a newly set proxied data model if
712 	 * the change's number of columns match the proxied data model's number of columns and based on:
713 	 * <itemizedlist>
714 	 *   <listitem><para>the contents of the proxied data model's modified row for updates and deletes</para></listitem>
715 	 *   <listitem><para>the inserts are always kept</para></listitem>
716 	 * </itemizedlist>
717 	 *
718 	 * Since: 5.2
719 	 **/
720 	g_object_class_install_property (object_class, PROP_CACHE_CHANGES,
721 					 g_param_spec_boolean ("cache-changes", NULL,
722 							       "set to TRUE to keep track of changes even when the proxied data model is changed", FALSE,
723 							       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
724 
725 	g_mutex_lock (&parser_mutex);
726 	internal_parser = gda_sql_parser_new ();
727 	g_mutex_unlock (&parser_mutex);
728 }
729 
730 static void
gda_data_proxy_data_model_init(GdaDataModelIface * iface)731 gda_data_proxy_data_model_init (GdaDataModelIface *iface)
732 {
733 	iface->i_get_n_rows = gda_data_proxy_get_n_rows;
734 	iface->i_get_n_columns = gda_data_proxy_get_n_columns;
735 	iface->i_describe_column = gda_data_proxy_describe_column;
736 	iface->i_get_access_flags = gda_data_proxy_get_access_flags;
737 	iface->i_get_value_at = gda_data_proxy_get_value_at;
738 	iface->i_get_attributes_at = gda_data_proxy_get_attributes_at;
739 
740 	iface->i_create_iter = NULL;
741 	iface->i_iter_at_row = NULL;
742 	iface->i_iter_next = NULL;
743 	iface->i_iter_prev = NULL;
744 
745 	iface->i_set_value_at = gda_data_proxy_set_value_at;
746 	iface->i_iter_set_value = NULL;
747 	iface->i_set_values = gda_data_proxy_set_values;
748         iface->i_append_values = gda_data_proxy_append_values;
749 	iface->i_append_row = gda_data_proxy_append_row;
750 	iface->i_remove_row = gda_data_proxy_remove_row;
751 	iface->i_find_row = gda_data_proxy_find_row_from_values;
752 
753 	iface->i_set_notify = gda_data_proxy_set_notify;
754 	iface->i_get_notify = gda_data_proxy_get_notify;
755 	iface->i_send_hint = gda_data_proxy_send_hint;
756 
757 	iface->row_inserted = NULL;
758 	iface->row_updated = NULL;
759 	iface->row_removed = NULL;
760 }
761 
762 /*
763  * REM: @add_null_entry, @defer_sync and @cache_changes are not defined
764  */
765 static void
do_init(GdaDataProxy * proxy)766 do_init (GdaDataProxy *proxy)
767 {
768 	if (!proxy->priv->mutex)
769 		proxy->priv->mutex = gda_mutex_new ();
770 
771 	proxy->priv->modify_rows = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
772 	proxy->priv->notify_changes = TRUE;
773 
774 	proxy->priv->force_direct_mapping = FALSE;
775 	proxy->priv->sample_size = 0;
776 	proxy->priv->chunk = NULL;
777 	proxy->priv->chunk_to = NULL;
778 	proxy->priv->chunk_sync_idle_id = 0;
779 	proxy->priv->columns = NULL;
780 
781 	proxy->priv->defer_proxied_model_insert = FALSE;
782 	proxy->priv->catched_inserted_row = -1;
783 }
784 
785 static void
gda_data_proxy_init(GdaDataProxy * proxy)786 gda_data_proxy_init (GdaDataProxy *proxy)
787 {
788 	proxy->priv = g_new0 (GdaDataProxyPrivate, 1);
789 
790 	do_init (proxy);
791 
792 	proxy->priv->add_null_entry = FALSE;
793 	proxy->priv->defer_sync = TRUE;
794 	proxy->priv->cache_changes = FALSE;
795 }
796 
797 static DisplayChunk *compute_display_chunk (GdaDataProxy *proxy);
798 static void adjust_displayed_chunk (GdaDataProxy *proxy);
799 static gboolean chunk_sync_idle (GdaDataProxy *proxy);
800 static void ensure_chunk_sync (GdaDataProxy *proxy);
801 
802 
803 static void proxied_model_row_inserted_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
804 static void proxied_model_row_updated_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
805 static void proxied_model_row_removed_cb (GdaDataModel *model, gint row, GdaDataProxy *proxy);
806 static void proxied_model_reset_cb (GdaDataModel *model, GdaDataProxy *proxy);
807 static void proxied_model_access_changed_cb (GdaDataModel *model, GdaDataProxy *proxy);
808 
809 
810 /**
811  * gda_data_proxy_new:
812  * @model: (transfer none): Data model to be proxied
813  *
814  * Creates a new proxy for @model. For bindings use @gda_data_proxy_new_with_data_model.
815  *
816  * Returns: (transfer full): a new #GdaDataProxy object
817  */
818 GObject *
gda_data_proxy_new(GdaDataModel * model)819 gda_data_proxy_new (GdaDataModel *model)
820 {
821 	GObject *obj;
822 
823 	g_return_val_if_fail (model && GDA_IS_DATA_MODEL (model), NULL);
824 
825 	obj = g_object_new (GDA_TYPE_DATA_PROXY, "model", model, NULL);
826 
827 	return obj;
828 }
829 
830 /**
831  * gda_data_proxy_new_with_data_model:
832  * @model: (transfer none): Data model to be proxied
833  *
834  * Creates a new proxy for @model. This is the preferred method to create
835  * #GdaDataProxy objects by bindings.
836  *
837  * Returns: (transfer full): a new #GdaDataProxy object
838  *
839  * Since: 5.2.0
840  */
841 GdaDataProxy*
gda_data_proxy_new_with_data_model(GdaDataModel * model)842 gda_data_proxy_new_with_data_model (GdaDataModel *model)
843 {
844 	GObject *obj;
845 
846 	g_return_val_if_fail (model && GDA_IS_DATA_MODEL (model), NULL);
847 
848 	obj = g_object_new (GDA_TYPE_DATA_PROXY, "model", model, NULL);
849 
850 	return (GdaDataProxy*) obj;
851 }
852 
853 static void
clean_proxy(GdaDataProxy * proxy)854 clean_proxy (GdaDataProxy *proxy)
855 {
856 	if (proxy->priv->all_modifs) {
857 		gda_data_proxy_cancel_all_changes (proxy);
858 		g_assert (! proxy->priv->all_modifs);
859 	}
860 
861 	if (proxy->priv->modify_rows) {
862 		g_hash_table_destroy (proxy->priv->modify_rows);
863 		proxy->priv->modify_rows = NULL;
864 	}
865 
866 	if (proxy->priv->filter_vcnc) {
867 		g_object_unref (proxy->priv->filter_vcnc);
868 		proxy->priv->filter_vcnc = NULL;
869 	}
870 
871 	if (proxy->priv->filter_expr) {
872 		g_free (proxy->priv->filter_expr);
873 		proxy->priv->filter_expr = NULL;
874 	}
875 
876 	if (proxy->priv->filter_stmt) {
877 		g_object_unref (proxy->priv->filter_stmt);
878 		proxy->priv->filter_stmt = NULL;
879 	}
880 
881 	if (proxy->priv->filtered_rows) {
882 		g_object_unref (proxy->priv->filtered_rows);
883 		proxy->priv->filtered_rows = NULL;
884 	}
885 
886 	proxy->priv->force_direct_mapping = FALSE;
887 	if (proxy->priv->chunk_sync_idle_id) {
888 		g_idle_remove_by_data (proxy);
889 		proxy->priv->chunk_sync_idle_id = 0;
890 	}
891 
892 	if (proxy->priv->chunk) {
893 		display_chunk_free (proxy->priv->chunk);
894 		proxy->priv->chunk = NULL;
895 	}
896 	if (proxy->priv->chunk_to) {
897 		display_chunk_free (proxy->priv->chunk_to);
898 		proxy->priv->chunk_to = NULL;
899 	}
900 
901 	if (proxy->priv->columns) {
902 		gint i;
903 		for (i = 0; i < 2 * proxy->priv->model_nb_cols; i++)
904 			g_object_unref (G_OBJECT (proxy->priv->columns[i]));
905 		g_free (proxy->priv->columns);
906 		proxy->priv->columns = NULL;
907 	}
908 
909 	if (proxy->priv->model) {
910 		g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
911 						      G_CALLBACK (proxied_model_row_inserted_cb), proxy);
912 		g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
913 						      G_CALLBACK (proxied_model_row_updated_cb), proxy);
914 		g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
915 						      G_CALLBACK (proxied_model_row_removed_cb), proxy);
916 		g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
917 						      G_CALLBACK (proxied_model_reset_cb), proxy);
918 		g_signal_handlers_disconnect_by_func (G_OBJECT (proxy->priv->model),
919 						      G_CALLBACK (proxied_model_access_changed_cb), proxy);
920 		g_object_unref (proxy->priv->model);
921 		proxy->priv->model = NULL;
922 	}
923 
924 	if (proxy->priv->columns_attrs) {
925 		g_free (proxy->priv->columns_attrs);
926 		proxy->priv->columns_attrs = NULL;
927 	}
928 }
929 
930 static void
gda_data_proxy_dispose(GObject * object)931 gda_data_proxy_dispose (GObject *object)
932 {
933 	GdaDataProxy *proxy;
934 
935 	g_return_if_fail (GDA_IS_DATA_PROXY (object));
936 
937 	proxy = GDA_DATA_PROXY (object);
938 	if (proxy->priv) {
939 		clean_proxy (proxy);
940 		if (proxy->priv->mutex) {
941 			gda_mutex_free (proxy->priv->mutex);
942 			proxy->priv->mutex = NULL;
943 		}
944 
945 		clean_cached_changes (proxy);
946 	}
947 
948 	/* parent class */
949 	parent_class->dispose (object);
950 }
951 
952 static void
gda_data_proxy_finalize(GObject * object)953 gda_data_proxy_finalize (GObject *object)
954 {
955 	GdaDataProxy *proxy;
956 
957 	g_return_if_fail (object != NULL);
958 	g_return_if_fail (GDA_IS_DATA_PROXY (object));
959 
960 	proxy = GDA_DATA_PROXY (object);
961 	if (proxy->priv) {
962 		g_free (proxy->priv);
963 		proxy->priv = NULL;
964 	}
965 
966 	/* parent class */
967 	parent_class->finalize (object);
968 }
969 
970 static void
gda_data_proxy_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)971 gda_data_proxy_set_property (GObject *object,
972 			     guint param_id,
973 			     const GValue *value,
974 			     GParamSpec *pspec)
975 {
976 	GdaDataProxy *proxy;
977 
978 	proxy = GDA_DATA_PROXY (object);
979 	if (proxy->priv) {
980 		gda_mutex_lock (proxy->priv->mutex);
981 		switch (param_id) {
982 		case PROP_MODEL: {
983 			GdaDataModel *model;
984 			gint col;
985 			gboolean already_set = FALSE;
986 
987 			if (proxy->priv->model) {
988 				if (proxy->priv->cache_changes)
989 					migrate_current_changes_to_cache (proxy);
990 
991 				gboolean notify_changes;
992 				notify_changes = proxy->priv->notify_changes;
993 
994 				proxy->priv->notify_changes = FALSE;
995 				clean_proxy (proxy);
996 				proxy->priv->notify_changes = notify_changes;
997 
998 				do_init (proxy);
999 				already_set = TRUE;
1000 			}
1001 
1002 			model = (GdaDataModel*) g_value_get_object (value);
1003 			g_return_if_fail (GDA_IS_DATA_MODEL (model));
1004 
1005 			if (! (gda_data_model_get_access_flags (model) & GDA_DATA_MODEL_ACCESS_RANDOM)) {
1006 				g_warning (_("GdaDataProxy can't handle non random access data models"));
1007 				gda_mutex_unlock (proxy->priv->mutex);
1008 				return;
1009 			}
1010 			proxy->priv->model = g_object_ref (model);
1011 
1012 			proxy->priv->model_nb_cols = gda_data_model_get_n_columns (model);
1013 			proxy->priv->model_nb_rows = gda_data_model_get_n_rows (model);
1014 
1015 			/* column attributes */
1016 			proxy->priv->columns_attrs = g_new0 (GdaValueAttribute, proxy->priv->model_nb_cols);
1017 			for (col = 0; col < proxy->priv->model_nb_cols; col++) {
1018 				GdaColumn *column;
1019 				GdaValueAttribute flags = GDA_VALUE_ATTR_IS_UNCHANGED;
1020 
1021 				column = gda_data_model_describe_column (model, col);
1022 				if (gda_column_get_allow_null (column))
1023 					flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
1024 				if (gda_column_get_default_value (column))
1025 					flags |= GDA_VALUE_ATTR_CAN_BE_DEFAULT;
1026 				proxy->priv->columns_attrs[col] = flags;
1027 			}
1028 
1029 			g_signal_connect (G_OBJECT (model), "row-inserted",
1030 					  G_CALLBACK (proxied_model_row_inserted_cb), proxy);
1031 			g_signal_connect (G_OBJECT (model), "row-updated",
1032 					  G_CALLBACK (proxied_model_row_updated_cb), proxy);
1033 			g_signal_connect (G_OBJECT (model), "row-removed",
1034 					  G_CALLBACK (proxied_model_row_removed_cb), proxy);
1035 			g_signal_connect (G_OBJECT (model), "reset",
1036 					  G_CALLBACK (proxied_model_reset_cb), proxy);
1037 			g_signal_connect (G_OBJECT (model), "access-changed",
1038 					  G_CALLBACK (proxied_model_access_changed_cb), proxy);
1039 
1040 			/* initial chunk settings, no need to emit any signal as it's an initial state */
1041 			proxy->priv->chunk = compute_display_chunk (proxy);
1042 			if (!proxy->priv->chunk->mapping) {
1043 				display_chunk_free (proxy->priv->chunk);
1044 				proxy->priv->chunk = NULL;
1045 			}
1046 
1047 			if (proxy->priv->cache_changes)
1048 				fetch_current_cached_changes (proxy);
1049 
1050 			if (already_set)
1051 				gda_data_model_reset (GDA_DATA_MODEL (proxy));
1052 			break;
1053 		}
1054 		case PROP_ADD_NULL_ENTRY:
1055 			if (proxy->priv->add_null_entry != g_value_get_boolean (value)) {
1056 				proxy->priv->add_null_entry = g_value_get_boolean (value);
1057 
1058 				if (proxy->priv->add_null_entry)
1059 					gda_data_model_row_inserted ((GdaDataModel *) proxy, 0);
1060 				else
1061 					gda_data_model_row_removed ((GdaDataModel *) proxy, 0);
1062 			}
1063 			break;
1064 		case PROP_DEFER_SYNC:
1065 			proxy->priv->defer_sync = g_value_get_boolean (value);
1066 			if (!proxy->priv->defer_sync && proxy->priv->chunk_sync_idle_id) {
1067 				g_idle_remove_by_data (proxy);
1068 				proxy->priv->chunk_sync_idle_id = 0;
1069 				chunk_sync_idle (proxy);
1070 			}
1071 			break;
1072 		case PROP_SAMPLE_SIZE:
1073 			proxy->priv->sample_size = g_value_get_int (value);
1074 			if (proxy->priv->sample_size < 0)
1075 				proxy->priv->sample_size = 0;
1076 
1077 			/* initial chunk settings, no need to emit any signal as it's an initial state */
1078 			proxy->priv->chunk = compute_display_chunk (proxy);
1079 			if (!proxy->priv->chunk->mapping) {
1080 				display_chunk_free (proxy->priv->chunk);
1081 				proxy->priv->chunk = NULL;
1082 			}
1083 			break;
1084 		case PROP_CACHE_CHANGES:
1085 			proxy->priv->cache_changes = g_value_get_boolean (value);
1086 			if (! proxy->priv->cache_changes)
1087 				clean_cached_changes (proxy);
1088 			break;
1089 		default:
1090 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1091 			break;
1092 		}
1093 		gda_mutex_unlock (proxy->priv->mutex);
1094 	}
1095 }
1096 
1097 static void
gda_data_proxy_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)1098 gda_data_proxy_get_property (GObject *object,
1099 			     guint param_id,
1100 			     GValue *value,
1101 			     GParamSpec *pspec)
1102 {
1103 	GdaDataProxy *proxy;
1104 
1105 	proxy = GDA_DATA_PROXY (object);
1106 	if (proxy->priv) {
1107 		gda_mutex_lock (proxy->priv->mutex);
1108 		switch (param_id) {
1109 		case PROP_ADD_NULL_ENTRY:
1110 			g_value_set_boolean (value, proxy->priv->add_null_entry);
1111 			break;
1112 		case PROP_DEFER_SYNC:
1113 			g_value_set_boolean (value, proxy->priv->defer_sync);
1114 			break;
1115 		case PROP_SAMPLE_SIZE:
1116 			g_value_set_int (value, proxy->priv->sample_size);
1117 			break;
1118 		case PROP_CACHE_CHANGES:
1119 			g_value_set_boolean (value, proxy->priv->cache_changes);
1120 			break;
1121 		default:
1122 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1123 			break;
1124 		}
1125 		gda_mutex_unlock (proxy->priv->mutex);
1126 	}
1127 }
1128 
1129 static void
proxied_model_row_inserted_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1130 proxied_model_row_inserted_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1131 {
1132 	gint abs_row;
1133 	gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
1134 	abs_row = row; /* can't call model_row_to_absolute_row because @row is not *officially* part of the computations */
1135 
1136 	/* internal cleanups: update chunk and chunk_to arrays */
1137 	if (proxy->priv->chunk) {
1138 		gsize i;
1139 		gint *v;
1140 
1141 		for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1142 			v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1143 			if (*v >= abs_row)
1144 				*v += 1;
1145 		}
1146 	}
1147 	if (proxy->priv->chunk_to && proxy->priv->chunk->mapping) {
1148 		gsize i;
1149 		gint *v;
1150 
1151 		for (i = 0; i < proxy->priv->chunk_to->mapping->len; i++) {
1152 			v = &g_array_index (proxy->priv->chunk_to->mapping, gint, i);
1153 			if (*v >= abs_row)
1154 				*v -= 1;
1155 		}
1156 	}
1157 
1158 	/* update all the RowModif where model_row > row */
1159 	if (proxy->priv->all_modifs) {
1160 		GSList *list;
1161 		for (list = proxy->priv->all_modifs; list; list = list->next) {
1162 			RowModif *tmprm;
1163 			tmprm = ROW_MODIF (list->data);
1164 			if (tmprm->model_row > row) {
1165 				gint tmp;
1166 				tmp = tmprm->model_row;
1167 				g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1168 				tmprm->model_row ++;
1169 
1170 				gint *ptr;
1171 				ptr = g_new (gint, 1);
1172 				*ptr = tmprm->model_row;
1173 				g_hash_table_insert (proxy->priv->modify_rows, ptr, tmprm);
1174 			}
1175 		}
1176 	}
1177 
1178 	/* Note: if there is a chunk, then the new row will *not* be part of that chunk and so
1179 	 * no signal will be emitted for its insertion */
1180 	proxy->priv->model_nb_rows ++;
1181 	if (proxy->priv->defer_proxied_model_insert)
1182 		proxy->priv->catched_inserted_row = row;
1183 	else if (!proxy->priv->chunk && !proxy->priv->chunk_to)
1184 		gda_data_model_row_inserted ((GdaDataModel *) proxy, row + signal_row_offset);
1185 }
1186 
1187 static void
proxied_model_row_updated_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1188 proxied_model_row_updated_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1189 {
1190 	gint proxy_row, tmp;
1191 	RowModif *rm;
1192 
1193 	/* destroy any RowModif associated ro @row */
1194 	tmp = row;
1195 	rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
1196 	if (rm) {
1197 		/* FIXME: compare with the new value of the updated row and remove RowModif only if there
1198 		 * are no more differences. For now we only get rid of that RowModif.
1199 		 */
1200 		g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1201 		proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1202 		row_modifs_free (rm);
1203 	}
1204 
1205 	/* if @row is a "visible" row, then emit the updated signal on it */
1206 	proxy_row = absolute_row_to_proxy_row (proxy, model_row_to_absolute_row (proxy, row));
1207 	if (proxy_row >= 0)
1208 		gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1209 }
1210 
1211 static void
proxied_model_row_removed_cb(G_GNUC_UNUSED GdaDataModel * model,gint row,GdaDataProxy * proxy)1212 proxied_model_row_removed_cb (G_GNUC_UNUSED GdaDataModel *model, gint row, GdaDataProxy *proxy)
1213 {
1214 	gint proxy_row, abs_row;
1215 	RowModif *rm;
1216 	gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
1217 	abs_row = model_row_to_absolute_row (proxy, row);
1218 	proxy_row = absolute_row_to_proxy_row (proxy, abs_row);
1219 
1220 	/* internal cleanups: update chunk and chunk_to arrays */
1221 	if (proxy->priv->chunk) {
1222 		gsize i;
1223 		gint *v, remove_index = -1;
1224 
1225 		for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1226 			v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1227 			if (*v > abs_row)
1228 				*v -= 1;
1229 			else if (*v == abs_row) {
1230 				g_assert (remove_index == -1);
1231 				remove_index = i;
1232 			}
1233 		}
1234 		if (remove_index >= 0)
1235 			g_array_remove_index (proxy->priv->chunk->mapping, remove_index);
1236 		if ((proxy_row >= 0) && (proxy->priv->chunk_sep >= (proxy_row - signal_row_offset)))
1237 			proxy->priv->chunk_sep--;
1238 	}
1239 	if (proxy->priv->chunk_to && proxy->priv->chunk->mapping) {
1240 		guint i;
1241 		gint *v, remove_index = -1;
1242 
1243 		for (i = 0; i < proxy->priv->chunk_to->mapping->len; i++) {
1244 			v = &g_array_index (proxy->priv->chunk_to->mapping, gint, i);
1245 			if (*v > abs_row)
1246 				*v -= 1;
1247 			else if (*v == abs_row) {
1248 				g_assert (remove_index == -1);
1249 				remove_index = i;
1250 			}
1251 		}
1252 		if (remove_index >= 0)
1253 			g_array_remove_index (proxy->priv->chunk_to->mapping, remove_index);
1254 	}
1255 	proxy->priv->chunk_proxy_nb_rows--;
1256 	proxy->priv->model_nb_rows --;
1257 
1258 	/* destroy any RowModif associated ro @row */
1259 	gint tmp;
1260 	tmp = row;
1261 	rm = g_hash_table_lookup (proxy->priv->modify_rows, &tmp);
1262 	if (rm) {
1263 		g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1264 		proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1265 		row_modifs_free (rm);
1266 	}
1267 
1268 	/* update all the RowModif where model_row > row */
1269 	if (proxy->priv->all_modifs) {
1270 		GSList *list;
1271 		for (list = proxy->priv->all_modifs; list; list = list->next) {
1272 			RowModif *tmprm;
1273 			tmprm = ROW_MODIF (list->data);
1274 			if (tmprm->model_row > row) {
1275 				tmp = tmprm->model_row;
1276 				g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1277 				tmprm->model_row --;
1278 
1279 				gint *ptr;
1280 				ptr = g_new (gint, 1);
1281 				*ptr = tmprm->model_row;
1282 				g_hash_table_insert (proxy->priv->modify_rows, ptr, tmprm);
1283 			}
1284 		}
1285 	}
1286 
1287 	/* actual signal emission if row is 'visible' */
1288 	if (proxy_row >= 0)
1289 		gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
1290 }
1291 
1292 /*
1293  * called when the proxied model emits a "reset" signal
1294  */
1295 static void
proxied_model_reset_cb(GdaDataModel * model,GdaDataProxy * proxy)1296 proxied_model_reset_cb (GdaDataModel *model, GdaDataProxy *proxy)
1297 {
1298 	g_object_ref (G_OBJECT (model));
1299 	clean_proxy (proxy);
1300 	do_init (proxy);
1301 	g_object_set (G_OBJECT (proxy), "model", model, NULL);
1302 	g_object_unref (G_OBJECT (model));
1303 
1304 	if (proxy->priv->columns) {
1305 		/* adjust column's types */
1306 		gint i;
1307 		GdaColumn *orig;
1308 		for (i = 0; i < proxy->priv->model_nb_cols; i++) {
1309 			orig = gda_data_model_describe_column (proxy->priv->model, i);
1310 			gda_column_set_g_type (proxy->priv->columns[i], gda_column_get_g_type (orig));
1311 		}
1312 		for (; i < 2 * proxy->priv->model_nb_cols; i++) {
1313 			orig = gda_data_model_describe_column (proxy->priv->model,
1314 							       i -  proxy->priv->model_nb_cols);
1315 			gda_column_set_g_type (proxy->priv->columns[i], gda_column_get_g_type (orig));
1316 		}
1317 	}
1318 
1319 	gda_data_model_reset (GDA_DATA_MODEL (proxy));
1320 }
1321 
1322 static void
proxied_model_access_changed_cb(G_GNUC_UNUSED GdaDataModel * model,GdaDataProxy * proxy)1323 proxied_model_access_changed_cb (G_GNUC_UNUSED GdaDataModel *model, GdaDataProxy *proxy)
1324 {
1325 	g_signal_emit_by_name (proxy, "access-changed");
1326 }
1327 
1328 /**
1329  * gda_data_proxy_get_proxied_model:
1330  * @proxy: a #GdaDataProxy object
1331  *
1332  * Fetch the #GdaDataModel which @proxy does proxy
1333  *
1334  * Returns: (transfer none): the proxied data model
1335  */
1336 GdaDataModel *
gda_data_proxy_get_proxied_model(GdaDataProxy * proxy)1337 gda_data_proxy_get_proxied_model (GdaDataProxy *proxy)
1338 {
1339 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
1340 	g_return_val_if_fail (proxy->priv, NULL);
1341 
1342 	return proxy->priv->model;
1343 }
1344 
1345 /**
1346  * gda_data_proxy_get_proxied_model_n_cols:
1347  * @proxy: a #GdaDataProxy object
1348  *
1349  * Get the number of columns in the proxied data model
1350  *
1351  * Returns: the number of columns, or -1 if an error occurred
1352  */
1353 gint
gda_data_proxy_get_proxied_model_n_cols(GdaDataProxy * proxy)1354 gda_data_proxy_get_proxied_model_n_cols (GdaDataProxy *proxy)
1355 {
1356 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1357 	g_return_val_if_fail (proxy->priv, -1);
1358 
1359 	return proxy->priv->model_nb_cols;
1360 }
1361 
1362 /**
1363  * gda_data_proxy_get_proxied_model_n_rows:
1364  * @proxy: a #GdaDataProxy object
1365  *
1366  * Get the number of rows in the proxied data model
1367  *
1368  * Returns: the number of rows, or -1 if the number of rows is not known
1369  */
1370 gint
gda_data_proxy_get_proxied_model_n_rows(GdaDataProxy * proxy)1371 gda_data_proxy_get_proxied_model_n_rows (GdaDataProxy *proxy)
1372 {
1373 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1374 	g_return_val_if_fail (proxy->priv, -1);
1375 
1376 	return gda_data_model_get_n_rows (proxy->priv->model);
1377 }
1378 
1379 /**
1380  * gda_data_proxy_is_read_only:
1381  * @proxy: a #GdaDataProxy object
1382  *
1383  * Returns: TRUE if the proxied data model is itself read-only
1384  */
1385 gboolean
gda_data_proxy_is_read_only(GdaDataProxy * proxy)1386 gda_data_proxy_is_read_only (GdaDataProxy *proxy)
1387 {
1388 	GdaDataModelAccessFlags flags;
1389 
1390 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), TRUE);
1391 	g_return_val_if_fail (proxy->priv, TRUE);
1392 
1393 	flags = gda_data_model_get_access_flags (proxy->priv->model);
1394 	return ! (flags & GDA_DATA_MODEL_ACCESS_WRITE);
1395 }
1396 
1397 
1398 static RowModif *find_or_create_row_modif (GdaDataProxy *proxy, gint proxy_row, gint col, RowValue **ret_rv);
1399 
1400 
1401 /*
1402  * Stores the new RowValue in @rv
1403  */
1404 static RowModif *
find_or_create_row_modif(GdaDataProxy * proxy,gint proxy_row,gint col,RowValue ** ret_rv)1405 find_or_create_row_modif (GdaDataProxy *proxy, gint proxy_row, gint col, RowValue **ret_rv)
1406 {
1407 	RowModif *rm = NULL;
1408 	RowValue *rv = NULL;
1409 	gint model_row;
1410 	g_assert (proxy_row >= 0);
1411 
1412 	model_row = absolute_row_to_model_row (proxy,
1413 					       proxy_row_to_absolute_row (proxy, proxy_row), &rm);
1414 	if (!rm) {
1415 		/* create a new RowModif */
1416 		g_assert (model_row >= 0);
1417 		rm = row_modifs_new (proxy, proxy_row);
1418 		rm->model_row = model_row;
1419 
1420 		gint *ptr;
1421 		ptr = g_new (gint, 1);
1422 		*ptr = model_row;
1423 		g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
1424 		proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1425 	}
1426 	else {
1427 		/* there are already some modifications to the row, try to catch the RowValue if available */
1428 		GSList *list;
1429 
1430 		list = rm->modify_values;
1431 		while (list && !rv) {
1432 			if (ROW_VALUE (list->data)->model_column == col)
1433 				rv = ROW_VALUE (list->data);
1434 			list = g_slist_next (list);
1435 		}
1436 	}
1437 
1438 	if (ret_rv)
1439 		*ret_rv = rv;
1440 	return rm;
1441 }
1442 
1443 
1444 /**
1445  * gda_data_proxy_get_values:
1446  * @proxy: a #GdaDataProxy object
1447  * @proxy_row: a proxy row
1448  * @cols_index: (array length=n_cols): array containing the columns for which the values are requested
1449  * @n_cols: size of @cols_index
1450  *
1451  * Retrieve a whole list of values from the @proxy data model. This function
1452  * calls gda_data_proxy_get_value()
1453  * for each column index specified in @cols_index, and generates a #GSList on the way.
1454  *
1455  * Returns: (element-type GValue) (transfer container): a new list of values (the list must be freed, not the values),
1456  * or %NULL if an error occurred
1457  */
1458 GSList *
gda_data_proxy_get_values(GdaDataProxy * proxy,gint proxy_row,gint * cols_index,gint n_cols)1459 gda_data_proxy_get_values (GdaDataProxy *proxy, gint proxy_row, gint *cols_index, gint n_cols)
1460 {
1461 	GSList *retval = NULL;
1462 	gint i;
1463 	const GValue *value;
1464 
1465 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
1466 	g_return_val_if_fail (proxy->priv, NULL);
1467 	g_return_val_if_fail (proxy_row >= 0, NULL);
1468 
1469 	gda_mutex_lock (proxy->priv->mutex);
1470 	for (i = 0; i < n_cols; i++) {
1471 		value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy, cols_index[i], proxy_row, NULL);
1472 		if (value)
1473 			retval = g_slist_prepend (retval, (GValue *) value);
1474 		else {
1475 			g_slist_free (retval);
1476 			gda_mutex_unlock (proxy->priv->mutex);
1477 			return NULL;
1478 		}
1479 	}
1480 	gda_mutex_unlock (proxy->priv->mutex);
1481 
1482 	return g_slist_reverse (retval);
1483 }
1484 
1485 /**
1486  * gda_data_proxy_get_value_attributes:
1487  * @proxy: a #GdaDataProxy object
1488  * @proxy_row: a proxy row
1489  * @col: a valid proxy column
1490  *
1491  * Get the attributes of the value stored at (proxy_row, col) in @proxy, which
1492  * is an ORed value of #GdaValueAttribute flags
1493  *
1494  * Returns: a #GdaValueAttribute with the value's attributes at given position
1495  */
1496 GdaValueAttribute
gda_data_proxy_get_value_attributes(GdaDataProxy * proxy,gint proxy_row,gint col)1497 gda_data_proxy_get_value_attributes (GdaDataProxy *proxy, gint proxy_row, gint col)
1498 {
1499 	gint model_row;
1500 	RowModif *rm;
1501 	gboolean value_has_modifs = FALSE;
1502 	GdaValueAttribute flags = 0;
1503 	gint model_column;
1504 
1505 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
1506 	g_return_val_if_fail (proxy->priv, 0);
1507 	g_return_val_if_fail (proxy_row >= 0, 0);
1508 
1509 	gda_mutex_lock (proxy->priv->mutex);
1510 	model_column = col % proxy->priv->model_nb_cols;
1511 	model_row = proxy_row_to_model_row (proxy, proxy_row);
1512 	flags = gda_data_model_get_attributes_at (proxy->priv->model, model_column, model_row);
1513 	if (model_row < 0)
1514 		flags |= GDA_VALUE_ATTR_IS_NULL;
1515 
1516 	rm = proxy_row_to_row_modif (proxy, proxy_row);
1517 	if (rm) {
1518 		if (rm->modify_values) {
1519 			/* there are some modifications to the row */
1520 			GSList *list;
1521 			RowValue *rv = NULL;
1522 
1523 			list = rm->modify_values;
1524 			while (list && !rv) {
1525 				if (ROW_VALUE (list->data)->model_column == model_column)
1526 					rv = ROW_VALUE (list->data);
1527 				list = g_slist_next (list);
1528 			}
1529 			if (rv) {
1530 				value_has_modifs = TRUE;
1531 				flags |= rv->attributes;
1532 				if (rv->value && !gda_value_is_null (rv->value))
1533 					flags &= ~GDA_VALUE_ATTR_IS_NULL;
1534 				else
1535 					flags |= GDA_VALUE_ATTR_IS_NULL;
1536 			}
1537 		}
1538 	}
1539 
1540 	if (! value_has_modifs)
1541 		flags |= GDA_VALUE_ATTR_IS_UNCHANGED;
1542 
1543 	/* compute the GDA_VALUE_ATTR_DATA_NON_VALID attribute */
1544 	if (! (flags & GDA_VALUE_ATTR_CAN_BE_NULL)) {
1545 		if ((flags & GDA_VALUE_ATTR_IS_NULL) && !(flags & GDA_VALUE_ATTR_IS_DEFAULT))
1546 			flags |= GDA_VALUE_ATTR_DATA_NON_VALID;
1547 	}
1548 
1549 	gda_mutex_unlock (proxy->priv->mutex);
1550 
1551 	/*g_print ("%s (%p, %d, %d) => %d\n", __FUNCTION__, proxy, col, proxy_row, flags);*/
1552 	return flags;
1553 }
1554 
1555 /**
1556  * gda_data_proxy_alter_value_attributes:
1557  * @proxy: a #GdaDataProxy object
1558  * @proxy_row: A proxy row number
1559  * @col: a valid column number
1560  * @alter_flags: (transfer none): flags to alter the attributes
1561  *
1562  * Alters the attributes of the value stored at (proxy_row, col) in @proxy. the @alter_flags
1563  * can only contain the GDA_VALUE_ATTR_IS_NULL, GDA_VALUE_ATTR_IS_DEFAULT and GDA_VALUE_ATTR_IS_UNCHANGED
1564  * flags (other flags are ignored).
1565  */
1566 void
gda_data_proxy_alter_value_attributes(GdaDataProxy * proxy,gint proxy_row,gint col,GdaValueAttribute alter_flags)1567 gda_data_proxy_alter_value_attributes (GdaDataProxy *proxy, gint proxy_row, gint col, GdaValueAttribute alter_flags)
1568 {
1569 	gint model_col;
1570 
1571 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1572 	g_return_if_fail (proxy->priv);
1573 	g_return_if_fail (proxy_row >= 0);
1574 
1575 	gda_mutex_lock (proxy->priv->mutex);
1576 
1577 	model_col = col % proxy->priv->model_nb_cols;
1578 	if (alter_flags & GDA_VALUE_ATTR_IS_NULL)
1579 		gda_data_proxy_set_value_at ((GdaDataModel*) proxy,
1580 					     model_col, proxy_row, NULL, NULL);
1581 	else {
1582 		RowModif *rm;
1583 		RowValue *rv = NULL;
1584 
1585 		rm = find_or_create_row_modif (proxy, proxy_row, model_col, &rv);
1586 		g_assert (rm);
1587 
1588 		if (alter_flags & GDA_VALUE_ATTR_IS_DEFAULT) {
1589 			GdaValueAttribute flags = 0;
1590 			if (!rv) {
1591 				/* create a new RowValue */
1592 				rv = g_new0 (RowValue, 1);
1593 				rv->row_modif = rm;
1594 				rv->model_column = model_col;
1595 				rv->attributes = proxy->priv->columns_attrs [col];
1596 				flags = rv->attributes;
1597 
1598 				rv->value = NULL;
1599 				flags &= ~GDA_VALUE_ATTR_IS_UNCHANGED;
1600 				if (rm->model_row >= 0)
1601 					flags |= GDA_VALUE_ATTR_HAS_VALUE_ORIG;
1602 				else
1603 					flags &= ~GDA_VALUE_ATTR_HAS_VALUE_ORIG;
1604 
1605 				rm->modify_values = g_slist_prepend (rm->modify_values, rv);
1606 			}
1607 			else {
1608 				flags = rv->attributes;
1609 				if (rv->value) {
1610 					gda_value_free (rv->value);
1611 					rv->value = NULL;
1612 				}
1613 			}
1614 			flags |= GDA_VALUE_ATTR_IS_DEFAULT;
1615 			rv->attributes = flags;
1616 
1617 			if (proxy->priv->notify_changes)
1618 				gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1619 		}
1620 		if (alter_flags & GDA_VALUE_ATTR_IS_UNCHANGED) {
1621 			if (!rm->orig_values)
1622 				g_warning ("Alter_Flags = GDA_VALUE_ATTR_IS_UNCHANGED, no RowValue!");
1623 			else
1624 				gda_data_proxy_set_value_at ((GdaDataModel*) proxy,
1625 							     model_col, proxy_row,
1626 							     rm->orig_values [model_col],
1627 							     NULL);
1628 		}
1629 	}
1630 
1631 	gda_mutex_unlock (proxy->priv->mutex);
1632 }
1633 
1634 /**
1635  * gda_data_proxy_get_proxied_model_row:
1636  * @proxy: a #GdaDataProxy object
1637  * @proxy_row: A proxy row number
1638  *
1639  * Get the @proxy's proxied model row corresponding to @proxy_row
1640 
1641  * Returns: the proxied model's row, or -1 if @proxy row which only exists @proxy
1642  */
1643 gint
gda_data_proxy_get_proxied_model_row(GdaDataProxy * proxy,gint proxy_row)1644 gda_data_proxy_get_proxied_model_row (GdaDataProxy *proxy, gint proxy_row)
1645 {
1646 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
1647 	g_return_val_if_fail (proxy->priv, 0);
1648 	g_return_val_if_fail (proxy_row >= 0, 0);
1649 
1650 	return proxy_row_to_model_row (proxy, proxy_row);
1651 }
1652 
1653 /**
1654  * gda_data_proxy_delete:
1655  * @proxy: a #GdaDataProxy object
1656  * @proxy_row: A proxy row number
1657  *
1658  * Marks the row @proxy_row to be deleted
1659  */
1660 void
gda_data_proxy_delete(GdaDataProxy * proxy,gint proxy_row)1661 gda_data_proxy_delete (GdaDataProxy *proxy, gint proxy_row)
1662 {
1663 	RowModif *rm = NULL;
1664 	gboolean do_signal = FALSE;
1665 	gint model_row, abs_row;
1666 
1667 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1668 	g_return_if_fail (proxy->priv);
1669 	g_return_if_fail (proxy_row >= 0);
1670 
1671 	gda_mutex_lock (proxy->priv->mutex);
1672 
1673 	/* ensure that there is no sync to be done */
1674 	ensure_chunk_sync (proxy);
1675 
1676 	if (proxy->priv->add_null_entry && proxy_row == 0) {
1677 		g_warning (_("The first row is an empty row artificially prepended and cannot be removed"));
1678 		gda_mutex_unlock (proxy->priv->mutex);
1679 		return;
1680 	}
1681 
1682 	if (! (gda_data_model_get_access_flags ((GdaDataModel*) proxy) & GDA_DATA_MODEL_ACCESS_DELETE)) {
1683 		gda_mutex_unlock (proxy->priv->mutex);
1684 		return;
1685 	}
1686 
1687 	abs_row = proxy_row_to_absolute_row (proxy, proxy_row);
1688 	model_row = absolute_row_to_model_row (proxy, abs_row, &rm);
1689 	if (rm) {
1690 		if (! rm->to_be_deleted) {
1691 			if (rm->model_row == -1) {
1692 				/* remove the row completely because it does not exist in the data model */
1693 				proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1694 				proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
1695 				row_modifs_free (rm);
1696 
1697 				if (proxy->priv->chunk) {
1698 					/* Update chunk */
1699 					gsize i;
1700 					gint *v;
1701 					gint row_cmp = proxy_row - (proxy->priv->add_null_entry ? 1 : 0);
1702 					for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1703 						v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1704 						if (*v > abs_row)
1705 							*v -= 1;
1706 					}
1707 					g_array_remove_index (proxy->priv->chunk->mapping, row_cmp);
1708 				}
1709 
1710 				if (proxy->priv->notify_changes)
1711 					gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
1712 			}
1713 			else {
1714 				rm->to_be_deleted = TRUE;
1715 				do_signal = TRUE;
1716 			}
1717 		}
1718 	}
1719 	else {
1720 		/* the row is an existing row in the data model, create a new RowModif */
1721 		rm = row_modifs_new (proxy, proxy_row);
1722 		rm->model_row = model_row;
1723 
1724 		gint *ptr;
1725 		ptr = g_new (gint, 1);
1726 		*ptr = model_row;
1727 		g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
1728 		proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1729 		rm->to_be_deleted = TRUE;
1730 		do_signal = TRUE;
1731 	}
1732 
1733 	if (do_signal && proxy->priv->notify_changes) {
1734 		gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1735 		g_signal_emit (G_OBJECT (proxy),
1736                                gda_data_proxy_signals[ROW_DELETE_CHANGED],
1737                                0, proxy_row, TRUE);
1738 	}
1739 
1740 	gda_mutex_unlock (proxy->priv->mutex);
1741 }
1742 
1743 /**
1744  * gda_data_proxy_undelete:
1745  * @proxy: a #GdaDataProxy object
1746  * @proxy_row: A proxy row number
1747  *
1748  * Remove the "to be deleted" mark at the row @proxy_row, if it existed.
1749  */
1750 void
gda_data_proxy_undelete(GdaDataProxy * proxy,gint proxy_row)1751 gda_data_proxy_undelete (GdaDataProxy *proxy, gint proxy_row)
1752 {
1753 	RowModif *rm = NULL;
1754 	gboolean do_signal = FALSE;
1755 	gint model_row;
1756 
1757 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1758 	g_return_if_fail (proxy->priv);
1759 	g_return_if_fail (proxy_row >= 0);
1760 
1761 	gda_mutex_lock (proxy->priv->mutex);
1762 
1763 	/* ensure that there is no sync to be done */
1764 	ensure_chunk_sync (proxy);
1765 
1766 	model_row = absolute_row_to_model_row (proxy,
1767 					       proxy_row_to_absolute_row (proxy, proxy_row), &rm);
1768 	if (rm) {
1769 		rm->to_be_deleted = FALSE;
1770 		if (!rm->modify_values) {
1771 			/* get rid of that RowModif */
1772 			do_signal= TRUE;
1773 
1774 			gint tmp;
1775 			tmp = model_row;
1776 			g_hash_table_remove (proxy->priv->modify_rows, &tmp);
1777 			proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1778 			row_modifs_free (rm);
1779 		}
1780 		else
1781 			do_signal= TRUE;
1782 	}
1783 
1784 	if (do_signal && proxy->priv->notify_changes) {
1785 		gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
1786 		g_signal_emit (G_OBJECT (proxy),
1787                                gda_data_proxy_signals[ROW_DELETE_CHANGED],
1788                                0, proxy_row, FALSE);
1789 	}
1790 
1791 	gda_mutex_unlock (proxy->priv->mutex);
1792 }
1793 
1794 /**
1795  * gda_data_proxy_row_is_deleted:
1796  * @proxy: a #GdaDataProxy object
1797  * @proxy_row: A proxy row number
1798  *
1799  * Tells if the row number @proxy_row is marked to be deleted.
1800  *
1801  * Returns: TRUE if the row is marked to be deleted
1802  */
1803 gboolean
gda_data_proxy_row_is_deleted(GdaDataProxy * proxy,gint proxy_row)1804 gda_data_proxy_row_is_deleted (GdaDataProxy *proxy, gint proxy_row)
1805 {
1806 	RowModif *rm;
1807 
1808 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
1809 	g_return_val_if_fail (proxy->priv, FALSE);
1810 	g_return_val_if_fail (proxy_row >= 0, FALSE);
1811 
1812 	rm = proxy_row_to_row_modif (proxy, proxy_row);
1813 	return rm && rm->to_be_deleted ? TRUE : FALSE;
1814 }
1815 
1816 /**
1817  * gda_data_proxy_row_is_inserted:
1818  * @proxy: a #GdaDataProxy object
1819  * @proxy_row: A proxy row number
1820  *
1821  * Tells if the row number @proxy_row is a row which has been inserted in @proxy
1822  * (and is thus not in the proxied data model).
1823  *
1824  * Returns: TRUE if the row is an inserted row
1825  */
1826 gboolean
gda_data_proxy_row_is_inserted(GdaDataProxy * proxy,gint proxy_row)1827 gda_data_proxy_row_is_inserted (GdaDataProxy *proxy, gint proxy_row)
1828 {
1829 	RowModif *rm;
1830 
1831 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
1832 	g_return_val_if_fail (proxy->priv, FALSE);
1833 	g_return_val_if_fail (proxy_row >= 0, FALSE);
1834 
1835 	rm = proxy_row_to_row_modif (proxy, proxy_row);
1836 	if (rm && (rm->model_row < 0))
1837 		return TRUE;
1838 	else
1839 		return FALSE;
1840 }
1841 
1842 /*
1843  * gda_data_proxy_append
1844  * @proxy: a #GdaDataProxy object
1845  *
1846  * Appends a new row to the proxy. The operation can fail if either:
1847  * <itemizedlist>
1848  * <listitem><para>The INSERT operation is not accepted by the proxied data model</para></listitem>
1849  * <listitem><para>There is an unknown number of rows in the proxy</para></listitem>
1850  * </itemizedlist>
1851  *
1852  * Returns: the proxy row number of the new row, or -1 if the row could not be appended
1853  */
1854 static gint
gda_data_proxy_append(GdaDataProxy * proxy)1855 gda_data_proxy_append (GdaDataProxy *proxy)
1856 {
1857 	RowModif *rm;
1858 	gint col;
1859 	gint proxy_row;
1860 	gint abs_row;
1861 
1862 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
1863 	g_return_val_if_fail (proxy->priv, -1);
1864 
1865 	/* ensure that there is no sync to be done */
1866 	ensure_chunk_sync (proxy);
1867 
1868 	if (! (gda_data_model_get_access_flags ((GdaDataModel *) proxy) & GDA_DATA_MODEL_ACCESS_INSERT))
1869 		return -1;
1870 	if (proxy->priv->model_nb_rows == -1)
1871 		return -1;
1872 
1873 	/* RowModif structure */
1874 	rm = row_modifs_new (proxy, -1);
1875 	rm->model_row = -1;
1876 	rm->orig_values = NULL; /* there is no original value */
1877 	rm->orig_values_size = proxy->priv->model_nb_cols;
1878 
1879 	proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
1880 	proxy->priv->new_rows = g_slist_append (proxy->priv->new_rows, rm);
1881 
1882 	/* new proxy row value */
1883 	abs_row = row_modif_to_absolute_row (proxy, rm);
1884 	if (proxy->priv->chunk) {
1885 		proxy_row = proxy->priv->chunk->mapping->len;
1886 		g_array_append_val (proxy->priv->chunk->mapping, abs_row);
1887 		if (proxy->priv->add_null_entry)
1888 			proxy_row++;
1889 	}
1890 	else
1891 		proxy_row = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy) - 1;
1892 
1893 	/* for the columns which allow a default value, set them to the default value */
1894 	for (col = 0; col < proxy->priv->model_nb_cols; col ++) {
1895 		GdaColumn *column;
1896 		const GValue *def;
1897 		RowValue *rv;
1898 		GdaValueAttribute flags = 0;
1899 
1900 		/* create a new RowValue */
1901 		rv = g_new0 (RowValue, 1);
1902 		rv->row_modif = rm;
1903 		rv->model_column = col;
1904 		rv->attributes = GDA_VALUE_ATTR_NONE;
1905 		rv->value = NULL;
1906 		rm->modify_values = g_slist_prepend (rm->modify_values, rv);
1907 
1908 		column = gda_data_model_describe_column (proxy->priv->model, col);
1909 		def = gda_column_get_default_value (column);
1910 		if (def) {
1911 			flags |= (GDA_VALUE_ATTR_IS_DEFAULT | GDA_VALUE_ATTR_CAN_BE_DEFAULT);
1912 			if (G_VALUE_TYPE (def) == gda_column_get_g_type (column))
1913 				rv->value = gda_value_copy (def);
1914 		}
1915 		if (gda_column_get_allow_null (column)) {
1916 			GdaValueAttribute attributes;
1917 
1918 			attributes = gda_data_model_get_attributes_at (proxy->priv->model, col, -1);;
1919 			if (attributes & GDA_VALUE_ATTR_CAN_BE_NULL)
1920 				flags |= GDA_VALUE_ATTR_CAN_BE_NULL;
1921 		}
1922 
1923 		if (gda_column_get_auto_increment (column))
1924 			flags |= (GDA_VALUE_ATTR_IS_DEFAULT | GDA_VALUE_ATTR_CAN_BE_DEFAULT);
1925 
1926 		rv->attributes = flags;
1927 	}
1928 
1929 	/* signal row insertion */
1930 	if (proxy->priv->notify_changes)
1931 		gda_data_model_row_inserted ((GdaDataModel *) proxy, proxy_row);
1932 
1933 	return proxy_row;
1934 }
1935 
1936 /**
1937  * gda_data_proxy_cancel_row_changes:
1938  * @proxy: a #GdaDataProxy object
1939  * @proxy_row: the row to cancel changes
1940  * @col: the column to cancel changes, or less than 0 to cancel any change on the @row row
1941  *
1942  * Resets data at the corresponding row and column. If @proxy_row corresponds to a new row, then
1943  * that new row is deleted from @proxy.
1944  */
1945 void
gda_data_proxy_cancel_row_changes(GdaDataProxy * proxy,gint proxy_row,gint col)1946 gda_data_proxy_cancel_row_changes (GdaDataProxy *proxy, gint proxy_row, gint col)
1947 {
1948 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
1949 	g_return_if_fail (proxy->priv);
1950 	g_return_if_fail (proxy_row >= 0);
1951 
1952 	gda_mutex_lock (proxy->priv->mutex);
1953 
1954 	/* ensure that there is no sync to be done */
1955 	ensure_chunk_sync (proxy);
1956 
1957 	if (((col >= 0) && (col < proxy->priv->model_nb_cols)) ||
1958 	    (col < 0)) {
1959 		RowModif *rm;
1960 		gboolean signal_update = FALSE;
1961 		gboolean signal_delete = FALSE;
1962 
1963 		rm = proxy_row_to_row_modif (proxy, proxy_row);
1964 		if (rm && rm->modify_values) {
1965 			/* there are some modifications to the row */
1966 			GSList *list;
1967 			RowValue *rv = NULL;
1968 
1969 			list = rm->modify_values;
1970 			while (list && (!rv || (col < 0))) {
1971 				if ((col < 0) || (ROW_VALUE (list->data)->model_column == col)) {
1972 					rv = ROW_VALUE (list->data);
1973 
1974 					/* remove this RowValue from the RowList */
1975 					rm->modify_values = g_slist_remove (rm->modify_values, rv);
1976 					if (!rm->modify_values && !rm->to_be_deleted) {
1977 						/* remove this RowList as well */
1978 						proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
1979 						if (rm->model_row < 0) {
1980 							if (proxy->priv->chunk) {
1981 								/* Update chunk */
1982 								gsize i;
1983 								gint *v, abs_row;
1984 								gint row_cmp = proxy_row - (proxy->priv->add_null_entry ? 1 : 0);
1985 								abs_row = proxy_row_to_absolute_row (proxy, proxy_row);
1986 								for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
1987 									v = &g_array_index (proxy->priv->chunk->mapping, gint, i);
1988 									if (*v > abs_row)
1989 										*v -= 1;
1990 								}
1991 								g_array_remove_index (proxy->priv->chunk->mapping, row_cmp);
1992 							}
1993 							signal_delete = TRUE;
1994 							proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
1995 						}
1996 						else {
1997 							gint tmp;
1998 							tmp = rm->model_row;
1999 							g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2000 						}
2001 						row_modifs_free (rm);
2002 						rm = NULL;
2003 					}
2004 					else {
2005 						signal_update = TRUE;
2006 					}
2007 					if (rm)
2008 						list = rm->modify_values;
2009 					else
2010 						list = NULL;
2011 				}
2012 				else
2013 					list = list->next;
2014 			}
2015 		}
2016 
2017 		if (proxy->priv->notify_changes) {
2018 			if (signal_delete)
2019 				gda_data_model_row_removed ((GdaDataModel *) proxy, proxy_row);
2020 			else if (signal_update)
2021 				gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
2022 		}
2023 	}
2024 	else
2025 		g_warning ("GdaDataProxy column %d is not a modifiable data column", col);
2026 
2027 	gda_mutex_unlock (proxy->priv->mutex);
2028 }
2029 
2030 static gboolean commit_row_modif (GdaDataProxy *proxy, RowModif *rm, gboolean adjust_display, GError **error);
2031 
2032 /**
2033  * gda_data_proxy_apply_row_changes:
2034  * @proxy: a #GdaDataProxy object
2035  * @proxy_row: the row number to commit
2036  * @error: place to store the error, or %NULL
2037  *
2038  * Commits the modified data in the proxy back into the #GdaDataModel.
2039  *
2040  * Returns: TRUE if no error occurred.
2041  */
2042 gboolean
gda_data_proxy_apply_row_changes(GdaDataProxy * proxy,gint proxy_row,GError ** error)2043 gda_data_proxy_apply_row_changes (GdaDataProxy *proxy, gint proxy_row, GError **error)
2044 {
2045 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2046 	g_return_val_if_fail (proxy->priv, FALSE);
2047 	g_return_val_if_fail (proxy_row >= 0, FALSE);
2048 
2049 	return commit_row_modif (proxy, proxy_row_to_row_modif (proxy, proxy_row), TRUE, error);
2050 }
2051 
2052 /*
2053  * Commits the modifications held in one single RowModif structure.
2054  *
2055  * Returns: TRUE if no error occurred
2056  */
2057 static gboolean
commit_row_modif(GdaDataProxy * proxy,RowModif * rm,gboolean adjust_display,GError ** error)2058 commit_row_modif (GdaDataProxy *proxy, RowModif *rm, gboolean adjust_display, GError **error)
2059 {
2060 	gboolean err = FALSE;
2061 	gint proxy_row, model_row;
2062 	GError *lerror = NULL;
2063 
2064 	if (!rm)
2065 		return TRUE;
2066 
2067 	gda_mutex_lock (proxy->priv->mutex);
2068 
2069 	model_row = rm->model_row;
2070 
2071 	/* ensure that there is no sync to be done */
2072 	ensure_chunk_sync (proxy);
2073 
2074 	/*
2075 	 * Steps in this procedure:
2076 	 * -1- send the "validate-row-changes" signal, and abort if return value is FALSE
2077 	 * -2- apply desired modification (which _should_ trigger "row_{inserted,removed,updated}" signals from
2078 	 *     the proxied model)
2079 	 * -3- if no error then destroy the RowModif which has just been applied
2080 	 *     and refresh displayed chunks if @adjust_display is set to TRUE
2081 	 * -4- send the "row-changes-applied" signal
2082 	 */
2083 	proxy_row = row_modif_to_proxy_row (proxy, rm);
2084 
2085 	/* validate the changes to this row */
2086         g_signal_emit (G_OBJECT (proxy),
2087                        gda_data_proxy_signals[VALIDATE_ROW_CHANGES],
2088                        0, proxy_row, rm->model_row, &lerror);
2089 	if (lerror) {
2090 		g_propagate_error (error, lerror);
2091 		gda_mutex_unlock (proxy->priv->mutex);
2092 		return FALSE;
2093 	}
2094 
2095 	/* apply the changes */
2096 	if (rm->to_be_deleted) {
2097 		/* delete the row */
2098 		g_assert (rm->model_row >= 0);
2099 		if (!gda_data_model_remove_row (proxy->priv->model, rm->model_row, error))
2100 			err = TRUE;
2101 	}
2102 	else {
2103 		if (rm->model_row >= 0) {
2104 			/* update the row */
2105 			GList *values = NULL;
2106 			gint i;
2107 
2108 			g_assert (rm->modify_values);
2109 			g_assert (rm->orig_values);
2110 			for (i=0; i < rm->orig_values_size; i++) {
2111 				gboolean newvalue_found = FALSE;
2112 				GValue *newvalue = NULL;
2113 				GSList *list;
2114 
2115 				for (list = rm->modify_values; list; list = list->next) {
2116 					if (ROW_VALUE (list->data)->model_column == i) {
2117 						newvalue_found = TRUE;
2118 						if (ROW_VALUE (list->data)->attributes &
2119 						    GDA_VALUE_ATTR_IS_DEFAULT)
2120 							newvalue = NULL;
2121 						else {
2122 							if (! ROW_VALUE (list->data)->value)
2123 								newvalue = gda_value_new_null ();
2124 							else
2125 								newvalue = gda_value_copy (ROW_VALUE (list->data)->value);
2126 						}
2127 						break;
2128 					}
2129 				}
2130 				if (!newvalue_found && rm->orig_values[i])
2131 					newvalue = gda_value_copy (rm->orig_values[i]);
2132 				values = g_list_append (values, newvalue);
2133 			}
2134 
2135 			err = ! gda_data_model_set_values (proxy->priv->model, rm->model_row,
2136 							   values, error);
2137 			g_list_foreach (values, (GFunc) gda_value_free, NULL);
2138 			g_list_free (values);
2139 		}
2140 		else {
2141 			/* insert a new row */
2142 			GSList *list;
2143 			GList *values = NULL;
2144 			gint i;
2145 			GValue *newvalue;
2146 			GValue **free_val;
2147 			gint new_row;
2148 
2149 			g_assert (rm->modify_values);
2150 			free_val = g_new0 (GValue *, proxy->priv->model_nb_cols);
2151 			for (i = 0; i < proxy->priv->model_nb_cols; i++) {
2152 				newvalue = NULL;
2153 
2154 				list = rm->modify_values;
2155 				while (list && !newvalue) {
2156 					if (ROW_VALUE (list->data)->model_column == i) {
2157 						if (ROW_VALUE (list->data)->attributes &
2158 						    GDA_VALUE_ATTR_IS_DEFAULT)
2159 							newvalue = NULL;
2160 						else {
2161 							if (! ROW_VALUE (list->data)->value) {
2162 								newvalue = gda_value_new_null ();
2163 								free_val [i] = newvalue;
2164 							}
2165 							else
2166 								newvalue = ROW_VALUE (list->data)->value;
2167 
2168 						}
2169 					}
2170 					list = g_slist_next (list);
2171 				}
2172 				values = g_list_append (values, newvalue);
2173 			}
2174 
2175 			proxy->priv->defer_proxied_model_insert = TRUE;
2176 			proxy->priv->catched_inserted_row = -1;
2177 			new_row = gda_data_model_append_values (proxy->priv->model, values, error);
2178 			err = new_row >= 0 ? FALSE : TRUE;
2179 
2180 			g_list_free (values);
2181 			for (i = 0; i < proxy->priv->model_nb_cols; i++)
2182 				if (free_val [i])
2183 					gda_value_free (free_val [i]);
2184 			g_free (free_val);
2185 			if (!err) {
2186 				if (proxy->priv->catched_inserted_row < 0) {
2187 					g_warning (_("Proxied data model reports the modifications as accepted, yet did not emit the "
2188 						     "corresponding \"row-inserted\", \"row-updated\" or \"row-removed\" signal. This "
2189 						     "is a bug of the %s's implementation (please report a bug)."),
2190 						   G_OBJECT_TYPE_NAME (proxy->priv->model));
2191 				}
2192 
2193 				proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
2194 				proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
2195 
2196 				gint tmp;
2197 				tmp = rm->model_row;
2198 				g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2199 				row_modifs_free (rm);
2200 				rm = NULL;
2201 
2202 				if (proxy_row >= 0)
2203 					gda_data_model_row_updated ((GdaDataModel*) proxy, proxy_row);
2204 
2205 				/* signal row actually changed */
2206 				g_signal_emit (G_OBJECT (proxy),
2207 					       gda_data_proxy_signals[ROW_CHANGES_APPLIED],
2208 					       0, proxy_row, -1);
2209 			}
2210 
2211 			proxy->priv->catched_inserted_row = -1;
2212 			proxy->priv->defer_proxied_model_insert = FALSE;
2213 		}
2214 	}
2215 
2216 	if (!err && rm) {
2217 		/* signal row actually changed */
2218 		g_signal_emit (G_OBJECT (proxy),
2219 			       gda_data_proxy_signals[ROW_CHANGES_APPLIED],
2220 			       0, proxy_row, model_row);
2221 
2222 		/* get rid of the committed change; if the changes have been applied correctly, @rm should
2223 		 * have been removed from the proxy->priv->all_modifs list because the proxied model
2224 		 * should habe emitted the "row_{inserted,removed,updated}" signals */
2225 		if (rm && g_slist_find (proxy->priv->all_modifs, rm)) {
2226 			g_warning (_("Proxied data model reports the modifications as accepted, yet did not emit the "
2227 				     "corresponding \"row-inserted\", \"row-updated\" or \"row-removed\" signal. This "
2228 				     "may be a bug of the %s's implementation (please report a bug)."),
2229 				   G_OBJECT_TYPE_NAME (proxy->priv->model));
2230 			proxy->priv->new_rows = g_slist_remove (proxy->priv->new_rows, rm);
2231 			proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
2232 
2233 			gint tmp;
2234 			tmp = rm->model_row;
2235 			g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2236 			row_modifs_free (rm);
2237 		}
2238 	}
2239 
2240 	if (adjust_display)
2241 		adjust_displayed_chunk (proxy);
2242 
2243 	gda_mutex_unlock (proxy->priv->mutex);
2244 
2245 	return !err;
2246 }
2247 
2248 /**
2249  * gda_data_proxy_has_changed:
2250  * @proxy: a #GdaDataProxy object
2251  *
2252  * Tells if @proxy contains any modifications not applied to the proxied data model.
2253  *
2254  * Returns: TRUE if there are some modifications in @proxy
2255  */
2256 gboolean
gda_data_proxy_has_changed(GdaDataProxy * proxy)2257 gda_data_proxy_has_changed (GdaDataProxy *proxy)
2258 {
2259         g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2260         g_return_val_if_fail (proxy->priv, FALSE);
2261 
2262         return proxy->priv->all_modifs ? TRUE : FALSE;
2263 }
2264 
2265 /**
2266  * gda_data_proxy_row_has_changed:
2267  * @proxy: a #GdaDataProxy object
2268  * @proxy_row: A proxy row number
2269  *
2270  * Tells if the row number @proxy_row has changed
2271  *
2272  * Returns: TRUE if the row has changed
2273  */
2274 gboolean
gda_data_proxy_row_has_changed(GdaDataProxy * proxy,gint proxy_row)2275 gda_data_proxy_row_has_changed (GdaDataProxy *proxy, gint proxy_row)
2276 {
2277 	RowModif *rm;
2278 
2279 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2280 	g_return_val_if_fail (proxy->priv, FALSE);
2281 	g_return_val_if_fail (proxy_row >= 0, FALSE);
2282 
2283 	rm = proxy_row_to_row_modif (proxy, proxy_row);
2284 	return rm && (rm->modify_values || rm->to_be_deleted) ? TRUE : FALSE;
2285 }
2286 
2287 /**
2288  * gda_data_proxy_get_n_new_rows:
2289  * @proxy: a #GdaDataProxy object
2290  *
2291  * Get the number of rows which have been added to @proxy and which are not part of
2292  * the proxied data model.
2293  *
2294  * Returns: the number of new rows
2295  */
2296 gint
gda_data_proxy_get_n_new_rows(GdaDataProxy * proxy)2297 gda_data_proxy_get_n_new_rows (GdaDataProxy *proxy)
2298 {
2299 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2300         g_return_val_if_fail (proxy->priv, 0);
2301 
2302         return g_slist_length (proxy->priv->new_rows);
2303 }
2304 
2305 /**
2306  * gda_data_proxy_get_n_modified_rows:
2307  * @proxy: a #GdaDataProxy object
2308  *
2309  * Get the number of rows which have been modified in the proxy (the sum of rows existing in
2310  * the proxied data model which have been modified, and new rows).
2311  *
2312  * Returns: the number of modified rows
2313  */
2314 gint
gda_data_proxy_get_n_modified_rows(GdaDataProxy * proxy)2315 gda_data_proxy_get_n_modified_rows (GdaDataProxy *proxy)
2316 {
2317 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2318         g_return_val_if_fail (proxy->priv, 0);
2319 
2320         return g_slist_length (proxy->priv->all_modifs);
2321 }
2322 
2323 /**
2324  * gda_data_proxy_set_sample_size:
2325  * @proxy: a #GdaDataProxy object
2326  * @sample_size: the requested size of a chunk, or 0
2327  *
2328  * Sets the size of each chunk of data to display: the maximum number of rows which
2329  * can be "displayed" at a time (the maximum number of rows which @proxy pretends to have).
2330  * The default value is arbitrary 300 as it is big enough to
2331  * be able to display quite a lot of data, but small enough to avoid too much data
2332  * displayed at the same time.
2333  *
2334  * Note: the rows which have been added but not yet committed will always be displayed
2335  * regardless of the current chunk of data, and the modified rows which are not visible
2336  * when the displayed chunk of data changes are still held as modified rows.
2337  *
2338  * To remove the chunking of the data to display, simply pass @sample_size the %0 value.
2339  */
2340 void
gda_data_proxy_set_sample_size(GdaDataProxy * proxy,gint sample_size)2341 gda_data_proxy_set_sample_size (GdaDataProxy *proxy, gint sample_size)
2342 {
2343 	gint new_sample_size;
2344 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
2345 	g_return_if_fail (proxy->priv);
2346 
2347 	gda_mutex_lock (proxy->priv->mutex);
2348 
2349 	/* ensure that there is no sync to be done */
2350 	ensure_chunk_sync (proxy);
2351 
2352 	new_sample_size = sample_size <= 0 ? 0 : sample_size;
2353 	if (proxy->priv->sample_size != new_sample_size) {
2354 		proxy->priv->sample_size = new_sample_size;
2355 		adjust_displayed_chunk (proxy);
2356 		g_signal_emit (G_OBJECT (proxy),
2357                                gda_data_proxy_signals[SAMPLE_SIZE_CHANGED],
2358                                0, sample_size);
2359 	}
2360 
2361 	gda_mutex_unlock (proxy->priv->mutex);
2362 }
2363 
2364 /**
2365  * gda_data_proxy_get_sample_size:
2366  * @proxy: a #GdaDataProxy object
2367  *
2368  * Get the size of each chunk of data displayed at a time.
2369  *
2370  * Returns: the chunk (or sample) size, or 0 if chunking is disabled.
2371  */
2372 gint
gda_data_proxy_get_sample_size(GdaDataProxy * proxy)2373 gda_data_proxy_get_sample_size (GdaDataProxy *proxy)
2374 {
2375 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2376 	g_return_val_if_fail (proxy->priv, 0);
2377 
2378 	return proxy->priv->sample_size;
2379 }
2380 
2381 /**
2382  * gda_data_proxy_set_sample_start:
2383  * @proxy: a #GdaDataProxy object
2384  * @sample_start: the number of the first row to be displayed
2385  *
2386  * Sets the number of the first row to be available in @proxy (in reference to the proxied data model)
2387  */
2388 void
gda_data_proxy_set_sample_start(GdaDataProxy * proxy,gint sample_start)2389 gda_data_proxy_set_sample_start (GdaDataProxy *proxy, gint sample_start)
2390 {
2391 	g_return_if_fail (GDA_IS_DATA_PROXY (proxy));
2392 	g_return_if_fail (proxy->priv);
2393 	g_return_if_fail (sample_start >= 0);
2394 
2395 	gda_mutex_lock (proxy->priv->mutex);
2396 
2397 	/* ensure that there is no sync to be done */
2398 	ensure_chunk_sync (proxy);
2399 
2400 	if (proxy->priv->sample_first_row != sample_start) {
2401 		proxy->priv->sample_first_row = sample_start;
2402 		adjust_displayed_chunk (proxy);
2403 	}
2404 
2405 	gda_mutex_unlock (proxy->priv->mutex);
2406 }
2407 
2408 /**
2409  * gda_data_proxy_get_sample_start:
2410  * @proxy: a #GdaDataProxy object
2411  *
2412  * Get the number of the first row to be available in @proxy (in reference to the proxied data model)
2413  *
2414  * Returns: the number of the first proxied model's row.
2415  */
2416 gint
gda_data_proxy_get_sample_start(GdaDataProxy * proxy)2417 gda_data_proxy_get_sample_start (GdaDataProxy *proxy)
2418 {
2419 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2420 	g_return_val_if_fail (proxy->priv, 0);
2421 
2422 	return proxy->priv->sample_first_row;
2423 }
2424 
2425 /**
2426  * gda_data_proxy_get_sample_end:
2427  * @proxy: a #GdaDataProxy object
2428  *
2429  * Get the number of the last row to be available in @proxy (in reference to the proxied data model)
2430  *
2431  * Returns: the number of the last proxied model's row.
2432  */
2433 gint
gda_data_proxy_get_sample_end(GdaDataProxy * proxy)2434 gda_data_proxy_get_sample_end (GdaDataProxy *proxy)
2435 {
2436 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), 0);
2437 	g_return_val_if_fail (proxy->priv, 0);
2438 
2439 	return proxy->priv->sample_last_row;
2440 }
2441 
2442 static DisplayChunk *
display_chunk_new(gint reserved_size)2443 display_chunk_new (gint reserved_size)
2444 {
2445 	DisplayChunk *chunk;
2446 
2447 	chunk = g_new0 (DisplayChunk, 1);
2448 	chunk->mapping = g_array_sized_new (FALSE, TRUE, sizeof (gint), reserved_size);
2449 
2450 	return chunk;
2451 }
2452 
2453 static void
display_chunk_free(DisplayChunk * chunk)2454 display_chunk_free (DisplayChunk *chunk)
2455 {
2456 	if (chunk->mapping)
2457 		g_array_free (chunk->mapping, TRUE);
2458 	g_free (chunk);
2459 }
2460 
2461 #ifdef GDA_DEBUG
2462 static void
display_chunks_dump(GdaDataProxy * proxy)2463 display_chunks_dump (GdaDataProxy *proxy)
2464 {
2465 #define DUMP
2466 #undef DUMP
2467 #ifdef DUMP
2468 	gint i, total1 = 0, total2 = 0;
2469 
2470 	g_print ("================== CHUNK=%p, TO=%p (mapping=%p), SEP=%d\n", proxy->priv->chunk, proxy->priv->chunk_to,
2471 		 proxy->priv->chunk_to ? proxy->priv->chunk_to->mapping : NULL,
2472 		 proxy->priv->chunk_sep);
2473 	if (!proxy->priv->chunk && !proxy->priv->chunk_to)
2474 		g_print ("No chunks at all\n");
2475 
2476 	if (proxy->priv->chunk)
2477 		total1 = proxy->priv->chunk->mapping->len;
2478 	if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping)
2479 		total2 = proxy->priv->chunk_to->mapping->len;
2480 
2481 	g_print ("CHUNK   CHUNK_TO\n");
2482 	for (i = 0; i < MAX (total1, total2); i++) {
2483 		if (i < total1)
2484 			g_print ("%03d", g_array_index (proxy->priv->chunk->mapping, gint, i));
2485 		else
2486 			g_print ("   ");
2487 		g_print (" ==== ");
2488 		if (i < total2)
2489 			g_print ("%03d", g_array_index (proxy->priv->chunk_to->mapping, gint, i));
2490 		else
2491 			g_print ("   ");
2492 		g_print ("\n");
2493 	}
2494 #endif
2495 }
2496 #else
2497 static void
display_chunks_dump(G_GNUC_UNUSED GdaDataProxy * proxy)2498 display_chunks_dump (G_GNUC_UNUSED GdaDataProxy *proxy)
2499 {}
2500 #endif
2501 
2502 /*
2503  * Makes sure the sync, if necessary, is finished
2504  */
2505 static void
ensure_chunk_sync(GdaDataProxy * proxy)2506 ensure_chunk_sync (GdaDataProxy *proxy)
2507 {
2508 	gda_mutex_lock (proxy->priv->mutex);
2509 	if (proxy->priv->chunk_sync_idle_id) {
2510 		gboolean defer_sync = proxy->priv->defer_sync;
2511 		proxy->priv->defer_sync = FALSE;
2512 
2513 		chunk_sync_idle (proxy);
2514 		proxy->priv->defer_sync = defer_sync;
2515 	}
2516 	gda_mutex_unlock (proxy->priv->mutex);
2517 }
2518 
2519 /*
2520  * Emit all the correct signals when switching from proxy->priv->chunk to
2521  * proxy->priv->chunk_to
2522  */
2523 static gboolean
chunk_sync_idle(GdaDataProxy * proxy)2524 chunk_sync_idle (GdaDataProxy *proxy)
2525 {
2526 #define IDLE_STEP 50
2527 	gda_mutex_lock (proxy->priv->mutex);
2528 
2529 	gboolean finished = FALSE;
2530 	guint index, max_steps, step;
2531 	GdaDataModelIter *iter = NULL;
2532 	gint signal_row_offset = proxy->priv->add_null_entry ? 1 : 0;
2533 
2534 	if (!proxy->priv->defer_sync) {
2535 		if (proxy->priv->chunk_sync_idle_id) {
2536 			g_idle_remove_by_data (proxy);
2537 			proxy->priv->chunk_sync_idle_id = 0;
2538 		}
2539 		max_steps = G_MAXINT;
2540 	}
2541 
2542 	if (!proxy->priv->chunk_to) {
2543 		gda_mutex_unlock (proxy->priv->mutex);
2544 		return FALSE; /* nothing to do */
2545 	}
2546 
2547 	max_steps = 0;
2548 	if (proxy->priv->chunk_proxy_nb_rows < 0)
2549 		proxy->priv->chunk_proxy_nb_rows = proxy->priv->model_nb_rows + g_slist_length (proxy->priv->new_rows);
2550 	if (proxy->priv->chunk_to->mapping)
2551 		max_steps = MAX (max_steps, proxy->priv->chunk_to->mapping->len - proxy->priv->chunk_sep + 1);
2552 	else
2553 		max_steps = MAX (max_steps, (guint)(proxy->priv->chunk_proxy_nb_rows - proxy->priv->chunk_sep + 1));
2554 	if (proxy->priv->chunk)
2555 		max_steps = MAX (max_steps, proxy->priv->chunk->mapping->len - proxy->priv->chunk_sep + 1);
2556 	else
2557 		max_steps = MAX (max_steps, (guint)(proxy->priv->chunk_proxy_nb_rows - proxy->priv->chunk_sep + 1));
2558 
2559 	if (proxy->priv->defer_sync)
2560 		max_steps = MIN (max_steps, IDLE_STEP);
2561 
2562 #ifdef DEBUG_SYNC
2563 	g_print ("////////// %s(defer_sync = %d)\n", __FUNCTION__, proxy->priv->defer_sync);
2564 	display_chunks_dump (proxy);
2565 #endif
2566 
2567 	for (index = proxy->priv->chunk_sep, step = 0;
2568 	     step < max_steps && !finished;
2569 	     step++) {
2570 		gint cur_row, repl_row;
2571 
2572 		if (proxy->priv->chunk) {
2573 			if (index < proxy->priv->chunk->mapping->len)
2574 				cur_row = g_array_index (proxy->priv->chunk->mapping, gint, index);
2575 			else
2576 				cur_row = -1;
2577 		}
2578 		else {
2579 			cur_row = index;
2580 			if (cur_row >= proxy->priv->chunk_proxy_nb_rows)
2581 				cur_row = -1;
2582 		}
2583 
2584 		if (proxy->priv->chunk_to->mapping) {
2585 			if (index < proxy->priv->chunk_to->mapping->len)
2586 				repl_row = g_array_index (proxy->priv->chunk_to->mapping, gint, index);
2587 			else
2588 				repl_row = -1;
2589 		}
2590 		else {
2591 			repl_row = index;
2592 			if (!iter)
2593 				iter = gda_data_model_create_iter (proxy->priv->model);
2594 			if (!gda_data_model_iter_move_to_row (iter, repl_row)) {
2595 				if (gda_data_model_iter_get_row (iter) != repl_row)
2596 					repl_row = -1;
2597 			}
2598 		}
2599 
2600 #ifdef DEBUG_SYNC
2601 		g_print ("INDEX=%d step=%d max_steps=%d cur_row=%d repl_row=%d\n", index, step, max_steps, cur_row, repl_row);
2602 #endif
2603 		if ((cur_row >= 0) && (repl_row >= 0)) {
2604 			/* emit the GdaDataModel::"row-updated" signal */
2605 			if (proxy->priv->chunk) {
2606 				g_array_insert_val (proxy->priv->chunk->mapping, index, repl_row);
2607 				g_array_remove_index (proxy->priv->chunk->mapping, index + 1);
2608 			}
2609 			proxy->priv->chunk_sep++;
2610 
2611 			if (cur_row != repl_row)
2612 				if (proxy->priv->notify_changes) {
2613 #ifdef DEBUG_SYNC
2614 					g_print ("Signal: Update row %d\n", index + signal_row_offset);
2615 #endif
2616 					gda_data_model_row_updated ((GdaDataModel *) proxy, index + signal_row_offset);
2617 				}
2618 			index++;
2619 		}
2620 		else if ((cur_row >= 0) && (repl_row < 0)) {
2621 			/* emit the GdaDataModel::"row-removed" signal */
2622 			if (proxy->priv->chunk)
2623 				g_array_remove_index (proxy->priv->chunk->mapping, index);
2624 			proxy->priv->chunk_proxy_nb_rows--;
2625 			if (proxy->priv->notify_changes) {
2626 #ifdef DEBUG_SYNC
2627 				g_print ("Signal: Remove row %d\n", index + signal_row_offset);
2628 #endif
2629 				gda_data_model_row_removed ((GdaDataModel *) proxy, index + signal_row_offset);
2630 			}
2631 		}
2632 		else if ((cur_row < 0) && (repl_row >= 0)) {
2633 			/* emit GdaDataModel::"row-inserted" insert signal */
2634 			if (proxy->priv->chunk)
2635 				g_array_insert_val (proxy->priv->chunk->mapping, index, repl_row);
2636 			proxy->priv->chunk_sep++;
2637 			if (proxy->priv->notify_changes) {
2638 #ifdef DEBUG_SYNC
2639 				g_print ("Signal: Insert row %d\n", index + signal_row_offset);
2640 #endif
2641 				gda_data_model_row_inserted ((GdaDataModel *) proxy, index + signal_row_offset);
2642 			}
2643 			index++;
2644 		}
2645 		else
2646 			finished = TRUE;
2647 	}
2648 
2649 	if (iter)
2650 		g_object_unref (iter);
2651 
2652 	if (finished) {
2653 		if (proxy->priv->chunk_sync_idle_id) {
2654 			g_idle_remove_by_data (proxy);
2655 			proxy->priv->chunk_sync_idle_id = 0;
2656 		}
2657 
2658 		if (! proxy->priv->chunk_to->mapping) {
2659 			if (proxy->priv->chunk) {
2660 				display_chunk_free (proxy->priv->chunk);
2661 				proxy->priv->chunk = NULL;
2662 			}
2663 			display_chunk_free (proxy->priv->chunk_to);
2664 			proxy->priv->chunk_to = NULL;
2665 		}
2666 		else {
2667 			if (proxy->priv->chunk)
2668 				display_chunk_free (proxy->priv->chunk);
2669 			proxy->priv->chunk = proxy->priv->chunk_to;
2670 			proxy->priv->chunk_to = NULL;
2671 		}
2672 #ifdef DEBUG_SYNC
2673 		g_print ("Sync. is now finished\n");
2674 #endif
2675 	}
2676 #ifdef DEBUG_SYNC
2677 	else
2678 		g_print ("Sync. is NOT finished yet\n");
2679 #endif
2680 	gda_mutex_unlock (proxy->priv->mutex);
2681 	return !finished;
2682 }
2683 
2684 static DisplayChunk *
compute_display_chunk(GdaDataProxy * proxy)2685 compute_display_chunk (GdaDataProxy *proxy)
2686 {
2687 	DisplayChunk *ret_chunk = NULL;
2688 
2689 	gda_mutex_lock (proxy->priv->mutex);
2690 	if (proxy->priv->filtered_rows) {
2691 		/* REM: when there is a filter applied, the new rows are mixed with the
2692 		 * existing ones => no need to treat them appart
2693 		 */
2694 		gint nb_rows = gda_data_model_get_n_rows (proxy->priv->filtered_rows);
2695 		gint i, new_nb_rows = 0;
2696 
2697 		g_assert (nb_rows >= 0); /* the number of rows IS known here */
2698 		if (proxy->priv->sample_size > 0) {
2699 			if (proxy->priv->sample_first_row >= nb_rows)
2700 				proxy->priv->sample_first_row = proxy->priv->sample_size *
2701 					((nb_rows - 1) / proxy->priv->sample_size);
2702 
2703 			proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2704 				proxy->priv->sample_size - 1;
2705 			if (proxy->priv->sample_last_row >= nb_rows)
2706 				proxy->priv->sample_last_row = nb_rows - 1;
2707 			new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2708 		}
2709 		else {
2710 			proxy->priv->sample_first_row = 0;
2711 			proxy->priv->sample_last_row = nb_rows - 1;
2712 			new_nb_rows = nb_rows;
2713 		}
2714 
2715 		ret_chunk = display_chunk_new (proxy->priv->sample_size > 0 ?
2716 						 proxy->priv->sample_size : nb_rows);
2717 		for (i = 0; i < new_nb_rows; i++) {
2718 			const GValue *value;
2719 			gint val;
2720 
2721 			g_assert (i + proxy->priv->sample_first_row < nb_rows);
2722 			value = gda_data_model_get_value_at (proxy->priv->filtered_rows,
2723 							     0, i + proxy->priv->sample_first_row, NULL);
2724 			g_assert (value);
2725 			g_assert (G_VALUE_TYPE (value) == G_TYPE_INT);
2726 			val = g_value_get_int (value);
2727 			g_array_append_val (ret_chunk->mapping, val);
2728 		}
2729 	}
2730 	else {
2731 		gint i, new_nb_rows = 0;
2732 
2733 		if (proxy->priv->model_nb_rows >= 0) {
2734 			/* known number of rows */
2735 			if (proxy->priv->sample_size > 0) {
2736 				if (proxy->priv->sample_first_row >= proxy->priv->model_nb_rows)
2737 					proxy->priv->sample_first_row = proxy->priv->sample_size *
2738 						((proxy->priv->model_nb_rows - 1) / proxy->priv->sample_size);
2739 
2740 				proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2741 					proxy->priv->sample_size - 1;
2742 				if (proxy->priv->sample_last_row >= proxy->priv->model_nb_rows)
2743 					proxy->priv->sample_last_row = proxy->priv->model_nb_rows - 1;
2744 				new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2745 				ret_chunk = display_chunk_new (proxy->priv->sample_size);
2746 			}
2747 			else {
2748 				/* no chunk_to->mapping needed */
2749 				ret_chunk = g_new0 (DisplayChunk, 1);
2750 
2751 				proxy->priv->sample_first_row = 0;
2752 				proxy->priv->sample_last_row = proxy->priv->model_nb_rows - 1;
2753 				new_nb_rows = proxy->priv->model_nb_rows;
2754 			}
2755 		}
2756 		else {
2757 			/* no chunk_to->mapping needed */
2758 			ret_chunk = g_new0 (DisplayChunk, 1);
2759 
2760 			if (proxy->priv->model_nb_rows == 0 ) {
2761 				/* known number of rows */
2762 				proxy->priv->sample_first_row = 0;
2763 				proxy->priv->sample_last_row = 0;
2764 				new_nb_rows = 0;
2765 			}
2766 			else {
2767 				/* unknown number of rows */
2768 				if (proxy->priv->sample_size > 0) {
2769 					proxy->priv->sample_last_row = proxy->priv->sample_first_row +
2770 						proxy->priv->sample_size - 1;
2771 					new_nb_rows = proxy->priv->sample_last_row - proxy->priv->sample_first_row + 1;
2772 				}
2773 				else {
2774 					proxy->priv->sample_first_row = 0;
2775 					proxy->priv->sample_last_row = G_MAXINT - 1;
2776 					new_nb_rows = G_MAXINT;
2777 				}
2778 			}
2779 		}
2780 		/* fill @chunk_to is it exists */
2781 		if (ret_chunk && ret_chunk->mapping) {
2782 			for (i = 0; i < new_nb_rows; i++) {
2783 				g_assert (i + proxy->priv->sample_first_row < proxy->priv->model_nb_rows);
2784 				gint val = model_row_to_absolute_row (proxy, i + proxy->priv->sample_first_row);
2785 				g_array_append_val (ret_chunk->mapping, val);
2786 			}
2787 			GSList *list;
2788 			for (i++, list = proxy->priv->new_rows; list; list = list->next, i++) {
2789 				gint val = row_modif_to_absolute_row (proxy, ROW_MODIF (list->data));
2790 				g_array_append_val (ret_chunk->mapping, val);
2791 			}
2792 		}
2793 	}
2794 
2795 	gda_mutex_unlock (proxy->priv->mutex);
2796 	return ret_chunk;
2797 }
2798 
2799 /*
2800  * Adjusts the values of the first and last rows to be displayed depending
2801  * on the sample size.
2802  *
2803  * Some rows may be added or removed during the adjustment.
2804  */
2805 static void
adjust_displayed_chunk(GdaDataProxy * proxy)2806 adjust_displayed_chunk (GdaDataProxy *proxy)
2807 {
2808 	g_return_if_fail (proxy->priv->model);
2809 
2810 	gda_mutex_lock (proxy->priv->mutex);
2811 
2812 	/*
2813 	 * Stop idle adding of rows if necessary
2814 	 */
2815 	if (proxy->priv->chunk_sync_idle_id) {
2816 		g_idle_remove_by_data (proxy);
2817 		proxy->priv->chunk_sync_idle_id = 0;
2818 	}
2819 
2820 	/* compute new DisplayChunk */
2821 	if (proxy->priv->chunk_to) {
2822 		display_chunk_free (proxy->priv->chunk_to);
2823 		proxy->priv->chunk_to = NULL;
2824 	}
2825 	proxy->priv->chunk_to = compute_display_chunk (proxy);
2826 	if (!proxy->priv->chunk_to) {
2827 		gda_mutex_unlock (proxy->priv->mutex);
2828 		return; /* nothing to do */
2829 	}
2830 
2831 	/* determine if chunking has changed */
2832 	gboolean equal = FALSE;
2833 	if (proxy->priv->chunk && proxy->priv->chunk_to->mapping) {
2834 		/* compare the 2 chunks */
2835 		if (proxy->priv->chunk->mapping->len == proxy->priv->chunk_to->mapping->len) {
2836 			gsize i;
2837 			equal = TRUE;
2838 			for (i = 0; i < proxy->priv->chunk->mapping->len; i++) {
2839 				if (g_array_index (proxy->priv->chunk->mapping, gint, i) !=
2840 				    g_array_index (proxy->priv->chunk_to->mapping, gint, i)) {
2841 					equal = FALSE;
2842 					break;
2843 				}
2844 			}
2845 		}
2846 	}
2847 	else if (!proxy->priv->chunk && !proxy->priv->chunk_to->mapping)
2848 		equal = TRUE;
2849 
2850 	/* handle new display chunk (which may be NULL) */
2851 	if (! equal) {
2852 #ifdef DEBUG_SYNC
2853 		g_print ("////////// %s(%d)\n", __FUNCTION__, __LINE__);
2854 #endif
2855 		display_chunks_dump (proxy);
2856 		/* signal sample changed if necessary */
2857 		g_signal_emit (G_OBJECT (proxy),
2858                                gda_data_proxy_signals[SAMPLE_CHANGED],
2859                                0, proxy->priv->sample_first_row, proxy->priv->sample_last_row);
2860 
2861 		/* sync proxy->priv->chunk to proxy->priv->chunk_to */
2862 		proxy->priv->chunk_sep = 0;
2863 		proxy->priv->chunk_proxy_nb_rows = -1;
2864 		if (!proxy->priv->defer_sync)
2865 			chunk_sync_idle (proxy);
2866 		else
2867 			proxy->priv->chunk_sync_idle_id = g_idle_add ((GSourceFunc) chunk_sync_idle, proxy);
2868 	}
2869 	else {
2870 		/* nothing to adjust => destroy proxy->priv->chunk_to if necessary */
2871 		if (proxy->priv->chunk_to) {
2872 			display_chunk_free (proxy->priv->chunk_to);
2873 			proxy->priv->chunk_to = NULL;
2874 		}
2875 	}
2876 
2877 	gda_mutex_unlock (proxy->priv->mutex);
2878 }
2879 
2880 /**
2881  * gda_data_proxy_apply_all_changes:
2882  * @proxy: a #GdaDataProxy object
2883  * @error: a place to store errors, or %NULL
2884  *
2885  * Apply all the changes stored in the proxy to the proxied data model. The changes are done row
2886  * after row, and if an error
2887  * occurs, then it is possible that not all the changes to all the rows have been applied.
2888  *
2889  * Returns: TRUE if no error occurred
2890  */
2891 gboolean
gda_data_proxy_apply_all_changes(GdaDataProxy * proxy,GError ** error)2892 gda_data_proxy_apply_all_changes (GdaDataProxy *proxy, GError **error)
2893 {
2894 	gboolean allok = TRUE;
2895 
2896 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2897 	g_return_val_if_fail (proxy->priv, FALSE);
2898 
2899 	gda_mutex_lock (proxy->priv->mutex);
2900 
2901 	/* ensure that there is no sync to be done */
2902 	ensure_chunk_sync (proxy);
2903 
2904 	gda_data_model_send_hint (proxy->priv->model, GDA_DATA_MODEL_HINT_START_BATCH_UPDATE, NULL);
2905 
2906 	while (proxy->priv->all_modifs && allok)
2907 		allok = commit_row_modif (proxy, ROW_MODIF (proxy->priv->all_modifs->data), FALSE, error);
2908 
2909 	gda_data_model_send_hint (proxy->priv->model, GDA_DATA_MODEL_HINT_END_BATCH_UPDATE, NULL);
2910 	adjust_displayed_chunk (proxy);
2911 
2912 	gda_mutex_unlock (proxy->priv->mutex);
2913 
2914 	return allok;
2915 }
2916 
2917 /**
2918  * gda_data_proxy_cancel_all_changes:
2919  * @proxy: a #GdaDataProxy object
2920  *
2921  * Cancel all the changes stored in the proxy (the @proxy will be reset to its state
2922  * as it was just after creation). Note that if there are some cached changes (i.e. not applied
2923  * to the current proxied data model), then these cached changes are not cleared (set the "cache-changes"
2924  * property to %FALSE for this).
2925  *
2926  * Returns: TRUE if no error occurred
2927  */
2928 gboolean
gda_data_proxy_cancel_all_changes(GdaDataProxy * proxy)2929 gda_data_proxy_cancel_all_changes (GdaDataProxy *proxy)
2930 {
2931 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
2932 	g_return_val_if_fail (proxy->priv, FALSE);
2933 
2934 	gda_mutex_lock (proxy->priv->mutex);
2935 
2936 	/* ensure that there is no sync to be done */
2937 	ensure_chunk_sync (proxy);
2938 	g_assert (!proxy->priv->chunk_to);
2939 
2940 	/* new rows are first treated and removed (no memory de-allocation here, though) */
2941 	if (proxy->priv->new_rows) {
2942 		if (proxy->priv->chunk) {
2943 			/* Using a chunk */
2944 			proxy->priv->chunk_to = display_chunk_new (proxy->priv->chunk->mapping->len);
2945 			g_array_append_vals (proxy->priv->chunk_to->mapping,
2946 					     proxy->priv->chunk->mapping->data, proxy->priv->chunk->mapping->len);
2947 
2948 			while (proxy->priv->new_rows) {
2949 				gint proxy_row;
2950 
2951 				proxy_row = row_modif_to_proxy_row (proxy, (ROW_MODIF (proxy->priv->new_rows->data)));
2952 				proxy->priv->new_rows = g_slist_delete_link (proxy->priv->new_rows, proxy->priv->new_rows);
2953 
2954 				if ((proxy_row >= 0) && proxy->priv->chunk_to)
2955 					g_array_remove_index (proxy->priv->chunk_to->mapping,
2956 							      proxy_row - (proxy->priv->add_null_entry ? 1 : 0));
2957 			}
2958 
2959 			if (proxy->priv->chunk_to) {
2960 				/* sync proxy->priv->chunk to proxy->priv->chunk_to */
2961 				gboolean defer_sync = proxy->priv->defer_sync;
2962 				proxy->priv->defer_sync = FALSE;
2963 				proxy->priv->chunk_sep = 0;
2964 				proxy->priv->chunk_proxy_nb_rows = -1;
2965 				chunk_sync_idle (proxy);
2966 				proxy->priv->defer_sync = defer_sync;
2967 			}
2968 		}
2969 		else {
2970 			/* no chunk used */
2971 			gint nrows = gda_data_proxy_get_n_rows ((GdaDataModel *) proxy);
2972 			while (proxy->priv->new_rows) {
2973 				proxy->priv->new_rows = g_slist_delete_link (proxy->priv->new_rows, proxy->priv->new_rows);
2974 				if (proxy->priv->notify_changes) {
2975 					gda_data_model_row_removed ((GdaDataModel *) proxy, nrows-1);
2976 					nrows--;
2977 				}
2978 			}
2979 		}
2980 	}
2981 
2982 	/* all modified rows are then treated (including memory de-allocation for new rows) */
2983 	while (proxy->priv->all_modifs) {
2984 		gint model_row = ROW_MODIF (proxy->priv->all_modifs->data)->model_row;
2985 		gint proxy_row = -1;
2986 
2987 		if (proxy->priv->notify_changes && (model_row >= 0))
2988 			proxy_row = row_modif_to_proxy_row (proxy, (ROW_MODIF (proxy->priv->all_modifs->data)));
2989 
2990 		row_modifs_free (ROW_MODIF (proxy->priv->all_modifs->data));
2991 		if (model_row >= 0) {
2992 			gint tmp;
2993 			tmp = model_row;
2994 			g_hash_table_remove (proxy->priv->modify_rows, &tmp);
2995 		}
2996 		proxy->priv->all_modifs = g_slist_delete_link (proxy->priv->all_modifs, proxy->priv->all_modifs);
2997 
2998 		if ((proxy_row >= 0) && proxy->priv->notify_changes)
2999 			gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
3000 	}
3001 
3002 	gda_mutex_unlock (proxy->priv->mutex);
3003 
3004 	return TRUE;
3005 }
3006 
3007 static gboolean
sql_where_foreach(GdaSqlAnyPart * part,GdaDataProxy * proxy,G_GNUC_UNUSED GError ** error)3008 sql_where_foreach (GdaSqlAnyPart *part, GdaDataProxy *proxy, G_GNUC_UNUSED GError **error)
3009 {
3010 	if (part->type == GDA_SQL_ANY_EXPR) {
3011 		GdaSqlExpr *expr = (GdaSqlExpr*) part;
3012 		if (expr->value && (G_VALUE_TYPE (expr->value) == G_TYPE_STRING)) {
3013 			const gchar *cstr = g_value_get_string (expr->value);
3014 			if (*cstr == '_') {
3015 				const gchar *ptr;
3016 				for (ptr = cstr+1; *ptr; ptr++)
3017 					if ((*ptr < '0') || (*ptr > '9'))
3018 						break;
3019 				if (!*ptr) {
3020 					/* column name is "_<number>", use column: <number> - 1 */
3021 					gint colnum;
3022 					colnum = atoi (cstr+1) - 1; /* Flawfinder: ignore */
3023 					if ((colnum >= 0) &&
3024 					    (colnum < gda_data_model_get_n_columns ((GdaDataModel*) proxy))) {
3025 						GdaColumn *col = gda_data_model_describe_column ((GdaDataModel*) proxy,
3026 												 colnum);
3027 						const gchar *cname = gda_column_get_name (col);
3028 						if (cname && *cname) {
3029 							g_value_take_string (expr->value,
3030 									     gda_sql_identifier_quote (cname,
3031 												       proxy->priv->filter_vcnc,
3032 												       NULL,
3033 												       FALSE, FALSE));
3034 						}
3035 					}
3036 				}
3037 			}
3038 		}
3039 	}
3040 	return TRUE;
3041 }
3042 
3043 /*
3044  * Applies proxy->priv->filter_stmt
3045  */
3046 static gboolean
apply_filter_statement(GdaDataProxy * proxy,GError ** error)3047 apply_filter_statement (GdaDataProxy *proxy, GError **error)
3048 {
3049 	GdaConnection *vcnc;
3050 	GdaDataModel *filtered_rows = NULL;
3051 	GdaStatement *stmt = NULL;
3052 
3053 	if (proxy->priv->filter_stmt) {
3054 		stmt = proxy->priv->filter_stmt;
3055 		proxy->priv->filter_stmt = NULL;
3056 	}
3057 
3058 	/* ensure that there is no sync to be done */
3059 	ensure_chunk_sync (proxy);
3060 
3061 	if (!stmt)
3062 		goto clean_previous_filter;
3063 
3064 	g_mutex_lock (&provider_mutex);
3065 	if (!virtual_provider)
3066 		virtual_provider = gda_vprovider_data_model_new ();
3067 	g_mutex_unlock (&provider_mutex);
3068 
3069 	/* Force direct data access where proxy_row <=> absolute_row */
3070 	proxy->priv->force_direct_mapping = TRUE;
3071 
3072 	vcnc = proxy->priv->filter_vcnc;
3073 	if (!vcnc) {
3074 		GError *lerror = NULL;
3075 		vcnc = gda_virtual_connection_open (virtual_provider, &lerror);
3076 		if (! vcnc) {
3077 			g_print ("Virtual ERROR: %s\n", lerror && lerror->message ? lerror->message : "No detail");
3078 			if (lerror)
3079 				g_error_free (lerror);
3080 			g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3081 				      "%s", _("Could not create virtual connection"));
3082 			proxy->priv->force_direct_mapping = FALSE;
3083 			goto clean_previous_filter;
3084 		}
3085 
3086 		proxy->priv->filter_vcnc = vcnc;
3087 	}
3088 
3089 	/* Add the @proxy to the virtual connection.
3090 	 *
3091 	 * REM: use a GdaDataModelWrapper to force the viewing of the un-modified columns of @proxy,
3092 	 * otherwise the GdaVconnectionDataModel will just take into account the modified columns
3093 	 */
3094 	GdaDataModel *wrapper;
3095 	wrapper = gda_data_access_wrapper_new ((GdaDataModel*) proxy);
3096 	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (vcnc), wrapper,
3097 						   "proxy", error)) {
3098 		g_object_unref (wrapper);
3099 		proxy->priv->force_direct_mapping = FALSE;
3100 		goto clean_previous_filter;
3101 	}
3102 	g_object_unref (wrapper);
3103 
3104 	/* remork the statement for column names */
3105 	GdaSqlStatement *sqlst;
3106 	g_object_get (G_OBJECT (stmt), "structure", &sqlst, NULL);
3107 	g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT);
3108 	gda_sql_any_part_foreach (GDA_SQL_ANY_PART (sqlst->contents), (GdaSqlForeachFunc) sql_where_foreach, proxy, NULL);
3109 	g_object_set (G_OBJECT (stmt), "structure", sqlst, NULL);
3110 #ifdef GDA_DEBUG_NO
3111 	gchar *ser;
3112 	ser = gda_sql_statement_serialize (sqlst);
3113 	g_print ("Modified Filter: %s\n", ser);
3114 	g_free (ser);
3115 #endif
3116 	gda_sql_statement_free (sqlst);
3117 
3118 	/* execute statement */
3119 	GError *lerror = NULL;
3120 	filtered_rows = gda_connection_statement_execute_select (vcnc, stmt, NULL, &lerror);
3121      	if (!filtered_rows) {
3122 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3123 			     _("Error in filter expression: %s"), lerror && lerror->message ? lerror->message : _("No detail"));
3124 		g_clear_error (&lerror);
3125 		proxy->priv->force_direct_mapping = FALSE;
3126 		gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (vcnc), "proxy", NULL);
3127 		goto clean_previous_filter;
3128 	}
3129 
3130 	/* copy filtered_rows and remove virtual table */
3131 	GdaDataModel *copy;
3132 	copy = (GdaDataModel*) gda_data_model_array_copy_model (filtered_rows, NULL);
3133 	g_object_unref (filtered_rows);
3134 	gda_vconnection_data_model_remove (GDA_VCONNECTION_DATA_MODEL (vcnc), "proxy", NULL);
3135 	if (!copy) {
3136 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3137 			      "%s", _("Error in filter expression"));
3138 		proxy->priv->force_direct_mapping = FALSE;
3139 		filtered_rows = NULL;
3140 		goto clean_previous_filter;
3141 	}
3142 	filtered_rows = copy;
3143 	proxy->priv->force_direct_mapping = FALSE;
3144 
3145  clean_previous_filter:
3146 	if (proxy->priv->filter_expr) {
3147 		g_free (proxy->priv->filter_expr);
3148 		proxy->priv->filter_expr = NULL;
3149 	}
3150 	if (proxy->priv->filtered_rows) {
3151 		g_object_unref (proxy->priv->filtered_rows);
3152 		proxy->priv->filtered_rows = NULL;
3153 	}
3154 #define FILTER_SELECT_WHERE "SELECT __gda_row_nb FROM proxy WHERE "
3155 #define FILTER_SELECT_NOWHERE "SELECT __gda_row_nb FROM proxy "
3156 	if (filtered_rows) {
3157 		gchar *sql;
3158 		sql = gda_statement_to_sql (stmt, NULL, NULL);
3159 		if (sql) {
3160 			if (!g_ascii_strncasecmp (sql, FILTER_SELECT_WHERE, strlen (FILTER_SELECT_WHERE)))
3161 				proxy->priv->filter_expr = g_strdup (sql + strlen (FILTER_SELECT_WHERE));
3162 			else if (!g_ascii_strncasecmp (sql, FILTER_SELECT_NOWHERE, strlen (FILTER_SELECT_NOWHERE)))
3163 				proxy->priv->filter_expr = g_strdup (sql + strlen (FILTER_SELECT_NOWHERE));
3164 			g_free (sql);
3165 		}
3166 		proxy->priv->filtered_rows = filtered_rows;
3167 		proxy->priv->filter_stmt = stmt;
3168 	}
3169 	else if (stmt)
3170 		g_object_unref (stmt);
3171 
3172 	g_signal_emit (G_OBJECT (proxy),
3173 		       gda_data_proxy_signals[FILTER_CHANGED],
3174 		       0);
3175 	gda_data_model_reset (GDA_DATA_MODEL (proxy));
3176 
3177 	adjust_displayed_chunk (proxy);
3178 
3179 	if (!stmt)
3180 		return TRUE;
3181 	else
3182 		return filtered_rows ? TRUE : FALSE;
3183 }
3184 
3185 /**
3186  * gda_data_proxy_set_filter_expr:
3187  * @proxy: a #GdaDataProxy object
3188  * @filter_expr: (nullable): an SQL based expression which will filter the contents of @proxy, or %NULL to remove any previous filter
3189  * @error: a place to store errors, or %NULL
3190  *
3191  * Sets a filter among the rows presented by @proxy. The filter is defined by a filter expression
3192  * which can be any SQL valid expression using @proxy's columns. For instance if @proxy has the "id" and
3193  * "name" columns, then a filter can be "length(name) < 5" to filter only the rows where the length of the
3194  * name is strictly inferior to 5, or "id >= 1000 and id < 2000 order by name limit 50" to filter only the rows where the id
3195  * is between 1000 and 2000, ordered by name and limited to 50 rows.
3196  *
3197  * Note about column names: real column names can be used (double quoted if necessary), but columns can also be named
3198  * "_&lt;column number&gt;" with column numbers starting at 1.
3199  *
3200  * Note that any previous filter expression is replaced with the new @filter_expr if no error occurs
3201  * (if an error occurs, then any previous filter is left unchanged).
3202  *
3203  * Returns: TRUE if no error occurred
3204  */
3205 gboolean
gda_data_proxy_set_filter_expr(GdaDataProxy * proxy,const gchar * filter_expr,GError ** error)3206 gda_data_proxy_set_filter_expr (GdaDataProxy *proxy, const gchar *filter_expr, GError **error)
3207 {
3208 	gchar *sql;
3209 	GdaStatement *stmt = NULL;
3210 
3211 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3212 	g_return_val_if_fail (proxy->priv, FALSE);
3213 
3214 	gda_mutex_lock (proxy->priv->mutex);
3215 
3216 	if (!filter_expr) {
3217 		if (proxy->priv->filter_stmt)
3218 			g_object_unref (proxy->priv->filter_stmt);
3219 		proxy->priv->filter_stmt = NULL;
3220 
3221 		gboolean retval = apply_filter_statement (proxy, error);
3222 		gda_mutex_unlock (proxy->priv->mutex);
3223 		return retval;
3224 	}
3225 
3226 	/* generate SQL with a special case if expression starts with "ORDER BY" */
3227 	gchar *tmp;
3228 	const gchar *ptr;
3229 	gint i;
3230 	tmp = g_strdup (filter_expr);
3231 	for (i = 0, ptr = filter_expr; *ptr && (i < 7); ptr++) {
3232 		if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n')) {
3233 		}
3234 		else {
3235 			tmp [i] = *ptr;
3236 			i++;
3237 		}
3238 	}
3239 	if (! g_ascii_strncasecmp (tmp, "orderby", 7))
3240 		sql = g_strdup_printf (FILTER_SELECT_NOWHERE "%s", filter_expr);
3241 	else
3242 		sql = g_strdup_printf (FILTER_SELECT_WHERE "%s", filter_expr);
3243 	g_free (tmp);
3244 
3245 	g_mutex_lock (&parser_mutex);
3246 	stmt = gda_sql_parser_parse_string (internal_parser, sql, &ptr, NULL);
3247 	g_mutex_unlock (&parser_mutex);
3248 	g_free (sql);
3249 	if (ptr || !stmt || (gda_statement_get_statement_type (stmt) != GDA_SQL_STATEMENT_SELECT)) {
3250 		/* also catches problems with multiple statements in @filter_expr, such as SQL code injection */
3251 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_FILTER_ERROR,
3252 			      "%s", _("Incorrect filter expression"));
3253 		if (stmt)
3254 			g_object_unref (stmt);
3255 		proxy->priv->force_direct_mapping = FALSE;
3256 
3257 		gda_mutex_unlock (proxy->priv->mutex);
3258 		return FALSE;
3259 	}
3260 
3261 	if (proxy->priv->filter_stmt)
3262 		g_object_unref (proxy->priv->filter_stmt);
3263 	proxy->priv->filter_stmt = stmt;
3264 
3265 	gboolean retval = apply_filter_statement (proxy, error);
3266 	gda_mutex_unlock (proxy->priv->mutex);
3267 	return retval;
3268 }
3269 
3270 /**
3271  * gda_data_proxy_set_ordering_column:
3272  * @proxy: a #GdaDataProxy object
3273  * @col: the column number to order from
3274  * @error: a place to store errors, or %NULL
3275  *
3276  * Orders by the @col column
3277  *
3278  * Returns: TRUE if no error occurred
3279  */
3280 gboolean
gda_data_proxy_set_ordering_column(GdaDataProxy * proxy,gint col,GError ** error)3281 gda_data_proxy_set_ordering_column (GdaDataProxy *proxy, gint col, GError **error)
3282 {
3283 	gboolean retval;
3284 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3285 	g_return_val_if_fail (proxy->priv, FALSE);
3286 	g_return_val_if_fail (col >= 0, FALSE);
3287 	g_return_val_if_fail (col < gda_data_model_get_n_columns ((GdaDataModel*) proxy), FALSE);
3288 
3289 	gda_mutex_lock (proxy->priv->mutex);
3290 
3291 	if (proxy->priv->filter_stmt) {
3292 		GdaSqlStatement *sqlst;
3293 		GdaSqlStatementSelect *selst;
3294 		gboolean replaced = FALSE;
3295 		const gchar *cname;
3296 		gchar *colname;
3297 
3298 		cname = gda_column_get_name (gda_data_model_describe_column ((GdaDataModel*) proxy, col));
3299 		if (cname && *cname)
3300 			colname = gda_sql_identifier_quote (cname, proxy->priv->filter_vcnc, NULL, FALSE, FALSE);
3301 		else
3302 			colname = g_strdup_printf ("_%d", col + 1);
3303 
3304 		g_object_get (G_OBJECT (proxy->priv->filter_stmt), "structure", &sqlst, NULL);
3305 		g_assert (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT);
3306 		g_free (sqlst->sql);
3307 		sqlst->sql = NULL;
3308 		selst = (GdaSqlStatementSelect*) sqlst->contents;
3309 
3310 		/* test if we can actually toggle the sort ASC <-> DESC */
3311 		if (selst->order_by && !selst->order_by->next) {
3312 			GdaSqlSelectOrder *order_by = (GdaSqlSelectOrder*) selst->order_by->data;
3313 			if (order_by->expr && order_by->expr->value &&
3314 			    (G_VALUE_TYPE (order_by->expr->value) == G_TYPE_STRING) &&
3315 			    gda_identifier_equal (g_value_get_string (order_by->expr->value), colname)) {
3316 				order_by->asc = !order_by->asc;
3317 				replaced = TRUE;
3318 				g_free (colname);
3319 			}
3320 		}
3321 
3322 		if (!replaced) {
3323 			/* replace the whole ordering part */
3324 			if (selst->order_by) {
3325 				g_slist_foreach (selst->order_by, (GFunc) gda_sql_select_order_free, NULL);
3326 				g_slist_free (selst->order_by);
3327 				selst->order_by = NULL;
3328 			}
3329 
3330 			GdaSqlSelectOrder *order_by;
3331 			GdaSqlExpr *expr;
3332 			order_by = gda_sql_select_order_new (GDA_SQL_ANY_PART (selst));
3333 			selst->order_by = g_slist_prepend (NULL, order_by);
3334 			expr = gda_sql_expr_new (GDA_SQL_ANY_PART (order_by));
3335 			order_by->expr = expr;
3336 			order_by->asc = TRUE;
3337 			expr->value = gda_value_new (G_TYPE_STRING);
3338 			g_value_take_string (expr->value, colname);
3339 		}
3340 
3341 		g_object_set (G_OBJECT (proxy->priv->filter_stmt), "structure", sqlst, NULL);
3342 #ifdef GDA_DEBUG_NO
3343 		gchar *ser;
3344 		ser = gda_sql_statement_serialize (sqlst);
3345 		g_print ("Modified Filter: %s\n", ser);
3346 		g_free (ser);
3347 #endif
3348 		gda_sql_statement_free (sqlst);
3349 		retval = apply_filter_statement (proxy, error);
3350 	}
3351 	else {
3352 		gchar *str;
3353 		str = g_strdup_printf ("ORDER BY _%d", col + 1);
3354 		retval = gda_data_proxy_set_filter_expr (proxy, str, error);
3355 		g_free (str);
3356 	}
3357 
3358 	gda_mutex_unlock (proxy->priv->mutex);
3359 	return retval;
3360 }
3361 
3362 /**
3363  * gda_data_proxy_get_filter_expr:
3364  * @proxy: a #GdaDataProxy object
3365  *
3366  * Get the current filter expression used by @proxy.
3367  *
3368  * Returns: the current filter expression or %NULL if no filter has been set
3369  */
3370 const gchar *
gda_data_proxy_get_filter_expr(GdaDataProxy * proxy)3371 gda_data_proxy_get_filter_expr (GdaDataProxy *proxy)
3372 {
3373 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), NULL);
3374 	g_return_val_if_fail (proxy->priv, NULL);
3375 
3376 	return proxy->priv->filter_expr;
3377 }
3378 
3379 /**
3380  * gda_data_proxy_get_filtered_n_rows:
3381  * @proxy: a #GdaDataProxy object
3382  *
3383  * Get the total number of filtered rows in @proxy if a filter has been applied. As new rows
3384  * (rows added to the proxy and not yet added to the proxied data model) and rows to remove
3385  * (rows marked for removal but not yet removed from the proxied data model) are also filtered,
3386  * the returned number also contains references to new rows and rows to be removed.
3387  *
3388  * Returns: the number of filtered rows in @proxy, or -1 if no filter has been applied
3389  */
3390 gint
gda_data_proxy_get_filtered_n_rows(GdaDataProxy * proxy)3391 gda_data_proxy_get_filtered_n_rows (GdaDataProxy *proxy)
3392 {
3393 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), -1);
3394 	g_return_val_if_fail (proxy->priv, -1);
3395 
3396 	gda_mutex_lock (proxy->priv->mutex);
3397 	if (! proxy->priv->filtered_rows) {
3398 		gda_mutex_unlock (proxy->priv->mutex);
3399 		return -1;
3400 	}
3401 	else {
3402 		gint n = gda_data_model_get_n_rows (proxy->priv->filtered_rows);
3403 		gda_mutex_unlock (proxy->priv->mutex);
3404 		return n;
3405 	}
3406 }
3407 
3408 /*
3409  * GdaDataModel interface implementation
3410  */
3411 static gint
gda_data_proxy_get_n_rows(GdaDataModel * model)3412 gda_data_proxy_get_n_rows (GdaDataModel *model)
3413 {
3414 	gint nbrows;
3415 	GdaDataProxy *proxy;
3416 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
3417 	proxy = GDA_DATA_PROXY (model);
3418 	g_return_val_if_fail (proxy->priv, -1);
3419 
3420 	gda_mutex_lock (proxy->priv->mutex);
3421 
3422 	if (proxy->priv->chunk && !proxy->priv->force_direct_mapping)
3423 		nbrows = proxy->priv->chunk->mapping->len;
3424 	else {
3425 		if (proxy->priv->model_nb_rows >= 0) {
3426 			if (proxy->priv->chunk_to && proxy->priv->chunk_to->mapping) {
3427 				nbrows = proxy->priv->chunk_proxy_nb_rows;
3428 			}
3429 			else
3430 				nbrows = proxy->priv->model_nb_rows +
3431 					g_slist_length (proxy->priv->new_rows);
3432 		}
3433 		else
3434 			return -1; /* unknown number of rows */
3435 	}
3436 	if (!proxy->priv->force_direct_mapping && proxy->priv->add_null_entry)
3437 		nbrows += 1;
3438 
3439 	gda_mutex_unlock (proxy->priv->mutex);
3440 
3441 	return nbrows;
3442 }
3443 
3444 static gint
gda_data_proxy_get_n_columns(GdaDataModel * model)3445 gda_data_proxy_get_n_columns (GdaDataModel *model)
3446 {
3447 	GdaDataProxy *proxy;
3448 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
3449 	proxy = GDA_DATA_PROXY (model);
3450 	g_return_val_if_fail (proxy->priv, -1);
3451 
3452 	return 2 * proxy->priv->model_nb_cols;
3453 }
3454 
3455 
3456 typedef struct {
3457 	gchar *name;
3458 	GType  type;
3459 } ExtraColAttrs;
3460 
create_columns(GdaDataProxy * proxy)3461 static void create_columns (GdaDataProxy *proxy)
3462 {
3463 	gint i;
3464 	if (proxy->priv->columns)
3465 		return;
3466 
3467 	proxy->priv->columns = g_new0 (GdaColumn *, 2 * proxy->priv->model_nb_cols);
3468 
3469 	/* current proxy's values */
3470 	for (i = 0; i < proxy->priv->model_nb_cols; i++) {
3471 		GdaColumn *orig;
3472 
3473 		orig = gda_data_model_describe_column (proxy->priv->model, i);
3474 		proxy->priv->columns[i] = gda_column_copy (orig);
3475 		gda_column_set_position (proxy->priv->columns[i], i);
3476 	}
3477 
3478 	/* proxied data model's values (original values), again reference columns from proxied data model */
3479 	for (; i < 2 * proxy->priv->model_nb_cols; i++) {
3480 		GdaColumn *orig;
3481 		const gchar *cname;
3482 		gchar *newname, *id;
3483 		gint k;
3484 
3485 		orig = gda_data_model_describe_column (proxy->priv->model,
3486 						       i -  proxy->priv->model_nb_cols);
3487 		proxy->priv->columns[i] = gda_column_copy (orig);
3488 		g_object_get ((GObject*) proxy->priv->columns[i], "id", &id, NULL);
3489 		if (id) {
3490 			gchar *newid;
3491 			newid = g_strdup_printf ("pre%s", id);
3492 			g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
3493 
3494 			/* make sure there is no duplicate ID */
3495 			for (k = 0; ; k++) {
3496 				gint j;
3497 				for (j = 0; j < i; j++) {
3498 					gchar *id2;
3499 					g_object_get ((GObject*) proxy->priv->columns[j], "id", &id2, NULL);
3500 					if (id2 && *id2 && !strcmp (id2, newid)) {
3501 						g_free (id2);
3502 						break;
3503 					}
3504 				}
3505 				if (j == i)
3506 					break;
3507 
3508 				g_free (newid);
3509 				newid = g_strdup_printf ("pre%s_%d", id, k);
3510 				g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
3511 			}
3512 			g_free (newid);
3513 			g_free (id);
3514 		}
3515 
3516 		cname =  gda_column_get_name (orig);
3517 		if (cname && *cname)
3518 			newname = g_strdup_printf ("pre%s", cname);
3519 		else
3520 			newname = g_strdup_printf ("pre%d", i);
3521 
3522 		/* make sure there is no duplicate name */
3523 		for (k = 0; ; k++) {
3524 			gint j;
3525 			for (j = 0; j < i; j++) {
3526 				const gchar *cname2;
3527 				cname2 =  gda_column_get_name (proxy->priv->columns[j]);
3528 				if (cname2 && *cname2 && !strcmp (cname2, newname))
3529 					break;
3530 			}
3531 			if (j == i)
3532 				break;
3533 			g_free (newname);
3534 			if (cname && *cname)
3535 				newname = g_strdup_printf ("pre%s_%d", cname, k);
3536 			else
3537 				newname = g_strdup_printf ("pre%d_%d", i, k);
3538 		}
3539 		gda_column_set_name (proxy->priv->columns[i], newname);
3540 		gda_column_set_description (proxy->priv->columns[i], newname);
3541 		g_free (newname);
3542 		gda_column_set_position (proxy->priv->columns[i], i);
3543 	}
3544 }
3545 
3546 static GdaColumn *
gda_data_proxy_describe_column(GdaDataModel * model,gint col)3547 gda_data_proxy_describe_column (GdaDataModel *model, gint col)
3548 {
3549 	GdaDataProxy *proxy;
3550 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), NULL);
3551 	proxy = GDA_DATA_PROXY (model);
3552 	g_return_val_if_fail (proxy->priv, NULL);
3553 
3554 	gda_mutex_lock (proxy->priv->mutex);
3555 	if (!proxy->priv->columns)
3556 		create_columns (proxy);
3557 	gda_mutex_unlock (proxy->priv->mutex);
3558 	if ((col < 0) || (col >= 2 * proxy->priv->model_nb_cols)) {
3559 		g_warning (_("Column %d out of range (0-%d)"), col,
3560 			   gda_data_model_get_n_columns (model) - 1);
3561 		return NULL;
3562 	}
3563 	else
3564 		return proxy->priv->columns [col];
3565 }
3566 
3567 static const GValue *
gda_data_proxy_get_value_at(GdaDataModel * model,gint column,gint proxy_row,GError ** error)3568 gda_data_proxy_get_value_at (GdaDataModel *model, gint column, gint proxy_row, GError **error)
3569 {
3570 	gint model_row;
3571 	GValue *retval = NULL;
3572 	GdaDataProxy *proxy;
3573 	static GValue *null_value = NULL;
3574 
3575 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), NULL);
3576 	proxy = GDA_DATA_PROXY (model);
3577 	g_return_val_if_fail (proxy->priv, NULL);
3578 	g_return_val_if_fail (proxy_row >= 0, NULL);
3579 
3580 	gda_mutex_lock (proxy->priv->mutex);
3581 
3582 	if ((proxy_row == 0) && proxy->priv->add_null_entry) {
3583 		if (!null_value)
3584 			null_value = gda_value_new_null ();
3585 		gda_mutex_unlock (proxy->priv->mutex);
3586 		return null_value;
3587 	}
3588 
3589 	model_row = proxy_row_to_model_row (proxy, proxy_row);
3590 
3591 	/* current proxy's values (values may be different than the ones in the proxied data model) */
3592 	if (column < proxy->priv->model_nb_cols) {
3593 		RowModif *rm;
3594 		gint model_col = column % proxy->priv->model_nb_cols;
3595 		gboolean value_has_modifs = FALSE;
3596 
3597 		rm = proxy_row_to_row_modif (proxy, proxy_row);
3598 		if (rm && rm->modify_values) {
3599 			/* there are some modifications to the row, see if there are some for the column */
3600 			GSList *list;
3601 			RowValue *rv = NULL;
3602 
3603 			list = rm->modify_values;
3604 			while (list && !rv) {
3605 				if (ROW_VALUE (list->data)->model_column == model_col)
3606 					rv = ROW_VALUE (list->data);
3607 				list = g_slist_next (list);
3608 			}
3609 			if (rv) {
3610 				value_has_modifs = TRUE;
3611 				retval = rv->value;
3612 				if (!retval) {
3613 					if (!null_value)
3614 						null_value = gda_value_new_null ();
3615 					retval = null_value;
3616 				}
3617 			}
3618 		}
3619 
3620 		if (!value_has_modifs) {
3621 			/* value has not been modified */
3622 			if (model_row >= 0) {
3623 				/* existing row */
3624 				retval = (GValue *) gda_data_model_get_value_at (proxy->priv->model, column, model_row, error);
3625 			}
3626 			else {
3627 				/* non existing row, return NULL */
3628 				gint n;
3629 				n = gda_data_model_get_n_rows (model);
3630 				if (n > 0)
3631 					g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3632 						     _("Row %d out of range (0-%d)"), proxy_row, n - 1);
3633 				else
3634 					g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3635 						     _("Row %d not found (empty data model)"), proxy_row);
3636 				retval = NULL;
3637 			}
3638 		}
3639 
3640 		gda_mutex_unlock (proxy->priv->mutex);
3641 		return retval;
3642 	}
3643 
3644 	/* proxied data model's values (original values) */
3645 	if (column < 2 *proxy->priv->model_nb_cols) {
3646 		RowModif *rm;
3647 		gint model_col = column % proxy->priv->model_nb_cols;
3648 
3649 		rm = proxy_row_to_row_modif (proxy, proxy_row);
3650 		if (rm) {
3651 			if (rm->orig_values)
3652 				retval = rm->orig_values [model_col];
3653 			else {
3654 				if (!null_value)
3655 					null_value = gda_value_new_null ();
3656 				retval = null_value;
3657 			}
3658 		}
3659 		else {
3660 			if (model_row >= 0) {
3661 				/* existing row */
3662 				retval = (GValue *) gda_data_model_get_value_at (proxy->priv->model, model_col, model_row, error);
3663 			}
3664 			else {
3665 				/* non existing row, return NULL */
3666 				gint n;
3667 				n = gda_data_model_get_n_rows (model);
3668 				if (n > 0)
3669 					g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3670 						     _("Row %d out of range (0-%d)"), proxy_row, n - 1);
3671 				else
3672 					g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_ROW_OUT_OF_RANGE_ERROR,
3673 						     _("Row %d not found (empty data model)"), proxy_row);
3674 				retval = NULL;
3675 			}
3676 		}
3677 
3678 		gda_mutex_unlock (proxy->priv->mutex);
3679 		return retval;
3680 	}
3681 
3682 	g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_COLUMN_OUT_OF_RANGE_ERROR,
3683 		     _("Column %d out of range (0-%d)"), column, 2 *proxy->priv->model_nb_cols - 1);
3684 
3685 	gda_mutex_unlock (proxy->priv->mutex);
3686 	return NULL;
3687 }
3688 
3689 static GdaValueAttribute
gda_data_proxy_get_attributes_at(GdaDataModel * model,gint col,gint row)3690 gda_data_proxy_get_attributes_at (GdaDataModel *model, gint col, gint row)
3691 {
3692 	GdaValueAttribute attrs;
3693 	GdaDataProxy *proxy;
3694 
3695 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3696 	proxy = (GdaDataProxy*) model;
3697 	g_return_val_if_fail (proxy->priv, FALSE);
3698 
3699 	gda_mutex_lock (proxy->priv->mutex);
3700 	attrs = gda_data_proxy_get_value_attributes ((GdaDataProxy *) model, row, col);
3701 	gda_mutex_unlock (proxy->priv->mutex);
3702 	return attrs;
3703 }
3704 
3705 static gint
gda_data_proxy_find_row_from_values(GdaDataModel * model,GSList * values,gint * cols_index)3706 gda_data_proxy_find_row_from_values (GdaDataModel *model, GSList *values, gint *cols_index)
3707 {
3708 	gboolean found = FALSE;
3709 	gint proxy_row;
3710 	gint current_nb_rows;
3711 	GdaDataProxy *proxy;
3712 
3713 	proxy = (GdaDataProxy*) model;
3714 	g_return_val_if_fail (GDA_IS_DATA_PROXY (proxy), FALSE);
3715 	g_return_val_if_fail (proxy->priv, FALSE);
3716 	g_return_val_if_fail (values, FALSE);
3717 
3718 	gda_mutex_lock (proxy->priv->mutex);
3719 
3720 	/* ensure that there is no sync to be done */
3721 	ensure_chunk_sync (proxy);
3722 
3723 	/* FIXME: use a virtual connection here with some SQL, it'll be much easier and will avoid
3724 	 * much code
3725 	 */
3726 	/*TO_IMPLEMENT;*/
3727 
3728 	/* if there are still some rows waiting to be added in the idle loop, then force them to be added
3729 	 * first, otherwise we might not find what we are looking for!
3730 	 */
3731 	if (proxy->priv->chunk_sync_idle_id) {
3732 		g_idle_remove_by_data (proxy);
3733 		proxy->priv->chunk_sync_idle_id = 0;
3734 		while (chunk_sync_idle (proxy)) ;
3735 	}
3736 
3737 	current_nb_rows = gda_data_proxy_get_n_rows ((GdaDataModel*) proxy);
3738 	for (proxy_row = 0; proxy_row < current_nb_rows; proxy_row++) {
3739 		gboolean allequal = TRUE;
3740 		GSList *list;
3741 		gint index;
3742 		const GValue *value;
3743 
3744 		for (list = values, index = 0;
3745 		     list;
3746 		     list = list->next, index++) {
3747 			if (cols_index)
3748 				g_return_val_if_fail (cols_index [index] < proxy->priv->model_nb_cols, FALSE);
3749 			value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy,
3750 							     cols_index ? cols_index [index] :
3751 							     index, proxy_row, NULL);
3752 			if ((!value) || !list->data ||
3753 			    (G_VALUE_TYPE (value) != G_VALUE_TYPE ((GValue *) list->data)) ||
3754 			    gda_value_compare ((GValue *) (list->data), (GValue *) value)) {
3755 				allequal = FALSE;
3756 				break;
3757 			}
3758 		}
3759 		if (allequal) {
3760 			found = TRUE;
3761 			break;
3762 		}
3763 	}
3764 
3765 	gda_mutex_unlock (proxy->priv->mutex);
3766 	return found ? proxy_row : -1;
3767 }
3768 
3769 static GdaDataModelAccessFlags
gda_data_proxy_get_access_flags(GdaDataModel * model)3770 gda_data_proxy_get_access_flags (GdaDataModel *model)
3771 {
3772 	GdaDataProxy *proxy;
3773 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), 0);
3774 	proxy = GDA_DATA_PROXY (model);
3775 	g_return_val_if_fail (proxy->priv, 0);
3776 
3777 	if (proxy->priv->model) {
3778 		GdaDataModelAccessFlags flags;
3779 		gda_mutex_lock (proxy->priv->mutex);
3780 		flags = gda_data_model_get_access_flags (proxy->priv->model) | GDA_DATA_MODEL_ACCESS_RANDOM;
3781 		gda_mutex_unlock (proxy->priv->mutex);
3782 		return flags;
3783 	}
3784 	else
3785 		return 0;
3786 }
3787 
3788 static gboolean
gda_data_proxy_set_value_at(GdaDataModel * model,gint col,gint proxy_row,const GValue * value,GError ** error)3789 gda_data_proxy_set_value_at (GdaDataModel *model, gint col, gint proxy_row, const GValue *value,
3790 			     GError **error)
3791 {
3792 	GdaDataProxy *proxy;
3793 
3794 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3795 	proxy = GDA_DATA_PROXY (model);
3796 	g_return_val_if_fail (proxy->priv, FALSE);
3797 	g_return_val_if_fail (proxy_row >= 0, FALSE);
3798 	g_return_val_if_fail (value, FALSE);
3799 
3800 	gda_mutex_lock (proxy->priv->mutex);
3801 
3802 	/* ensure that there is no sync to be done */
3803 	ensure_chunk_sync (proxy);
3804 
3805 	if ((proxy_row == 0) && proxy->priv->add_null_entry) {
3806 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_ROW,
3807 			      "%s", _("The first row is an empty row artificially prepended and cannot be altered"));
3808 		gda_mutex_unlock (proxy->priv->mutex);
3809 		return FALSE;
3810 	}
3811 
3812 	/* current proxy's values (values may be different than the ones in the proxied data model) */
3813 	if ((col >= 0) && (col < proxy->priv->model_nb_cols)) {
3814 		/* Storing a GValue value */
3815 		RowModif *rm;
3816 		RowValue *rv = NULL;
3817 		const GValue *cmp_value;
3818 
3819 		/* compare with the current stored value */
3820 		cmp_value = gda_data_proxy_get_value_at ((GdaDataModel *) proxy, col, proxy_row, error);
3821 		if (!cmp_value) {
3822 			GdaValueAttribute attrs;
3823 			attrs = gda_data_proxy_get_value_attributes (proxy, proxy_row, col);
3824 			if (attrs & GDA_VALUE_ATTR_NO_MODIF) {
3825 				gda_mutex_unlock (proxy->priv->mutex);
3826 				return FALSE;
3827 			}
3828 			else {
3829 				GType exptype;
3830 				exptype = gda_column_get_g_type (gda_data_model_describe_column ((GdaDataModel *) proxy,
3831 												 col));
3832 				if ((G_VALUE_TYPE (value) != GDA_TYPE_NULL) &&
3833 				    (exptype != GDA_TYPE_NULL) &&
3834 				    (exptype != G_VALUE_TYPE (value))) {
3835 					gda_mutex_unlock (proxy->priv->mutex);
3836 					g_warning (_("Wrong value type: expected '%s' and got '%s'"),
3837 						   g_type_name (exptype),
3838 						   g_type_name (G_VALUE_TYPE (value)));
3839 					return FALSE;
3840 				}
3841 			}
3842 		}
3843 		else if ((G_VALUE_TYPE (cmp_value) != GDA_TYPE_NULL) &&
3844 			 (G_VALUE_TYPE (value) != GDA_TYPE_NULL) &&
3845 			 (G_VALUE_TYPE (value) != G_VALUE_TYPE (cmp_value))) {
3846 			gda_mutex_unlock (proxy->priv->mutex);
3847 			g_warning (_("Wrong value type: expected '%s' and got '%s'"),
3848 				   g_type_name (G_VALUE_TYPE (cmp_value)),
3849 				   g_type_name (G_VALUE_TYPE (value)));
3850 			return FALSE;
3851 		}
3852 		else if (! gda_value_compare ((GValue *) value, (GValue *) cmp_value)) {
3853 			/* nothing to do: values are equal */
3854 			gda_mutex_unlock (proxy->priv->mutex);
3855 			return TRUE;
3856 		}
3857 
3858 		/* from now on we have a new value for the row */
3859 		rm = find_or_create_row_modif (proxy, proxy_row, col, &rv);
3860 
3861 		if (rv) {
3862 			/* compare with the original value (before modifications) and either
3863 			 * delete the RowValue or alter it */
3864 			if (rv->value) {
3865 				gda_value_free (rv->value);
3866 				rv->value = NULL;
3867 			}
3868 
3869 			if (rm->orig_values && (col < rm->orig_values_size) &&
3870 			    rm->orig_values [col] &&
3871 			    ! gda_value_compare ((GValue *) value, rm->orig_values [col])) {
3872 				/* remove the RowValue */
3873 				rm->modify_values = g_slist_remove (rm->modify_values, rv);
3874 				g_free (rv);
3875 				rv = NULL;
3876 			}
3877 			else {
3878 				/* simply alter the RowValue */
3879 				GdaValueAttribute flags = rv->attributes;
3880 
3881 				if (value && !gda_value_is_null ((GValue *) value)) {
3882 					flags &= ~GDA_VALUE_ATTR_IS_NULL;
3883 					rv->value = gda_value_copy ((GValue*) value);
3884 				}
3885 				else
3886 					flags |= GDA_VALUE_ATTR_IS_NULL;
3887 				rv->attributes = flags;
3888 			}
3889 		}
3890 		else {
3891 			/* create a new RowValue */
3892 			GdaValueAttribute flags = 0;
3893 
3894 			rv = g_new0 (RowValue, 1);
3895 			rv->row_modif = rm;
3896 			rv->model_column = col;
3897 			rv->attributes = proxy->priv->columns_attrs [col];
3898 			flags = rv->attributes;
3899 
3900 			if (value && !gda_value_is_null ((GValue*) value)) {
3901 				rv->value = gda_value_copy ((GValue*) value);
3902 				flags &= ~GDA_VALUE_ATTR_IS_NULL;
3903 			}
3904 			else
3905 				flags |= GDA_VALUE_ATTR_IS_NULL;
3906 			if (rm->model_row >= 0)
3907 				flags |= GDA_VALUE_ATTR_HAS_VALUE_ORIG;
3908 			else
3909 				flags &= ~GDA_VALUE_ATTR_HAS_VALUE_ORIG;
3910 			rv->attributes = flags;
3911 			rm->modify_values = g_slist_prepend (rm->modify_values, rv);
3912 		}
3913 
3914 		if (rv) {
3915 			GdaValueAttribute flags = rv->attributes;
3916 			flags &= ~GDA_VALUE_ATTR_IS_UNCHANGED;
3917 			flags &= ~GDA_VALUE_ATTR_IS_DEFAULT;
3918 			rv->attributes = flags;
3919 		}
3920 
3921 		if (!rm->to_be_deleted && !rm->modify_values && (rm->model_row >= 0)) {
3922 			/* remove that RowModif, it's useless */
3923 			gint tmp;
3924 			tmp = rm->model_row;
3925 			g_hash_table_remove (proxy->priv->modify_rows, &tmp);
3926 			proxy->priv->all_modifs = g_slist_remove (proxy->priv->all_modifs, rm);
3927 			row_modifs_free (rm);
3928 		}
3929 
3930 		if (proxy->priv->notify_changes)
3931 			gda_data_model_row_updated ((GdaDataModel *) proxy, proxy_row);
3932 	}
3933 	else {
3934 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_VALUE,
3935 			     _("Trying to change read-only column: %d"), col);
3936 		gda_mutex_unlock (proxy->priv->mutex);
3937 		return FALSE;
3938 	}
3939 
3940 	gda_mutex_unlock (proxy->priv->mutex);
3941 	return TRUE;
3942 }
3943 
3944 static gboolean
gda_data_proxy_set_values(GdaDataModel * model,gint row,GList * values,GError ** error)3945 gda_data_proxy_set_values (GdaDataModel *model, gint row, GList *values, GError **error)
3946 {
3947 	GdaDataProxy *proxy;
3948 	gboolean notify_changes;
3949 	gint col;
3950 	GList *list;
3951 	gboolean err = FALSE;
3952 
3953 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
3954 	proxy = GDA_DATA_PROXY (model);
3955 	g_return_val_if_fail (proxy->priv, FALSE);
3956 	if (!values)
3957 		return TRUE;
3958 
3959 	g_return_val_if_fail ((gint)g_list_length (values) <= gda_data_proxy_get_n_columns (model), FALSE);
3960 
3961 	/* check values */
3962 	col = 0;
3963 	list = values;
3964 	while (list && !err) {
3965 		GValue *value = (GValue *)(list->data);
3966 
3967 		if (value && !gda_value_is_null (value)) {
3968 			GdaColumn *column;
3969 			column = gda_data_model_describe_column (model, col);
3970 			if (gda_column_get_g_type (column) != G_VALUE_TYPE (value)) {
3971 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_VALUE_TYPE_ERROR,
3972 					     _("Value type mismatch %s instead of %s"),
3973 					     gda_g_type_to_string (G_VALUE_TYPE (value)),
3974 					     gda_g_type_to_string (gda_column_get_g_type (column)));
3975 				err = TRUE;
3976 			}
3977 		}
3978 		col++;
3979 		list = g_list_next (list);
3980 	}
3981 
3982 	/* stop here if there is a value error */
3983 	if (err)
3984 		return FALSE;
3985 
3986 	gda_mutex_lock (proxy->priv->mutex);
3987 
3988 	/* temporary disable changes notification */
3989 	notify_changes = proxy->priv->notify_changes;
3990 	proxy->priv->notify_changes = FALSE;
3991 
3992 	for (col = 0, list = values; list; col ++, list = list->next) {
3993 		if (list->data &&
3994 		    !gda_data_proxy_set_value_at (model, col, row, (GValue *)(list->data), error)) {
3995 			err = TRUE;
3996 			break;
3997 		}
3998 	}
3999 
4000 	proxy->priv->notify_changes = notify_changes;
4001 	if (col && proxy->priv->notify_changes)
4002 		/* at least one successful value change occurred */
4003 		gda_data_model_row_updated (model, row);
4004 
4005 	gda_mutex_unlock (proxy->priv->mutex);
4006 	return !err;
4007 }
4008 
4009 static gint
gda_data_proxy_append_values(GdaDataModel * model,const GList * values,GError ** error)4010 gda_data_proxy_append_values (GdaDataModel *model, const GList *values, GError **error)
4011 {
4012 	GdaDataProxy *proxy;
4013 	gint newrow;
4014 	gboolean notify_changes;
4015 
4016 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
4017 	proxy = GDA_DATA_PROXY (model);
4018 	g_return_val_if_fail (proxy->priv, -1);
4019 
4020 	gda_mutex_lock (proxy->priv->mutex);
4021 
4022 	/* ensure that there is no sync to be done */
4023 	ensure_chunk_sync (proxy);
4024 
4025 	/* temporary disable changes notification */
4026 	notify_changes = proxy->priv->notify_changes;
4027 	proxy->priv->notify_changes = FALSE;
4028 
4029 	newrow = gda_data_proxy_append (proxy);
4030 	if (! gda_data_proxy_set_values (model, newrow, (GList *) values, error)) {
4031 		gda_data_proxy_remove_row (model, newrow, NULL);
4032 		proxy->priv->notify_changes = notify_changes;
4033 		gda_mutex_unlock (proxy->priv->mutex);
4034 		return -1;
4035 	}
4036 	else {
4037 		proxy->priv->notify_changes = notify_changes;
4038 		if (proxy->priv->notify_changes)
4039 			gda_data_model_row_inserted (model, newrow);
4040 		gda_mutex_unlock (proxy->priv->mutex);
4041 		return newrow;
4042 	}
4043 }
4044 
4045 static gint
gda_data_proxy_append_row(GdaDataModel * model,G_GNUC_UNUSED GError ** error)4046 gda_data_proxy_append_row (GdaDataModel *model, G_GNUC_UNUSED GError **error)
4047 {
4048 	GdaDataProxy *proxy;
4049 	gint i;
4050 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), -1);
4051 	proxy = GDA_DATA_PROXY (model);
4052 	g_return_val_if_fail (proxy->priv, -1);
4053 
4054 	gda_mutex_lock (proxy->priv->mutex);
4055 	i = gda_data_proxy_append (proxy);
4056 	gda_mutex_unlock (proxy->priv->mutex);
4057 	return i;
4058 }
4059 
4060 static gboolean
gda_data_proxy_remove_row(GdaDataModel * model,gint row,GError ** error)4061 gda_data_proxy_remove_row (GdaDataModel *model, gint row, GError **error)
4062 {
4063 	GdaDataProxy *proxy;
4064 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
4065 	proxy = GDA_DATA_PROXY (model);
4066 	g_return_val_if_fail (proxy->priv, FALSE);
4067 
4068 	gda_mutex_lock (proxy->priv->mutex);
4069 
4070 	if (proxy->priv->add_null_entry && row == 0) {
4071 		g_set_error (error, GDA_DATA_PROXY_ERROR, GDA_DATA_PROXY_READ_ONLY_ROW,
4072 			      "%s", _("The first row is an empty row artificially prepended and cannot be removed"));
4073 		gda_mutex_unlock (proxy->priv->mutex);
4074 		return FALSE;
4075 	}
4076 
4077 	gda_data_proxy_delete (proxy, row);
4078 	gda_mutex_unlock (proxy->priv->mutex);
4079 	return TRUE;
4080 }
4081 
4082 static void
gda_data_proxy_set_notify(GdaDataModel * model,gboolean do_notify_changes)4083 gda_data_proxy_set_notify (GdaDataModel *model, gboolean do_notify_changes)
4084 {
4085 	GdaDataProxy *proxy;
4086 	g_return_if_fail (GDA_IS_DATA_PROXY (model));
4087 	proxy = GDA_DATA_PROXY (model);
4088 	g_return_if_fail (proxy->priv);
4089 
4090 	gda_mutex_lock (proxy->priv->mutex);
4091 	proxy->priv->notify_changes = do_notify_changes;
4092 	gda_mutex_unlock (proxy->priv->mutex);
4093 }
4094 
4095 static gboolean
gda_data_proxy_get_notify(GdaDataModel * model)4096 gda_data_proxy_get_notify (GdaDataModel *model)
4097 {
4098 	GdaDataProxy *proxy;
4099 	g_return_val_if_fail (GDA_IS_DATA_PROXY (model), FALSE);
4100 	proxy = GDA_DATA_PROXY (model);
4101 	g_return_val_if_fail (proxy->priv, FALSE);
4102 
4103 	return proxy->priv->notify_changes;
4104 }
4105 
4106 static void
gda_data_proxy_send_hint(GdaDataModel * model,GdaDataModelHint hint,const GValue * hint_value)4107 gda_data_proxy_send_hint (GdaDataModel *model, GdaDataModelHint hint, const GValue *hint_value)
4108 {
4109 	GdaDataProxy *proxy;
4110 	g_return_if_fail (GDA_IS_DATA_PROXY (model));
4111 	proxy = GDA_DATA_PROXY (model);
4112 	g_return_if_fail (proxy->priv);
4113 
4114 	if (proxy->priv->model)
4115 		gda_data_model_send_hint (proxy->priv->model, hint, hint_value);
4116 }
4117 
4118 /*
4119  * Cache management
4120  */
4121 static void
clean_cached_changes(GdaDataProxy * proxy)4122 clean_cached_changes (GdaDataProxy *proxy)
4123 {
4124 	while (proxy->priv->cached_modifs) {
4125 		row_modifs_free (ROW_MODIF (proxy->priv->cached_modifs->data));
4126 		proxy->priv->cached_modifs = g_slist_delete_link (proxy->priv->cached_modifs,
4127 								  proxy->priv->cached_modifs);
4128 	}
4129 	while (proxy->priv->cached_inserts) {
4130 		row_modifs_free (ROW_MODIF (proxy->priv->cached_inserts->data));
4131 		proxy->priv->cached_inserts = g_slist_delete_link (proxy->priv->cached_inserts,
4132 								  proxy->priv->cached_inserts);
4133 	}
4134 }
4135 
4136 static void
migrate_current_changes_to_cache(GdaDataProxy * proxy)4137 migrate_current_changes_to_cache (GdaDataProxy *proxy)
4138 {
4139 	while (proxy->priv->all_modifs) {
4140 		RowModif *rm;
4141 		rm = (RowModif*) proxy->priv->all_modifs->data;
4142 #ifdef GDA_DEBUG_NO
4143 		g_print ("=== cached RM %p for row %d\n", rm, rm->model_row);
4144 #endif
4145 		if (rm->model_row == -1)
4146 			proxy->priv->cached_inserts = g_slist_prepend (proxy->priv->cached_inserts, rm);
4147 		else
4148 			proxy->priv->cached_modifs = g_slist_prepend (proxy->priv->cached_modifs, rm);
4149 		proxy->priv->all_modifs = g_slist_delete_link (proxy->priv->all_modifs,
4150 							       proxy->priv->all_modifs);
4151 	}
4152 	g_hash_table_remove_all (proxy->priv->modify_rows);
4153 	if (proxy->priv->new_rows) {
4154 		g_slist_free (proxy->priv->new_rows);
4155 		proxy->priv->new_rows = NULL;
4156 	}
4157 }
4158 
4159 static void
fetch_current_cached_changes(GdaDataProxy * proxy)4160 fetch_current_cached_changes (GdaDataProxy *proxy)
4161 {
4162 	GSList *list;
4163 	gint ncols;
4164 	g_return_if_fail (proxy->priv->model);
4165 
4166 	ncols = proxy->priv->model_nb_cols;
4167 
4168 	/* handle INSERT Row Modifs */
4169 	for (list = proxy->priv->cached_inserts; list;) {
4170 		RowModif *rm = (RowModif*) list->data;
4171 		if (rm->orig_values_size != ncols) {
4172 			list = list->next;
4173 			continue;
4174 		}
4175 
4176 		gint i;
4177 		for (i = 0; i < ncols; i++) {
4178 			GdaColumn *gcol;
4179 			const GValue *cv = NULL;
4180 			gcol = gda_data_model_describe_column (proxy->priv->model, i);
4181 
4182 			GSList *rvl;
4183 			for (rvl = rm->modify_values; rvl; rvl = rvl->next) {
4184 				RowValue *rv = ROW_VALUE (rvl->data);
4185 				if (rv->model_column == i) {
4186 					cv = rv->value;
4187 					break;
4188 				}
4189 			}
4190 
4191 			if (cv && (G_VALUE_TYPE (cv) != GDA_TYPE_NULL) &&
4192 			    G_VALUE_TYPE (cv) != gda_column_get_g_type (gcol))
4193 				break;
4194 		}
4195 
4196 		if (i == ncols) {
4197 			/* Matched! => move that Row Modif from cache */
4198 			GSList *nlist;
4199 			nlist = list->next;
4200 			proxy->priv->cached_inserts = g_slist_delete_link (proxy->priv->cached_inserts,
4201 									   list);
4202 			proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
4203 			proxy->priv->new_rows = g_slist_append (proxy->priv->new_rows, rm);
4204 #ifdef GDA_DEBUG_NO
4205 			g_print ("=== fetched RM %p for row %d\n", rm, rm->model_row);
4206 #endif
4207 			list = nlist;
4208 		}
4209 		else
4210 			list = list->next;
4211 	}
4212 
4213 	/* handle UPDATE and DELETE Row Modifs */
4214 	if (! proxy->priv->cached_modifs)
4215 		return;
4216 
4217 	GdaDataModelIter *iter;
4218 	iter = gda_data_model_create_iter (proxy->priv->model);
4219 	while (gda_data_model_iter_move_next (iter)) {
4220 		for (list = proxy->priv->cached_modifs; list; list = list->next) {
4221 			RowModif *rm = (RowModif*) list->data;
4222 			const GValue *v1, *v2;
4223 			if (rm->orig_values_size != ncols)
4224 				continue;
4225 
4226 			gint i;
4227 			for (i = 0; i < ncols; i++) {
4228 				v1 = gda_data_model_iter_get_value_at (iter, i);
4229 				v2 = rm->orig_values [i];
4230 
4231 				if ((v1 && !v2) || (!v1 && v2))
4232 					break;
4233 				else if (v1 && v2) {
4234 					if ((G_VALUE_TYPE (v1) != G_VALUE_TYPE (v2)) ||
4235 					    gda_value_differ (v1, v2))
4236 						break;
4237 				}
4238 			}
4239 
4240 			if (i == ncols) {
4241 				/* Matched! => move that Row Modif from cache */
4242 				proxy->priv->cached_modifs = g_slist_delete_link (proxy->priv->cached_modifs,
4243 										  list);
4244 				proxy->priv->all_modifs = g_slist_prepend (proxy->priv->all_modifs, rm);
4245 
4246 				gint *ptr;
4247 				ptr = g_new (gint, 1);
4248 #ifdef GDA_DEBUG_NO
4249 				g_print ("=== fetched RM %p for row %d (old %d)\n", rm, gda_data_model_iter_get_row (iter), rm->model_row);
4250 #endif
4251 				rm->model_row = gda_data_model_iter_get_row (iter);
4252 				*ptr = rm->model_row;
4253 				g_hash_table_insert (proxy->priv->modify_rows, ptr, rm);
4254 				break; /* the FOR over cached_modifs, as there can only be 1 Row Modif
4255 					* for the row iter is on */
4256 			}
4257 		}
4258 	}
4259 	g_object_unref (iter);
4260 }
4261