[libgda] Optimized virtual tables usage



commit 3fedfba98df0e98e803c6965ad5a397ec4b7ce33
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Oct 26 20:49:46 2010 +0200

    Optimized virtual tables usage
    
    - make use of the infrastructure provided by SQLite to pre-filter
      data model's contents when possible
    - require only GDA_STATEMENT_MODEL_CURSOR_FORWARD data models

 ChangeLog                                          |   14 -
 doc/C/tmpl/gda-vconnection-data-model.sgml         |    4 +-
 doc/C/tmpl/gda-virtual-connection.sgml             |    2 +-
 doc/C/tmpl/gda-vprovider-data-model.sgml           |    8 +-
 doc/C/tmpl/gda-vprovider-hub.sgml                  |   11 +-
 doc/C/virtual.xml                                  |    2 +
 libgda/gda-data-model.c                            |    6 +
 libgda/gda-data-proxy.c                            |   30 ++-
 libgda/gda-set.c                                   |    2 +-
 libgda/sqlite/gda-symbols-util.c                   |    4 +
 libgda/sqlite/gda-symbols-util.h                   |    2 +
 libgda/sqlite/virtual/gda-vconnection-data-model.c |    2 +-
 libgda/sqlite/virtual/gda-vconnection-data-model.h |   54 ++-
 libgda/sqlite/virtual/gda-vconnection-hub.c        |  487 ++++++++++++++++++--
 libgda/sqlite/virtual/gda-virtual-connection.c     |    9 +-
 libgda/sqlite/virtual/gda-virtual-connection.h     |    5 +-
 libgda/sqlite/virtual/gda-vprovider-data-model.c   |  482 +++++++++++++-------
 testing/.gitignore                                 |    2 +
 testing/Makefile.am                                |   17 +-
 {libgda/sqlite/virtual => testing}/names.csv       |    0
 {libgda/sqlite/virtual => testing}/test_model.xml  |    0
 testing/virtual-test-2.c                           |  113 +++++
 {libgda/sqlite/virtual => testing}/virtual-test.c  |   29 +-
 23 files changed, 1037 insertions(+), 248 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 5009e0f..9fc0f8d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,16 +1,2 @@
-2010-10-16  Murray Cumming  <murrayc murrayc-x61>
-
-	reviewed by: <delete if not using a buddy>
-
-	* libgda/gda-meta-store.c:
-	* libgda/sql-parser/gda-statement-struct-compound.c:
-	* libgda/sql-parser/gda-statement-struct-delete.c:
-	* libgda/sql-parser/gda-statement-struct-insert.c:
-	* libgda/sql-parser/gda-statement-struct-pspec.c:
-	* libgda/sql-parser/gda-statement-struct-select.c:
-	* libgda/sql-parser/gda-statement-struct-trans.c:
-	* libgda/sql-parser/gda-statement-struct-unknown.c:
-	* libgda/sql-parser/gda-statement-struct-update.c:
-
 The ChangeLog is auto-generated when releasing. If you
 are seeing this, use 'git log' for a detailed list of changes.
diff --git a/doc/C/tmpl/gda-vconnection-data-model.sgml b/doc/C/tmpl/gda-vconnection-data-model.sgml
index 0bdbc73..1702e55 100644
--- a/doc/C/tmpl/gda-vconnection-data-model.sgml
+++ b/doc/C/tmpl/gda-vconnection-data-model.sgml
@@ -46,8 +46,8 @@ The #GdaVproviderDataModel provider to use to create such connection objects.
 @data_model: 
 @create_columns_func: 
 @create_model_func: 
- _gda_reserved1: 
- _gda_reserved2: 
+ create_filter_func: 
+ create_filtered_model_func: 
 
 <!-- ##### USER_FUNCTION GdaVconnectionDataModelCreateColumnsFunc ##### -->
 <para>
diff --git a/doc/C/tmpl/gda-virtual-connection.sgml b/doc/C/tmpl/gda-virtual-connection.sgml
index aa759af..4cf79a3 100644
--- a/doc/C/tmpl/gda-virtual-connection.sgml
+++ b/doc/C/tmpl/gda-virtual-connection.sgml
@@ -62,7 +62,7 @@ This is a base virtual class for all virtual connection implementations
 
 </para>
 
- cnc: 
+ vcnc: 
 @Returns: 
 
 
diff --git a/doc/C/tmpl/gda-vprovider-data-model.sgml b/doc/C/tmpl/gda-vprovider-data-model.sgml
index c4e4907..d286f5e 100644
--- a/doc/C/tmpl/gda-vprovider-data-model.sgml
+++ b/doc/C/tmpl/gda-vprovider-data-model.sgml
@@ -6,14 +6,14 @@ Virtual provider for connections based on a list of GdaDataModel
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-This provider is used to create virtual connections in which each #GdaDataModel data models can be
-added as tables in the connection. Using gda_server_provider_create_connection() will generate a
-#GdaVconnectionDataModel connection object.
+  This provider is used to create virtual connections in which each #GdaDataModel data model can be
+  added as a table in the connection. Using gda_virtual_connection_open() with this provider as argument
+  will generate a #GdaVconnectionDataModel connection object, from which data models can be added.
 </para>
 
 <!-- ##### SECTION See_Also ##### -->
 <para>
-
+  See also the <link linkend="VirtualIntro">introduction to virtual connections</link>.
 </para>
 
 <!-- ##### SECTION Stability_Level ##### -->
diff --git a/doc/C/tmpl/gda-vprovider-hub.sgml b/doc/C/tmpl/gda-vprovider-hub.sgml
index 870810f..b7489a4 100644
--- a/doc/C/tmpl/gda-vprovider-hub.sgml
+++ b/doc/C/tmpl/gda-vprovider-hub.sgml
@@ -6,17 +6,18 @@ Virtual provider for connections based on other connection
 
 <!-- ##### SECTION Long_Description ##### -->
 <para>
-This provider is used to create virtual connections which "incorporate" tables from other connections. This is typically
-used when one need to compare or migrate data from one database to the other by creating two connections for each database,
-and "binding" them into a third virtual connection using this provider.
+  This provider is used to create virtual connections which "incorporate" tables from other connections. This is typically
+  used when one need to compare or migrate data from one database to the other by creating two connections for each database,
+  and "binding" them into a third virtual connection using this provider.
 </para>
 <para>
-Using gda_server_provider_create_connection() will generate a #GdaVconnectionHub connection object.
+  Using gda_virtual_connection_open() with this provider as argument
+  will generate a #GdaVconnectionHub connection object, from which connections can be added.
 </para>
 
 <!-- ##### SECTION See_Also ##### -->
 <para>
-
+  See also the <link linkend="VirtualIntro">introduction to virtual connections</link>.
 </para>
 
 <!-- ##### SECTION Stability_Level ##### -->
diff --git a/doc/C/virtual.xml b/doc/C/virtual.xml
index 99ea3df..551a690 100644
--- a/doc/C/virtual.xml
+++ b/doc/C/virtual.xml
@@ -32,6 +32,8 @@ cnc = gda_virtual_connection_open (provider, NULL);
       <listitem><para><filename class="directory">samples/Virtual</filename></para></listitem>
       <listitem><para><filename class="directory">samples/DirDataModel</filename></para></listitem>
       <listitem><para><filename class="directory">samples/F-Spot</filename></para></listitem>
+      <listitem><para><filename class="directory">testing/virtual-test.c</filename></para></listitem>
+      <listitem><para><filename class="directory">testing/virtual-test-2.c</filename></para></listitem>
     </itemizedlist>
   </para>
   &libgda-virtual-notice;
diff --git a/libgda/gda-data-model.c b/libgda/gda-data-model.c
index 4e37536..16911ba 100644
--- a/libgda/gda-data-model.c
+++ b/libgda/gda-data-model.c
@@ -784,6 +784,12 @@ gda_data_model_set_values (GdaDataModel *model, gint row, GList *values, GError
  * gda_data_model_iter_move_next() (and gda_data_model_iter_move_prev() if
  * supported).
  *
+ * Note: for the #GdaDataProxy data model (which proxies any #GdaDataModel for modifications and
+ * has twice the number of columns of the proxied data model), this method will create an iterator
+ * in which only the columns of the proxied data model appear. If you need to have a #GdaDataModelIter
+ * in which all the proxy's columns appear, create it using:
+ * <programlisting><![CDATA[iter = g_object_new (GDA_TYPE_DATA_MODEL_ITER, "data-model", proxy, NULL);]]></programlisting>
+ *
  * Returns: (transfer full): a #GdaDataModelIter object, or %NULL if an error occurred
  */
 GdaDataModelIter *
diff --git a/libgda/gda-data-proxy.c b/libgda/gda-data-proxy.c
index 4b2b6ac..c38eba7 100644
--- a/libgda/gda-data-proxy.c
+++ b/libgda/gda-data-proxy.c
@@ -3338,12 +3338,40 @@ static void create_columns (GdaDataProxy *proxy)
 	for (; i < 2 * proxy->priv->model_nb_cols; i++) {
 		GdaColumn *orig;
 		const gchar *cname;
-		gchar *newname;
+		gchar *newname, *id;
 		gint k;
 		
 		orig = gda_data_model_describe_column (proxy->priv->model, 
 						       i -  proxy->priv->model_nb_cols);
 		proxy->priv->columns[i] = gda_column_copy (orig);
+		g_object_get ((GObject*) proxy->priv->columns[i], "id", &id, NULL);
+		if (id) {
+			gchar *newid;
+			newid = g_strdup_printf ("pre%s", id);
+			g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
+
+			/* make sure there is no duplicate ID */
+			for (k = 0; ; k++) {
+				gint j;
+				for (j = 0; j < i; j++) {
+					gchar *id2;
+					g_object_get ((GObject*) proxy->priv->columns[j], "id", &id2, NULL);
+					if (id2 && *id2 && !strcmp (id2, newid)) {
+						g_free (id2);
+						break;
+					}
+				}
+				if (j == i)
+					break;
+
+				g_free (newid);
+				newid = g_strdup_printf ("pre%s_%d", id, k);
+				g_object_set ((GObject*) proxy->priv->columns[i], "id", newid, NULL);
+			}
+			g_free (newid);
+			g_free (id);
+		}
+
 		cname =  gda_column_get_name (orig);
 		if (cname && *cname) 
 			newname = g_strdup_printf ("pre%s", cname);
diff --git a/libgda/gda-set.c b/libgda/gda-set.c
index ad08ea1..8c1d705 100644
--- a/libgda/gda-set.c
+++ b/libgda/gda-set.c
@@ -1483,7 +1483,7 @@ gda_set_get_nth_holder (GdaSet *set, gint pos)
 		for (list = set->holders; list; list = list->next)
 			g_array_append_val (set->priv->holders_array, list->data);
 	}
-	if ((guint)pos > set->priv->holders_array->len)
+	if ((guint)pos >= set->priv->holders_array->len)
 		return NULL;
 	else
 		return g_array_index (set->priv->holders_array, GdaHolder*, pos);
diff --git a/libgda/sqlite/gda-symbols-util.c b/libgda/sqlite/gda-symbols-util.c
index a043934..ac144e1 100644
--- a/libgda/sqlite/gda-symbols-util.c
+++ b/libgda/sqlite/gda-symbols-util.c
@@ -267,6 +267,10 @@ load_symbols (GModule *module)
 		goto onerror;
 	if (! g_module_symbol (module, "sqlite3_value_int", (gpointer*) &(s3r->sqlite3_value_int)))
 		goto onerror;
+	if (! g_module_symbol (module, "sqlite3_value_int64", (gpointer*) &(s3r->sqlite3_value_int64)))
+		goto onerror;
+	if (! g_module_symbol (module, "sqlite3_value_double", (gpointer*) &(s3r->sqlite3_value_double)))
+		goto onerror;
 	if (! g_module_symbol (module, "sqlite3_value_text", (gpointer*) &(s3r->sqlite3_value_text)))
 		goto onerror;
 	if (! g_module_symbol (module, "sqlite3_value_type", (gpointer*) &(s3r->sqlite3_value_type)))
diff --git a/libgda/sqlite/gda-symbols-util.h b/libgda/sqlite/gda-symbols-util.h
index b543740..f26b646 100644
--- a/libgda/sqlite/gda-symbols-util.h
+++ b/libgda/sqlite/gda-symbols-util.h
@@ -104,6 +104,8 @@ typedef struct {
 	const void * (*sqlite3_value_blob)(sqlite3_value*);
 	int  (*sqlite3_value_bytes)(sqlite3_value*);
 	int  (*sqlite3_value_int)(sqlite3_value*);
+	double (*sqlite3_value_double)(sqlite3_value*);
+	sqlite3_int64 (*sqlite3_value_int64)(sqlite3_value*);
 	const unsigned char * (*sqlite3_value_text)(sqlite3_value*);
 	int  (*sqlite3_value_type)(sqlite3_value*);
 
diff --git a/libgda/sqlite/virtual/gda-vconnection-data-model.c b/libgda/sqlite/virtual/gda-vconnection-data-model.c
index 4e90571..23fed9f 100644
--- a/libgda/sqlite/virtual/gda-vconnection-data-model.c
+++ b/libgda/sqlite/virtual/gda-vconnection-data-model.c
@@ -233,7 +233,7 @@ gda_vconnection_data_model_add (GdaVconnectionDataModel *cnc, GdaVconnectionData
 	g_return_val_if_fail (GDA_IS_VCONNECTION_DATA_MODEL (cnc), FALSE);
 	g_return_val_if_fail (table_name && *table_name, FALSE);
 	g_return_val_if_fail (spec, FALSE);
-	g_return_val_if_fail (spec->data_model || (spec->create_columns_func && spec->create_model_func), FALSE);
+	g_return_val_if_fail (spec->data_model || (spec->create_columns_func && (spec->create_model_func || spec->create_filtered_model_func)), FALSE);
 	if (spec->data_model)
 		g_return_val_if_fail (GDA_IS_DATA_MODEL (spec->data_model), FALSE);
 
diff --git a/libgda/sqlite/virtual/gda-vconnection-data-model.h b/libgda/sqlite/virtual/gda-vconnection-data-model.h
index 2dbdf12..0eb7727 100644
--- a/libgda/sqlite/virtual/gda-vconnection-data-model.h
+++ b/libgda/sqlite/virtual/gda-vconnection-data-model.h
@@ -1,5 +1,5 @@
-/* GDA
- * Copyright (C) 2007 - 2009 The GNOME Foundation.
+/*
+ * Copyright (C) 2007 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -24,6 +24,7 @@
 #define __GDA_VCONNECTION_DATA_MODEL_H__
 
 #include <virtual/gda-virtual-connection.h>
+#include <sql-parser/gda-statement-struct-parts.h>
 
 #define GDA_TYPE_VCONNECTION_DATA_MODEL            (gda_vconnection_data_model_get_type())
 #define GDA_VCONNECTION_DATA_MODEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, GDA_TYPE_VCONNECTION_DATA_MODEL, GdaVconnectionDataModel))
@@ -37,21 +38,62 @@ typedef struct _GdaVconnectionDataModel      GdaVconnectionDataModel;
 typedef struct _GdaVconnectionDataModelClass GdaVconnectionDataModelClass;
 typedef struct _GdaVconnectionDataModelPrivate GdaVconnectionDataModelPrivate;
 typedef struct _GdaVconnectionDataModelSpec  GdaVconnectionDataModelSpec;
+typedef struct _GdaVconnectionDataModelFilter GdaVconnectionDataModelFilter;
 
 typedef GList        *(*GdaVconnectionDataModelCreateColumnsFunc) (GdaVconnectionDataModelSpec *, GError **);
 typedef GdaDataModel *(*GdaVconnectionDataModelCreateModelFunc)   (GdaVconnectionDataModelSpec *);
 typedef void (*GdaVconnectionDataModelFunc) (GdaDataModel *, const gchar *, gpointer );
 
+/* 
+ * Enabling pre-filtering when creating a data model to be used as a table,
+ * (structure closely mapped with SQLite's sqlite3_index_info type), to enable
+ * the data model to perform some filter tasks itself.
+ *
+ * A pointer to this structure is passed to the GdaVconnectionDataModelParseFilterFunc function
+ * and the function has to modify the variables in the *Outputs* section (and nowhere else)
+ *
+ * The @idxNum and @idxPointer are passed to the GdaVconnectionDataModelCreateFModelFunc function call
+ * and they represent nothing specific except that the GdaVconnectionDataModelParseFilterFunc and
+ * GdaVconnectionDataModelCreateFModelFunc functions need to agree on their meaning.
+ *
+ * See the gda-vconnection-hub.c file for an usage example.
+ */
+struct _GdaVconnectionDataModelFilter {
+	/* Inputs */
+	int nConstraint; /* Number of entries in aConstraint */
+	struct GdaVirtualConstraint {
+		int iColumn;       /* Column on left-hand side of constraint */
+		GdaSqlOperatorType op;  /* Constraint operator, among _EQ, _LT, _LEQ, _GT, _GEQ, _REGEXP */
+	} *aConstraint;            /* Table of WHERE clause constraints */
+	int nOrderBy;              /* Number of terms in the ORDER BY clause */
+	struct GdaVirtualOrderby {
+		int iColumn;       /* Column number */
+		gboolean desc;     /* TRUE for DESC.  FALSE for ASC. */
+	} *aOrderBy;               /* The ORDER BY clause */
+
+	/* Outputs */
+	struct GdaVirtualConstraintUsage {
+		int argvIndex;     /* if >0, constraint is part of argv to xFilter */
+		gboolean omit;     /* Do not code a test for this constraint if TRUE */
+	} *aConstraintUsage;
+	int idxNum;                /* Number used to identify the index */
+	gpointer idxPointer;       /* Pointer used to identify the index */
+	gboolean orderByConsumed;  /* TRUE if output is already ordered */
+	double estimatedCost;      /* Estimated cost of using this index */
+};
+
+typedef void          (*GdaVconnectionDataModelParseFilterFunc)   (GdaVconnectionDataModelSpec *, GdaVconnectionDataModelFilter *); 
+typedef GdaDataModel *(*GdaVconnectionDataModelCreateFModelFunc)  (GdaVconnectionDataModelSpec *,
+								   int, const char *, int, GValue **);
+
 struct _GdaVconnectionDataModelSpec {
 	GdaDataModel                             *data_model;
 	GdaVconnectionDataModelCreateColumnsFunc  create_columns_func;
 	GdaVconnectionDataModelCreateModelFunc    create_model_func;
 
-	/* Padding for future expansion */
-	void (*_gda_reserved1) (void);
-	void (*_gda_reserved2) (void);
+	GdaVconnectionDataModelParseFilterFunc    create_filter_func;
+	GdaVconnectionDataModelCreateFModelFunc   create_filtered_model_func;
 };
-
 #define GDA_VCONNECTION_DATA_MODEL_SPEC(x) ((GdaVconnectionDataModelSpec*)(x))
 
 struct _GdaVconnectionDataModel {
diff --git a/libgda/sqlite/virtual/gda-vconnection-hub.c b/libgda/sqlite/virtual/gda-vconnection-hub.c
index a242e07..f5d2982 100644
--- a/libgda/sqlite/virtual/gda-vconnection-hub.c
+++ b/libgda/sqlite/virtual/gda-vconnection-hub.c
@@ -1,6 +1,6 @@
 /* 
  * GDA common library
- * Copyright (C) 2007 - 2008 The GNOME Foundation.
+ * Copyright (C) 2007 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -28,6 +28,8 @@
 #include <sql-parser/gda-sql-parser.h>
 #include <libgda/gda-util.h>
 #include <libgda/gda-data-select.h>
+#include <gda-sql-builder.h>
+#include "../gda-sqlite.h"
 
 typedef struct {
 	GdaVconnectionHub *hub;
@@ -345,72 +347,499 @@ static void meta_changed_cb (GdaMetaStore *store, GSList *changes, HubConnection
 
 typedef struct {
 	GdaVconnectionDataModelSpec spec;
-	GValue *table_name;
+	GValue        *table_name;
 	HubConnection *hc;
+
+	GError        *cols_error;
+	gint           ncols;
+	gchar        **col_names; /* free using g_strfreev */
+	GType         *col_gtypes;/* free using g_free */
+	gchar        **col_dtypes;/* free using g_strfreev */
+
+	GHashTable    *filters_hash; /* key = string; value = a ComputedFilter pointer */
 } LocalSpec;
 
 static void local_spec_free (LocalSpec *spec)
 {
 	gda_value_free (spec->table_name);
+	if (spec->col_names)
+		g_strfreev (spec->col_names);
+	if (spec->col_gtypes)
+		g_free (spec->col_gtypes);
+	if (spec->col_dtypes)
+		g_strfreev (spec->col_dtypes);
+	g_clear_error (&(spec->cols_error));
+	if (spec->filters_hash)
+		g_hash_table_destroy (spec->filters_hash);
 	g_free (spec);
 }
 
-static GList *
-dict_table_create_columns_func (GdaVconnectionDataModelSpec *spec, GError **error)
+static void
+compute_column_specs (GdaVconnectionDataModelSpec *spec)
 {
 	LocalSpec *lspec = (LocalSpec *) spec;
 	gint i, nrows;
-	GList *columns = NULL;
 	GdaDataModel *model;
 
+	if (lspec->col_names)
+		return;
+
 	model = gda_connection_get_meta_store_data (lspec->hc->cnc, 
 						    GDA_CONNECTION_META_FIELDS, NULL, 1, "name", lspec->table_name);
 	if (!model)
-		return NULL;
+		return;
+
 	nrows = gda_data_model_get_n_rows (model);
+	lspec->col_names = g_new0 (gchar *, nrows+1);
+	lspec->col_gtypes = g_new0 (GType, nrows);
+	lspec->col_dtypes = g_new0 (gchar *, nrows+1);
+
 	for (i = 0; i < nrows; i++) {
-		GdaColumn *col;
 		const GValue *v0, *v1, *v2;
 
-		v0 = gda_data_model_get_value_at (model, 0, i, error);
-		v1 = gda_data_model_get_value_at (model, 1, i, error);
-		v2 = gda_data_model_get_value_at (model, 2, i, error);
+		v0 = gda_data_model_get_value_at (model, 0, i, NULL);
+		v1 = gda_data_model_get_value_at (model, 1, i, NULL);
+		v2 = gda_data_model_get_value_at (model, 2, i, NULL);
 
 		if (!v0 || !v1 || !v2) {
-			if (columns) {
-				g_list_foreach (columns, (GFunc) g_object_unref, NULL);
-				g_list_free (columns);
-				columns = NULL;
-			}
 			break;
 		}
 
+		lspec->col_names[i] = g_value_dup_string (v0);
+		lspec->col_gtypes[i] = gda_g_type_from_string (g_value_get_string (v2));
+		lspec->col_dtypes[i] = g_value_dup_string (v1);
+	}
+	g_object_unref (model);
+
+	if (i != nrows) {
+		/* there has been an error */
+		g_strfreev (lspec->col_names);
+		lspec->col_names = NULL;
+		g_free (lspec->col_gtypes);
+		lspec->col_gtypes = NULL;
+		g_strfreev (lspec->col_dtypes);
+		lspec->col_dtypes = NULL;
+		g_set_error (&(lspec->cols_error), GDA_META_STORE_ERROR, GDA_META_STORE_INTERNAL_ERROR,
+			     _("Unable to get information about table '%s'"),
+			     g_value_get_string (lspec->table_name));
+	}
+	else
+		lspec->ncols = nrows;
+}
+
+static GList *
+dict_table_create_columns_func (GdaVconnectionDataModelSpec *spec, GError **error)
+{
+	LocalSpec *lspec = (LocalSpec *) spec;
+	GList *columns = NULL;
+	gint i;
+
+	compute_column_specs (spec);
+	if (lspec->cols_error) {
+		if (error)
+			*error = g_error_copy (lspec->cols_error);
+		return NULL;
+	}
+
+	for (i = 0; i < lspec->ncols; i++) {
+		GdaColumn *col;
+
 		col = gda_column_new ();
-		gda_column_set_name (col, g_value_get_string (v0));
-		gda_column_set_g_type (col, gda_g_type_from_string (g_value_get_string (v2)));
-		gda_column_set_dbms_type (col, g_value_get_string (v1));
+		gda_column_set_name (col, lspec->col_names[i]);
+		gda_column_set_g_type (col, lspec->col_gtypes[i]);
+		gda_column_set_dbms_type (col, lspec->col_dtypes[i]);
 		columns = g_list_prepend (columns, col);
 	}
-	g_object_unref (model);
 
 	return g_list_reverse (columns);
 }
 
-static GdaDataModel *
-dict_table_create_model_func (GdaVconnectionDataModelSpec *spec)
+static gchar *
+make_string_for_sqlite3_index_info (sqlite3_index_info *info)
 {
-	GdaDataModel *model;
+	GString *string;
+	gint i;
+
+	string = g_string_new ("");
+	for (i = 0; i < info->nConstraint; i++) {
+		const struct sqlite3_index_constraint *cons;
+		cons = &(info->aConstraint [i]);
+		if (! cons->usable)
+			continue;
+		g_string_append_printf (string, "|%d,%d", cons->iColumn, cons->op);
+	}
+	g_string_append_c (string, '/');
+	for (i = 0; i < info->nOrderBy; i++) {
+		struct sqlite3_index_orderby *order;
+		order = &(info->aOrderBy[i]);
+		g_string_append_printf (string, "|%d,%d", order->iColumn, order->desc ? 1 : 0);
+	}
+	return g_string_free (string, FALSE);
+}
+
+typedef struct {
 	GdaStatement *stmt;
-	gchar *tmp;
+	int orderByConsumed;
+	struct sqlite3_index_constraint_usage *out_const;
+} ComputedFilter;
+
+static void
+computed_filter_free (ComputedFilter *filter)
+{
+	g_object_unref (filter->stmt);
+	g_free (filter->out_const);
+	g_free (filter);
+}
+
+static void
+dict_table_create_filter (GdaVconnectionDataModelSpec *spec, sqlite3_index_info *info)
+{
 	LocalSpec *lspec = (LocalSpec *) spec;
+	GdaSqlBuilder *b;
+	gint i;
+	gchar *hash;
+
+	compute_column_specs (spec);
+	if (lspec->cols_error)
+		return;
+
+	hash = make_string_for_sqlite3_index_info (info);
+	if (lspec->filters_hash) {
+		ComputedFilter *filter;
+		filter = g_hash_table_lookup (lspec->filters_hash, hash);
+		if (filter) {
+			info->idxStr = (char*) filter->stmt;
+			info->needToFreeIdxStr = FALSE;
+			info->orderByConsumed = filter->orderByConsumed;
+			memcpy (info->aConstraintUsage,
+				filter->out_const,
+				sizeof (struct sqlite3_index_constraint_usage) * info->nConstraint);
+			/*g_print ("Reusing filter %p, hash=[%s]\n", filter, hash);*/
+			g_free (hash);
+			return;
+		}
+	}
+
+	/* SELECT core */
+	b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+	for (i = 0; i < lspec->ncols; i++)
+		gda_sql_builder_select_add_field (b, lspec->col_names[i], NULL, NULL);
+	gda_sql_builder_select_add_target_id (b,
+					      gda_sql_builder_add_id (b, g_value_get_string (lspec->table_name)),
+					      NULL);
+
+	/* WHERE part */
+	gint argpos;
+	GdaSqlBuilderId *op_ids;
+	op_ids = g_new (GdaSqlBuilderId, info->nConstraint);
+	for (i = 0, argpos = 0; i < info->nConstraint; i++) {
+		const struct sqlite3_index_constraint *cons;
+		cons = &(info->aConstraint [i]);
+		if (! cons->usable)
+			continue;
+		if (cons->iColumn >= lspec->ncols) {
+			g_warning ("Internal error: column known by SQLite's virtual table %d is not known for "
+				   "table '%s', which has %d column(s)", cons->iColumn,
+				   g_value_get_string (lspec->table_name), lspec->ncols);
+			continue;
+		}
+
+		GdaSqlBuilderId fid, pid, eid;
+		gchar *pname;
+
+		if (lspec->col_gtypes[cons->iColumn] == GDA_TYPE_BLOB) /* ignore BLOBs */
+			continue;
+
+		fid = gda_sql_builder_add_id (b, lspec->col_names[cons->iColumn]);
+		pname = g_strdup_printf ("param%d", argpos);
+		pid = gda_sql_builder_add_param (b, pname, lspec->col_gtypes[cons->iColumn], TRUE);
+		g_free (pname);
+		eid = gda_sql_builder_add_cond (b, cons->op, fid, pid, 0);
+		op_ids[argpos] = eid;
+
+		/* update info->aConstraintUsage */
+		info->aConstraintUsage [i].argvIndex = argpos+1;
+		info->aConstraintUsage [i].omit = 1;
+		argpos++;
+	}
+	if (argpos > 0) {
+		GdaSqlBuilderId whid;
+		whid = gda_sql_builder_add_cond_v (b, GDA_SQL_OPERATOR_TYPE_AND, op_ids, argpos);
+		gda_sql_builder_set_where (b, whid);
+	}
+	g_free (op_ids);
 	
-	tmp = g_strdup_printf ("SELECT * FROM %s", g_value_get_string (lspec->table_name));
-	stmt = gda_sql_parser_parse_string (internal_parser, tmp, NULL, NULL);
-	g_free (tmp);
-	model = gda_connection_statement_execute_select (lspec->hc->cnc, stmt, NULL, NULL);
+	/* ORDER BY part */
+	info->orderByConsumed = TRUE;
+	for (i = 0; i < info->nOrderBy; i++) {
+		struct sqlite3_index_orderby *ao;
+		GdaSqlBuilderId fid;
+		ao = &(info->aOrderBy [i]);
+		if (ao->iColumn >= lspec->ncols) {
+			g_warning ("Internal error: column known by SQLite's virtual table %d is not known for "
+				   "table '%s', which has %d column(s)", ao->iColumn,
+				   g_value_get_string (lspec->table_name), lspec->ncols);
+			info->orderByConsumed = FALSE;
+			continue;
+		}
+		fid = gda_sql_builder_add_id (b, lspec->col_names[ao->iColumn]);
+		gda_sql_builder_select_order_by (b, fid, ao->desc ? FALSE : TRUE, NULL);
+	}
+
+	GdaStatement *stmt;
+	stmt = gda_sql_builder_get_statement (b, NULL);
+	g_object_unref (b);
+	if (stmt) {
+		ComputedFilter *filter;
+
+		filter = g_new0 (ComputedFilter, 1);
+		filter->stmt = stmt;
+		filter->orderByConsumed = info->orderByConsumed;
+		filter->out_const = g_new (struct sqlite3_index_constraint_usage,  info->nConstraint);
+		memcpy (filter->out_const,
+			info->aConstraintUsage,
+			sizeof (struct sqlite3_index_constraint_usage) * info->nConstraint);
+
+		gchar *sql;
+		sql = gda_statement_to_sql (stmt, NULL, NULL);
+		/*g_print ("Filter %p for [%s] hash=[%s]\n", filter, sql, hash);*/
+		g_free (sql);
+
+		if (! lspec->filters_hash)
+			lspec->filters_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+								     g_free,
+								     (GDestroyNotify) computed_filter_free);
+		
+		g_hash_table_insert (lspec->filters_hash, hash, filter);
+		info->idxStr = (char*) filter->stmt;
+		/*g_print ("There are now %d statements in store...\n", g_hash_table_size (lspec->filters_hash));*/
+	}
+	else {
+		for (i = 0, argpos = 0; i < info->nConstraint; i++) {
+			info->aConstraintUsage[i].argvIndex = 0;
+			info->aConstraintUsage[i].omit = 0;
+		}
+		info->idxStr = NULL;
+		info->orderByConsumed = 0;
+		g_free (hash);
+	}
+}
+
+
+/*
+ * Takes as input #GValue created when calling spec->create_filtered_model_func()
+ * and creates a GValue with the correct requested type
+ */
+static GValue *
+create_value_from_sqlite3_gvalue (GType type, GValue *svalue, GError **error)
+{
+	GValue *value;
+	gboolean allok = TRUE;
+	value = g_new0 (GValue, 1);
+
+	if (type != GDA_TYPE_NULL)
+		g_value_init (value, type);
+
+	if (type == GDA_TYPE_NULL)
+		;
+	else if (type == G_TYPE_INT) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_INT64)
+			allok = FALSE;
+		else {
+			gint64 i;
+			i = g_value_get_int64 (svalue);
+			if ((i > G_MAXINT) || (i < G_MININT)) {
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_DATA_ERROR,
+					     "%s", _("Integer value is out of bounds"));
+				allok = FALSE;
+			}
+			else
+				g_value_set_int (value, (gint) i);
+		}
+	}
+	else if (type == G_TYPE_UINT) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_INT64)
+			allok = FALSE;
+		else {
+			gint64 i;
+			i = g_value_get_int64 (svalue);
+			if ((i < 0) || (i > G_MAXUINT)) {
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_DATA_ERROR,
+					     "%s", _("Integer value is out of bounds"));
+				allok = FALSE;
+			}
+			else
+				g_value_set_uint (value, (guint) i);
+		}
+	}
+	else if (type == G_TYPE_INT64) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_INT64)
+			allok = FALSE;
+		else
+			g_value_set_int64 (value, g_value_get_int64 (svalue));
+	}
+	else if (type == G_TYPE_UINT64) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_INT64)
+			allok = FALSE;
+		else
+			g_value_set_uint64 (value, (guint64) g_value_get_int64 (svalue));
+	}
+	else if (type == G_TYPE_DOUBLE) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_DOUBLE)
+			allok = FALSE;
+		else
+			g_value_set_double (value, g_value_get_double (svalue));
+	}
+	else if (type == G_TYPE_STRING) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_STRING)
+			allok = FALSE;
+		else
+			g_value_set_string (value, g_value_get_string (svalue));
+	}
+	else if (type == GDA_TYPE_BINARY) {
+		if (G_VALUE_TYPE (svalue) != GDA_TYPE_BINARY)
+			allok = FALSE;
+		else
+			gda_value_set_binary (value, gda_value_get_binary (svalue));
+	}
+	else if (type == GDA_TYPE_BLOB) {
+		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+			     GDA_SERVER_PROVIDER_DATA_ERROR,
+			     _("Blob constraints are not handled in virtual table condition"));
+		allok = FALSE;
+	}
+	else if (type == G_TYPE_BOOLEAN) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_INT64)
+			allok = FALSE;
+		else
+			g_value_set_boolean (value, g_value_get_int64 (svalue) == 0 ? FALSE : TRUE);
+	}
+	else if (type == G_TYPE_DATE) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_STRING)
+			allok = FALSE;
+		else {
+			GDate date;
+			if (!gda_parse_iso8601_date (&date, g_value_get_string (svalue))) {
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_DATA_ERROR,
+					     _("Invalid date '%s' (date format should be YYYY-MM-DD)"),
+					     g_value_get_string (svalue));
+				allok = FALSE;
+			}
+			else
+				g_value_set_boxed (value, &date);
+		}
+	}
+	else if (type == GDA_TYPE_TIME) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_STRING)
+			allok = FALSE;
+		else {
+			GdaTime timegda;
+			if (!gda_parse_iso8601_time (&timegda, g_value_get_string (svalue))) {
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_DATA_ERROR,
+					     _("Invalid time '%s' (time format should be HH:MM:SS[.ms])"),
+					     g_value_get_string (svalue));
+				allok = FALSE;
+			}
+			else
+				gda_value_set_time (value, &timegda);
+		}
+	}
+	else if (type == GDA_TYPE_TIMESTAMP) {
+		if (G_VALUE_TYPE (svalue) != G_TYPE_STRING)
+			allok = FALSE;
+		else {
+			GdaTimestamp timestamp;
+			if (!gda_parse_iso8601_timestamp (&timestamp, g_value_get_string (svalue))) {
+				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
+					     GDA_SERVER_PROVIDER_DATA_ERROR,
+					     _("Invalid timestamp '%s' (format should be YYYY-MM-DD HH:MM:SS[.ms])"),
+					     g_value_get_string (svalue));
+				allok = FALSE;
+			}
+			else
+				gda_value_set_timestamp (value, &timestamp);
+		}
+	}
+	else
+		g_error ("Unhandled GDA type %s in SQLite recordset",
+			 gda_g_type_to_string (type));
+
+	if (! allok) {
+		g_free (value);
+		return NULL;
+	}
+	else
+		return value;
+}
+
+static GdaDataModel *
+dict_table_create_model_func (GdaVconnectionDataModelSpec *spec, int idxNum, const char *idxStr,
+			      int argc, GValue **argv)
+{
+	GdaDataModel *model;
+	GdaStatement *stmt = NULL;
+	GdaSet *params = NULL;
+	LocalSpec *lspec = (LocalSpec *) spec;
+
+	if (idxStr) {
+		gint i;
+		GSList *list;
+		stmt = GDA_STATEMENT (idxStr);
+		if (! gda_statement_get_parameters (stmt, &params, NULL))
+			return NULL;
+		if (argc > 0) {
+			g_assert (params && (argc == g_slist_length (params->holders)));
+			for (i = 0, list = params->holders; i < argc; i++, list = list->next) {
+				GdaHolder *holder = GDA_HOLDER (list->data);
+				GValue *value;
+				value = create_value_from_sqlite3_gvalue (gda_holder_get_g_type (holder),
+									  argv [i], NULL);
+				if (value)
+					g_assert (gda_holder_take_value (holder, value, NULL));
+				else {
+					g_object_ref (params);
+					return NULL;
+				}
+			}
+		}
+		g_object_ref (stmt);
+	}
+	else {
+		GdaSqlBuilder *b;
+		b = gda_sql_builder_new (GDA_SQL_STATEMENT_SELECT);
+		if (lspec->ncols > 0) {
+			gint i;
+			for (i = 0; i < lspec->ncols; i++)
+				gda_sql_builder_select_add_field (b, lspec->col_names[i], NULL, NULL);
+		}
+		else
+			gda_sql_builder_add_field_value_id (b,
+							    gda_sql_builder_add_id (b, "*"), 0);
+		gda_sql_builder_select_add_target_id (b,
+						      gda_sql_builder_add_id (b, g_value_get_string (lspec->table_name)),
+						      NULL);
+		stmt = gda_sql_builder_get_statement (b, NULL);
+		g_object_unref (b);
+		g_assert (stmt);
+	}
+	GError *lerror = NULL;
+	model = gda_connection_statement_execute_select_full (lspec->hc->cnc, stmt, params,
+							      GDA_STATEMENT_MODEL_CURSOR_FORWARD, NULL,
+							      &lerror);
 	g_object_unref (stmt);
+	if (params)
+		g_object_unref (params);
 	if (model) 
 		gda_data_select_compute_modification_statements (GDA_DATA_SELECT (model), NULL);
+	else {
+		gda_log_message ("Virtual table: data model error: %s",
+			       lerror && lerror->message ? lerror->message : "no detail");
+		g_clear_error (&lerror);
+	}
 
 	return model;
 }
@@ -547,7 +976,9 @@ table_add (HubConnection *hc, const GValue *table_name, GError **error)
 	lspec = g_new0 (LocalSpec, 1);
 	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->data_model = NULL;
 	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_columns_func = (GdaVconnectionDataModelCreateColumnsFunc) dict_table_create_columns_func;
-	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_model_func = (GdaVconnectionDataModelCreateModelFunc) dict_table_create_model_func;
+	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_model_func = NULL;
+	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_filter_func = (GdaVconnectionDataModelParseFilterFunc) dict_table_create_filter;
+	GDA_VCONNECTION_DATA_MODEL_SPEC (lspec)->create_filtered_model_func = (GdaVconnectionDataModelCreateFModelFunc) dict_table_create_model_func;
 	lspec->table_name = gda_value_copy (table_name);
 	lspec->hc = hc;
 	tmp = get_complete_table_name (hc, lspec->table_name);
diff --git a/libgda/sqlite/virtual/gda-virtual-connection.c b/libgda/sqlite/virtual/gda-virtual-connection.c
index 2545f47..74eaebb 100644
--- a/libgda/sqlite/virtual/gda-virtual-connection.c
+++ b/libgda/sqlite/virtual/gda-virtual-connection.c
@@ -1,6 +1,5 @@
 /* 
- * GDA common library
- * Copyright (C) 2007 - 2008 The GNOME Foundation.
+ * Copyright (C) 2007 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -129,7 +128,8 @@ gda_virtual_connection_get_type (void)
  * @virtual_provider: a #GdaVirtualProvider object
  * @error: a place to store errors, or %NULL
  *
- * Creates and opens a new virtual connection using the @virtual_provider provider
+ * Creates and opens a new virtual connection using the @virtual_provider provider. The returned value
+ * is a new #GdaVirtualConnection which needs to be used to actually add some contents to the virtual connection.
  *
  * Returns: a new #GdaConnection object, or %NULL if an error occurred
  */
@@ -166,6 +166,9 @@ gda_virtual_connection_open (GdaVirtualProvider *virtual_provider, GError **erro
  * a thread wrapped connection, and the actual (wrapped) virtual connection can be obtained through
  * the "gda-virtual-connection" user property (use g_object_get_data() to get it).
  *
+ * The returned value is a new #GdaVirtualConnection which needs to be used to actually add some
+ * contents to the virtual connection.
+ *
  * Returns: a new #GdaConnection object, or %NULL if an error occurred
  */
 GdaConnection *
diff --git a/libgda/sqlite/virtual/gda-virtual-connection.h b/libgda/sqlite/virtual/gda-virtual-connection.h
index 1f7e50c..d0d9976 100644
--- a/libgda/sqlite/virtual/gda-virtual-connection.h
+++ b/libgda/sqlite/virtual/gda-virtual-connection.h
@@ -1,5 +1,5 @@
 /* GDA virtual connection
- * Copyright (C) 2007 - 2008 The GNOME Foundation.
+ * Copyright (C) 2007 - 2010 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -46,6 +46,7 @@ struct _GdaVirtualConnection {
 struct _GdaVirtualConnectionClass {
 	GdaConnectionClass           parent_class;
 
+	/*< private >*/
 	/* Padding for future expansion */
 	void (*_gda_reserved1) (void);
 	void (*_gda_reserved2) (void);
@@ -59,7 +60,7 @@ GdaConnection *gda_virtual_connection_open_extended              (GdaVirtualProv
 								  GdaConnectionOptions options, GError **error);
 void           gda_virtual_connection_internal_set_provider_data (GdaVirtualConnection *vcnc, 
 								  gpointer data, GDestroyNotify destroy_func);
-gpointer       gda_virtual_connection_internal_get_provider_data (GdaVirtualConnection *cnc);
+gpointer       gda_virtual_connection_internal_get_provider_data (GdaVirtualConnection *vcnc);
 
 G_END_DECLS
 
diff --git a/libgda/sqlite/virtual/gda-vprovider-data-model.c b/libgda/sqlite/virtual/gda-vprovider-data-model.c
index 116dfc3..7a0c051 100644
--- a/libgda/sqlite/virtual/gda-vprovider-data-model.c
+++ b/libgda/sqlite/virtual/gda-vprovider-data-model.c
@@ -29,32 +29,20 @@
 #include <sqlite3.h>
 #include <libgda/gda-connection-private.h>
 #include <libgda/gda-data-model-iter.h>
-#include <libgda/gda-data-access-wrapper.h>
 #include <libgda/gda-blob-op.h>
 #include "../gda-sqlite.h"
 #include <sql-parser/gda-statement-struct-util.h>
 
+#define GDA_DEBUG_VIRTUAL
+#undef GDA_DEBUG_VIRTUAL
+
 struct _GdaVproviderDataModelPrivate {
 	int foo;
 };
 
-/* properties */
-enum
-{
-        PROP_0,
-};
-
 static void gda_vprovider_data_model_class_init (GdaVproviderDataModelClass *klass);
 static void gda_vprovider_data_model_init       (GdaVproviderDataModel *prov, GdaVproviderDataModelClass *klass);
 static void gda_vprovider_data_model_finalize   (GObject *object);
-static void gda_vprovider_data_model_set_property (GObject *object,
-					       guint param_id,
-					       const GValue *value,
-					       GParamSpec *pspec);
-static void gda_vprovider_data_model_get_property (GObject *object,
-					       guint param_id,
-					       GValue *value,
-					       GParamSpec *pspec);
 static GObjectClass  *parent_class = NULL;
 
 static GdaConnection *gda_vprovider_data_model_create_connection (GdaServerProvider *provider);
@@ -89,10 +77,6 @@ gda_vprovider_data_model_class_init (GdaVproviderDataModelClass *klass)
         server_class->create_operation = NULL;
         server_class->render_operation = NULL;
         server_class->perform_operation = NULL;
-
-	/* Properties */
-        object_class->set_property = gda_vprovider_data_model_set_property;
-        object_class->get_property = gda_vprovider_data_model_get_property;
 }
 
 static void
@@ -146,43 +130,6 @@ gda_vprovider_data_model_get_type (void)
 	return type;
 }
 
-static void
-gda_vprovider_data_model_set_property (GObject *object,
-				       guint param_id,
-				       G_GNUC_UNUSED const GValue *value,
-				       GParamSpec *pspec)
-{
-        GdaVproviderDataModel *prov;
-
-        prov = GDA_VPROVIDER_DATA_MODEL (object);
-        if (prov->priv) {
-                switch (param_id) {
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-			break;
-                }
-        }
-}
-
-static void
-gda_vprovider_data_model_get_property (GObject *object,
-				       guint param_id,
-				       G_GNUC_UNUSED GValue *value,
-				       GParamSpec *pspec)
-{
-        GdaVproviderDataModel *prov;
-
-        prov = GDA_VPROVIDER_DATA_MODEL (object);
-        if (prov->priv) {
-		switch (param_id) {
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-			break;
-		}
-        }
-}
-
-
 /**
  * gda_vprovider_data_model_new
  *
@@ -218,6 +165,7 @@ static int virtualBegin (sqlite3_vtab *tab);
 static int virtualSync (sqlite3_vtab *tab);
 static int virtualCommit (sqlite3_vtab *tab);
 static int virtualRollback (sqlite3_vtab *tab);
+static int virtualRename (sqlite3_vtab *pVtab, const char *zNew);
 
 static sqlite3_module Module = {
 	0,                         /* iVersion */
@@ -239,7 +187,7 @@ static sqlite3_module Module = {
 	virtualCommit,                /* xCommit - commit transaction */
 	virtualRollback,              /* xRollback - rollback transaction */
 	NULL,                         /* xFindFunction - function overloading */
-	NULL                          /* Rename - Notification that the table will be given a new name */
+	virtualRename                 /* Rename - Notification that the table will be given a new name */
 };
 
 static GdaConnection *
@@ -325,36 +273,34 @@ gda_vprovider_data_model_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
 }
 
 /* module implementation */
-#define TRACE() g_print ("== %s()\n", __FUNCTION__)
-#undef TRACE
-#define TRACE()
+#ifdef GDA_DEBUG_VIRTUAL
+  #define TRACE(table) g_print ("== %s (table=>%p)\n", __FUNCTION__, (table))
+#else
+  #define TRACE(table)
+#endif
 
 typedef struct {
 	sqlite3_vtab                 base;
 	GdaVconnectionDataModel     *cnc;
-	GdaDataModel                *wrapper;
 	GdaVConnectionTableData     *td;
 } VirtualTable;
 
 typedef struct {
-	sqlite3_vtab_cursor      base;
+	sqlite3_vtab_cursor      base; /* base.pVtab is a pointer to the sqlite3_vtab virtual table */
 	GdaDataModelIter        *iter;
 	gint                     ncols;
 } VirtualCursor;
 
-static void virtual_table_manage_real_data_model (VirtualTable *vtable);
-
 static int
 virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr)
 {
 	GdaVconnectionDataModel *cnc = GDA_VCONNECTION_DATA_MODEL (pAux);
-	GdaDataModel *wrapper = NULL;
 	GString *sql;
 	gint i, ncols;
 	gchar *spec_name, *tmp;
 	GdaVConnectionTableData *td;
 
-	TRACE ();
+	TRACE (NULL);
 
 	/* find Spec */
 	g_assert (argc == 4);
@@ -371,21 +317,9 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
 
 	/* preparations */
 	if (td->spec->data_model) {
-		if (gda_data_model_get_access_flags (td->spec->data_model) & GDA_DATA_MODEL_ACCESS_RANDOM) {
-			wrapper = td->spec->data_model;
-			g_object_ref (wrapper);
-		}
-		else {
-			/* no random access => use a wrapper */
-			GdaDataModel *wrapper;
-			wrapper = gda_data_access_wrapper_new (td->spec->data_model);
-			g_assert (wrapper);
-		}
-
-		ncols = gda_data_model_get_n_columns (wrapper);
+		ncols = gda_data_model_get_n_columns (td->spec->data_model);
 		if (ncols <= 0) {
 			*pzErr = SQLITE3_CALL (sqlite3_mprintf) (_("Data model must have at least one column"));
-			g_object_unref (wrapper);
 			return SQLITE_ERROR;
 		}
 		td->real_model = td->spec->data_model;
@@ -425,7 +359,7 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
 		if (td->columns)
 			column = g_list_nth_data (td->columns, i);
 		else
-			column = gda_data_model_describe_column (wrapper, i);
+			column = gda_data_model_describe_column (td->spec->data_model, i);
 		if (!column) {
 			*pzErr = SQLITE3_CALL (sqlite3_mprintf) (_("Can't get data model description for column %d"), i);
 			g_string_free (sql, TRUE);
@@ -484,7 +418,6 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
 	VirtualTable *vtable;
 	vtable = g_new0 (VirtualTable, 1);
 	vtable->cnc = cnc;
-	vtable->wrapper = wrapper;
 	vtable->td = td;
 	*ppVtab = &(vtable->base);
 
@@ -505,7 +438,7 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
 static int
 virtualConnect (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr)
 {
-	TRACE ();
+	TRACE (NULL);
 
 	return virtualCreate (db, pAux, argc, argv, ppVtab, pzErr);
 }
@@ -515,10 +448,8 @@ virtualDisconnect (sqlite3_vtab *pVtab)
 {
 	VirtualTable *vtable = (VirtualTable *) pVtab;
 
-	TRACE ();
+	TRACE (pVtab);
 
-	if (vtable->wrapper)
-		g_object_unref (vtable->wrapper);
 	g_free (vtable);
 	return SQLITE_OK;
 }
@@ -526,66 +457,22 @@ virtualDisconnect (sqlite3_vtab *pVtab)
 static int
 virtualDestroy (sqlite3_vtab *pVtab)
 {
-	TRACE ();
+	TRACE (pVtab);
 
 	return virtualDisconnect (pVtab);
 }
 
-static void
-virtual_table_manage_real_data_model (VirtualTable *vtable)
-{
-	if (vtable->td->spec->create_model_func) {
-		if (vtable->td->real_model)
-			g_object_unref (vtable->td->real_model);
-		if (vtable->wrapper)
-			g_object_unref (vtable->wrapper);
-
-		vtable->td->real_model = vtable->td->spec->create_model_func (vtable->td->spec);
-		if (! vtable->td->columns && vtable->td->spec->create_columns_func)
-			vtable->td->columns = vtable->td->spec->create_columns_func (vtable->td->spec, NULL);
-		if (vtable->td->columns) {
-			/* columns */
-			GList *list;
-			guint i, ncols;
-			ncols = gda_data_model_get_n_columns (vtable->td->real_model);
-			g_assert (ncols == g_list_length (vtable->td->columns));
-			for (i = 0, list = vtable->td->columns;
-			     i < ncols;
-			     i++, list = list->next) {
-				GdaColumn *mcol = gda_data_model_describe_column (vtable->td->real_model, i);
-				GdaColumn *ccol = (GdaColumn*) list->data;
-				if (gda_column_get_g_type (mcol) == GDA_TYPE_NULL)
-					gda_column_set_g_type (mcol, gda_column_get_g_type (ccol));
-			}
-		}
-
-		/*g_print ("Created real model %p for table %s\n", vtable->td->real_model, vtable->td->table_name);*/
-		
-		if (gda_data_model_get_access_flags (vtable->td->real_model) & GDA_DATA_MODEL_ACCESS_RANDOM) {
-			vtable->wrapper = vtable->td->real_model;
-			g_object_ref (vtable->wrapper);
-		}
-		else {
-			/* no random access => use a wrapper */
-			vtable->wrapper = gda_data_access_wrapper_new (vtable->td->real_model);
-		}
-	}
-}
-
 static int
 virtualOpen (sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
 {
 	VirtualCursor *cursor;
-	VirtualTable *vtable = (VirtualTable*) pVTab;
 
-	TRACE ();
-
-	virtual_table_manage_real_data_model (vtable);
+	TRACE (pVTab);
 
+	/* create empty cursor */
 	cursor = g_new0 (VirtualCursor, 1);
-	cursor->iter = gda_data_model_create_iter (vtable->wrapper);
-	cursor->ncols = gda_data_model_get_n_columns (GDA_DATA_MODEL (vtable->td->real_model));
 	*ppCursor = (sqlite3_vtab_cursor*) cursor;
+
 	return SQLITE_OK;
 }
 
@@ -594,9 +481,10 @@ virtualClose (sqlite3_vtab_cursor *cur)
 {
 	VirtualCursor *cursor = (VirtualCursor*) cur;
 
-	TRACE ();
+	TRACE (cur->pVtab);
 
-	g_object_unref (cursor->iter);
+	if (cursor->iter)
+		g_object_unref (cursor->iter);
 	/* FIXME: destroy table->spec->model and table->wrapper */
 	g_free (cur);
 	return SQLITE_OK;
@@ -607,7 +495,7 @@ virtualEof (sqlite3_vtab_cursor *cur)
 {
 	VirtualCursor *cursor = (VirtualCursor*) cur;
 
-	TRACE ();
+	TRACE (cur->pVtab);
 
 	if (gda_data_model_iter_is_valid (cursor->iter))
 		return FALSE;
@@ -621,7 +509,7 @@ virtualNext (sqlite3_vtab_cursor *cur)
 	VirtualCursor *cursor = (VirtualCursor*) cur;
 	/*VirtualTable *vtable = (VirtualTable*) cur->pVtab;*/
 
-	TRACE ();
+	TRACE (cur->pVtab);
 
 	if (!gda_data_model_iter_move_next (cursor->iter)) {
 		if (gda_data_model_iter_is_valid (cursor->iter))
@@ -637,7 +525,7 @@ virtualColumn (sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i)
 {
 	VirtualCursor *cursor = (VirtualCursor*) cur;
 
-	TRACE ();
+	TRACE (cur->pVtab);
 
 	GdaHolder *param;
 	
@@ -693,36 +581,291 @@ virtualRowid (sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid)
 {
 	VirtualCursor *cursor = (VirtualCursor*) cur;
 
-	TRACE ();
+	TRACE (cur->pVtab);
 
 	*pRowid = gda_data_model_iter_get_row (cursor->iter);
 	return SQLITE_OK;
 }
 
+/* NEVER returns %NULL */
+static GValue *
+create_value_from_sqlite3_value_notype (sqlite3_value *svalue)
+{
+	GValue *value;
+	value = g_new0 (GValue, 1);
+
+	switch (SQLITE3_CALL (sqlite3_value_type) (svalue)) {
+	case SQLITE_INTEGER:
+		g_value_init (value, G_TYPE_INT64);
+		g_value_set_int64 (value, SQLITE3_CALL (sqlite3_value_int64) (svalue));
+		break;
+	case SQLITE_FLOAT:
+		g_value_init (value, G_TYPE_DOUBLE);
+		g_value_set_double (value, SQLITE3_CALL (sqlite3_value_double) (svalue));
+		break;
+	case SQLITE_BLOB: {
+		GdaBinary *bin;
+		g_value_init (value, GDA_TYPE_BINARY);
+		bin = g_new0 (GdaBinary, 1);
+		bin->binary_length = SQLITE3_CALL (sqlite3_value_bytes) (svalue);
+		if (bin->binary_length > 0) {
+			bin->data = g_new (guchar, bin->binary_length);
+			memcpy (bin->data, SQLITE3_CALL (sqlite3_value_blob) (svalue),
+				bin->binary_length);
+		}
+		else
+			bin->binary_length = 0;
+		gda_value_take_binary (value, bin);
+		break;
+	}
+	case SQLITE_NULL:
+		break;
+	case SQLITE3_TEXT:
+	default:
+		g_value_init (value, G_TYPE_STRING);
+		g_value_set_string (value, (gchar *) SQLITE3_CALL (sqlite3_value_text) (svalue));
+		break;
+	}
+	return value;
+}
+
+/*
+ * Returns: a new array of @argc values, and %NULL if @argc = 0
+ */
+static GValue **
+create_gvalues_array_from_sqlite3_array (int argc, sqlite3_value **argv)
+{
+	GValue **array;
+	gint i;
+	if (argc == 0)
+		return NULL;
+	array = g_new (GValue *, argc);
+	for (i = 0; i < argc; i++)
+		array[i] = create_value_from_sqlite3_value_notype (argv[i]);
+	return array;
+}
+
+static void
+virtual_table_manage_real_data_model (VirtualTable *vtable, int idxNum, const char *idxStr,
+				      int argc, sqlite3_value **argv)
+{
+	if (!vtable->td->spec->create_filtered_model_func && !vtable->td->spec->create_model_func)
+		return;
+
+	if (vtable->td->real_model) {
+		g_object_unref (vtable->td->real_model);
+		vtable->td->real_model = NULL;
+	}
+
+	/* actual data model creation */
+	if (vtable->td->spec->create_filtered_model_func) {
+		GValue **gargv;
+		gargv = create_gvalues_array_from_sqlite3_array (argc, argv);
+		vtable->td->real_model = vtable->td->spec->create_filtered_model_func (vtable->td->spec,
+										       idxNum, idxStr,
+										       argc, gargv);
+		if (gargv) {
+			gint i;
+			for (i = 0; i < argc; i++)
+				gda_value_free (gargv[i]);
+			g_free (gargv);
+		}
+	}
+	else if (vtable->td->spec->create_model_func)
+		vtable->td->real_model = vtable->td->spec->create_model_func (vtable->td->spec);
+	if (! vtable->td->real_model)
+		return;
+	
+	/* columns if not yet created */
+	if (! vtable->td->columns && vtable->td->spec->create_columns_func)
+		vtable->td->columns = vtable->td->spec->create_columns_func (vtable->td->spec, NULL);
+
+	if (vtable->td->columns) {
+		/* columns */
+		GList *list;
+		guint i, ncols;
+		ncols = gda_data_model_get_n_columns (vtable->td->real_model);
+		g_assert (ncols == g_list_length (vtable->td->columns));
+		for (i = 0, list = vtable->td->columns;
+		     i < ncols;
+		     i++, list = list->next) {
+			GdaColumn *mcol = gda_data_model_describe_column (vtable->td->real_model, i);
+			GdaColumn *ccol = (GdaColumn*) list->data;
+			if (gda_column_get_g_type (mcol) == GDA_TYPE_NULL)
+				gda_column_set_g_type (mcol, gda_column_get_g_type (ccol));
+		}
+	}
+
+	/*g_print ("Created real model %p for table %s\n", vtable->td->real_model, vtable->td->table_name);*/
+}
+
 static int
-virtualFilter (sqlite3_vtab_cursor *pVtabCursor, int idxNum, G_GNUC_UNUSED const char *idxStr, G_GNUC_UNUSED int argc, G_GNUC_UNUSED sqlite3_value **argv)
+virtualFilter (sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv)
 {
 	VirtualCursor *cursor = (VirtualCursor*) pVtabCursor;
+	VirtualTable *vtable = (VirtualTable*) pVtabCursor->pVtab;
 
-	TRACE ();
+	TRACE (pVtabCursor->pVtab);
 
-	switch (idxNum) {
-	case 0: /* no filtering at all */
-		gda_data_model_iter_move_next (cursor->iter);
-		break; 
-	default:
-		TO_IMPLEMENT;
-		break;
+	virtual_table_manage_real_data_model (vtable, idxNum, idxStr, argc, argv);
+	if (! vtable->td->real_model)
+		return SQLITE_ERROR;
+
+	/* initialize cursor */
+	if (GDA_IS_DATA_PROXY (vtable->td->real_model)) {
+		cursor->iter = g_object_new (GDA_TYPE_DATA_MODEL_ITER,
+					     "data-model", vtable->td->real_model, NULL);
+		cursor->ncols = gda_data_model_get_n_columns (GDA_DATA_MODEL (vtable->td->real_model));
+	}
+	else {
+		cursor->iter = gda_data_model_create_iter (vtable->td->real_model);
+		cursor->ncols = gda_data_model_get_n_columns (GDA_DATA_MODEL (vtable->td->real_model));
 	}
+
+	gda_data_model_iter_move_next (cursor->iter);
 	return SQLITE_OK;
 }
 
+#ifdef GDA_DEBUG_VIRTUAL
+
+static void
+index_info_dump (sqlite3_index_info *pIdxInfo, gboolean dump_out)
+{
+	int nc;
+	if (dump_out) {
+		g_print ("Dump of OUT sqlite3_index_info [%p]\n", pIdxInfo);
+		for (nc = 0; nc < pIdxInfo->nConstraint; nc++) {
+			struct sqlite3_index_constraint_usage *cons;
+			cons = &(pIdxInfo->aConstraintUsage[nc]);
+			g_print ("sqlite3_index_constraint_usage[%d]\n", nc);
+			g_print ("   argvIndex=%d\n", cons->argvIndex);
+			g_print ("   omit=%d\n", cons->omit);
+		}
+		g_print ("idxNum=%d\n", pIdxInfo->idxNum);
+		g_print ("orderByConsumed=%d\n", pIdxInfo->orderByConsumed);
+	}
+	else {
+		g_print ("Dump of IN sqlite3_index_info [%p]\n", pIdxInfo);
+		for (nc = 0; nc < pIdxInfo->nConstraint; nc++) {
+			struct sqlite3_index_constraint *cons;
+			cons = &(pIdxInfo->aConstraint[nc]);
+			g_print ("sqlite3_index_constraint[%d]\n", nc);
+			g_print ("   iColumn=%d\n", cons->iColumn);
+			g_print ("   op=%d\n", cons->op);
+			g_print ("   usable=%d\n", cons->usable);
+			g_print ("   iTermOffset=%d\n", cons->iTermOffset);
+		}
+		
+		for (nc = 0; nc < pIdxInfo->nOrderBy; nc++) {
+			struct sqlite3_index_orderby *cons;
+			cons = &(pIdxInfo->aOrderBy[nc]);
+			g_print ("sqlite3_index_orderby[%d]\n", nc);
+			g_print ("   iColumn=%d\n", cons->iColumn);
+			g_print ("   desc=%d\n", cons->desc);
+		}
+	}
+}
+#endif
+
+static void
+map_sqlite3_info_to_gda_filter (sqlite3_index_info *info, GdaVconnectionDataModelFilter *filter)
+{
+	gint i;
+	memset (filter, 0, sizeof (GdaVconnectionDataModelFilter));
+	filter->nConstraint = info->nConstraint;
+	if (info->nConstraint > 0) {
+		filter->aConstraint = g_new (struct GdaVirtualConstraint, filter->nConstraint);
+		filter->aConstraintUsage = g_new (struct GdaVirtualConstraintUsage, filter->nConstraint);
+		for (i = 0; i < info->nConstraint; i++) {
+			filter->aConstraint[i].iColumn = info->aConstraint[i].iColumn;
+			switch (info->aConstraint[i].op) {
+			case SQLITE_INDEX_CONSTRAINT_EQ:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_EQ;
+				break;
+			case SQLITE_INDEX_CONSTRAINT_GT:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_GT;
+				break;
+			case SQLITE_INDEX_CONSTRAINT_LE:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_LEQ;
+				break;
+			case SQLITE_INDEX_CONSTRAINT_LT:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_LT;
+				break;
+			case SQLITE_INDEX_CONSTRAINT_GE:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_GEQ;
+				break;
+			case SQLITE_INDEX_CONSTRAINT_MATCH:
+				filter->aConstraint[i].op = GDA_SQL_OPERATOR_TYPE_REGEXP;
+				break;
+			default:
+				g_assert_not_reached ();
+			}
+
+			filter->aConstraintUsage[i].argvIndex = 0;
+			filter->aConstraintUsage[i].omit = FALSE;
+		}
+	}
+	filter->nOrderBy = info->nOrderBy;
+	if (filter->nOrderBy > 0) {
+		filter->aOrderBy = g_new (struct GdaVirtualOrderby, filter->nOrderBy);
+		for (i = 0; i < info->nOrderBy; i++) {
+			filter->aOrderBy[i].iColumn = info->aOrderBy[i].iColumn;
+			filter->aOrderBy[i].desc = info->aOrderBy[i].desc ? TRUE : FALSE;
+		}
+	}
+	filter->idxNum = 0;
+	filter->idxPointer = NULL;
+	filter->orderByConsumed = FALSE;
+	filter->estimatedCost = info->estimatedCost;
+}
+
+/*
+ * Also frees @filter's dynamically allocated parts
+ */
+static void
+map_consume_gda_filter_to_sqlite3_info (GdaVconnectionDataModelFilter *filter, sqlite3_index_info *info)
+{
+	gint i;
+	g_assert (filter->nConstraint == info->nConstraint);
+	if (info->nConstraint > 0) {
+		for (i = 0; i < info->nConstraint; i++) {
+			info->aConstraintUsage[i].argvIndex = filter->aConstraintUsage[i].argvIndex;
+			info->aConstraintUsage[i].omit = filter->aConstraintUsage[i].omit ? 1 : 0;
+		}
+		g_free (filter->aConstraint);
+		g_free (filter->aConstraintUsage);
+
+	}
+	if (filter->nOrderBy > 0)
+		g_free (filter->aOrderBy);
+	info->idxNum = filter->idxNum;
+	info->idxStr = (char*) filter->idxPointer;
+	info->needToFreeIdxStr = 0;
+	info->orderByConsumed = filter->orderByConsumed ? 1 : 0;
+	info->estimatedCost = filter->estimatedCost;
+}
+
 static int
-virtualBestIndex (G_GNUC_UNUSED sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
+virtualBestIndex (sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
 {
-	TRACE ();
-	
-	pIdxInfo->idxNum = 0;
+	VirtualTable *vtable = (VirtualTable *) tab;
+
+	TRACE (tab);
+#ifdef GDA_DEBUG_VIRTUAL
+	index_info_dump (pIdxInfo, FALSE);
+#endif
+
+	if (vtable->td->spec->create_filter_func) {
+		GdaVconnectionDataModelFilter filter;
+		map_sqlite3_info_to_gda_filter (pIdxInfo, &filter);
+		vtable->td->spec->create_filter_func (vtable->td->spec, &filter);
+		map_consume_gda_filter_to_sqlite3_info (&filter, pIdxInfo);
+#ifdef GDA_DEBUG_VIRTUAL
+		index_info_dump (pIdxInfo, TRUE);
+#endif
+	}
+
+
 	return SQLITE_OK;
 }
 
@@ -743,7 +886,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 	VirtualTable *vtable = (VirtualTable *) tab;
 	const gchar *api_misuse_error = NULL;
 
-	TRACE ();
+	TRACE (tab);
 
 	/* REM: when using the values of apData[], the limit is
 	 * (nData -1 ) and not nData because the last column of the corresponding CREATE TABLE ...
@@ -754,7 +897,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 		/* DELETE */
 		if (SQLITE3_CALL (sqlite3_value_type) (apData[0]) == SQLITE_INTEGER) {
 			gint rowid = SQLITE3_CALL (sqlite3_value_int) (apData [0]);
-			return gda_data_model_remove_row (vtable->wrapper, rowid, NULL) ? SQLITE_OK : SQLITE_READONLY;
+			return gda_data_model_remove_row (vtable->td->real_model, rowid, NULL) ? SQLITE_OK : SQLITE_READONLY;
 		}
 		else {
 			api_misuse_error = "argc==1 and argv[0] is not an integer";
@@ -774,7 +917,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 		for (i = 2; i < (nData - 1); i++) {
 			GType type;
 			GValue *value;
-			type = gda_column_get_g_type (gda_data_model_describe_column (vtable->wrapper, i - 2));
+			type = gda_column_get_g_type (gda_data_model_describe_column (vtable->td->real_model, i - 2));
 			if ((type != GDA_TYPE_NULL) && SQLITE3_CALL (sqlite3_value_text) (apData [i]))
 				value = gda_value_new_from_string ((const gchar*) SQLITE3_CALL (sqlite3_value_text) (apData [i]), type);
 			else
@@ -785,7 +928,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 		}
 		values = g_list_reverse (values);
 
-		newrow = gda_data_model_append_values (vtable->wrapper, values, NULL);
+		newrow = gda_data_model_append_values (vtable->td->real_model, values, NULL);
 		g_list_foreach (values, (GFunc) gda_value_free, NULL);
 		g_list_free (values);
 		if (newrow < 0)
@@ -809,9 +952,9 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 			GError *error = NULL;
 
 			/*g_print ("%d => %s\n", i, SQLITE3_CALL (sqlite3_value_text) (apData [i]));*/
-			type = gda_column_get_g_type (gda_data_model_describe_column (vtable->wrapper, i - 2));
+			type = gda_column_get_g_type (gda_data_model_describe_column (vtable->td->real_model, i - 2));
 			value = gda_value_new_from_string ((const gchar*) SQLITE3_CALL (sqlite3_value_text) (apData [i]), type);
-			res = gda_data_model_set_value_at (vtable->wrapper, i - 2, rowid, value, &error);
+			res = gda_data_model_set_value_at (vtable->td->real_model, i - 2, rowid, value, &error);
 			gda_value_free (value);
 			if (!res) {
 				g_print ("Error: %s\n", error && error->message ? error->message : "???");
@@ -836,30 +979,39 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
 static int
 virtualBegin (sqlite3_vtab *tab)
 {
-	TRACE ();
-
-	virtual_table_manage_real_data_model ((VirtualTable *) tab);
-
+	TRACE (tab);
+	/* no documentation currently available, don't do anything */
 	return SQLITE_OK;
 }
 
 static int
 virtualSync (G_GNUC_UNUSED sqlite3_vtab *tab)
 {
-	TRACE ();
+	TRACE (tab);
+	/* no documentation currently available, don't do anything */
 	return SQLITE_OK;
 }
 
 static int
 virtualCommit (G_GNUC_UNUSED sqlite3_vtab *tab)
 {
-	TRACE ();
+	TRACE (tab);
+	/* no documentation currently available, don't do anything */
 	return SQLITE_OK;
 }
 
 static int
 virtualRollback (G_GNUC_UNUSED sqlite3_vtab *tab)
 {	
-	TRACE ();
+	TRACE (tab);
+	/* no documentation currently available, don't do anything */
+	return SQLITE_OK;
+}
+
+static int
+virtualRename(sqlite3_vtab *pVtab, const char *zNew)
+{
+	TRACE (pVtab);
+	/* not yet analysed and implemented */
 	return SQLITE_OK;
 }
diff --git a/testing/.gitignore b/testing/.gitignore
index 0ac7eb0..f32ecf8 100644
--- a/testing/.gitignore
+++ b/testing/.gitignore
@@ -4,4 +4,6 @@ gda-provider-status
 gdaui-test-data-entries
 gdaui-test-widget-entry
 gdaui-test-errors
+virtual-test
+virtual-test-2
 index.html
diff --git a/testing/Makefile.am b/testing/Makefile.am
index 15e73ec..6f0db3b 100644
--- a/testing/Makefile.am
+++ b/testing/Makefile.am
@@ -1,6 +1,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/libgda \
+	-I$(top_srcdir)/libgda/sqlite \
 	-I$(top_builddir) \
 	$(LIBGDA_CFLAGS)
 
@@ -8,7 +9,7 @@ bin_PROGRAMS = gda-test-connection-4.0
 if HAVE_UI
 UI_PROGS=gdaui-test-data-entries gdaui-test-widget-entry gdaui-test-errors
 endif
-noinst_PROGRAMS = gda-test-blob gda-provider-status $(UI_PROGS)
+noinst_PROGRAMS = gda-test-blob gda-provider-status virtual-test virtual-test-2 $(UI_PROGS)
 
 gda_test_connection_4_0_SOURCES = \
         gda-test-connection.c
@@ -35,6 +36,20 @@ gda_provider_status_LDADD = \
         $(top_builddir)/libgda/libgda-4.0.la \
         $(LIBGDA_LIBS)
 
+virtual_test_SOURCES = \
+	virtual-test.c
+
+virtual_test_LDADD = \
+        $(top_builddir)/libgda/libgda-4.0.la \
+        $(LIBGDA_LIBS)
+
+virtual_test_2_SOURCES = \
+	virtual-test-2.c
+
+virtual_test_2_LDADD = \
+        $(top_builddir)/libgda/libgda-4.0.la \
+        $(LIBGDA_LIBS)
+
 gdaui_test_data_entries_CFLAGS = $(GTK_CFLAGS)
 gdaui_test_data_entries_SOURCES = \
         gdaui-test-data-entries.c
diff --git a/libgda/sqlite/virtual/names.csv b/testing/names.csv
similarity index 100%
rename from libgda/sqlite/virtual/names.csv
rename to testing/names.csv
diff --git a/libgda/sqlite/virtual/test_model.xml b/testing/test_model.xml
similarity index 100%
rename from libgda/sqlite/virtual/test_model.xml
rename to testing/test_model.xml
diff --git a/testing/virtual-test-2.c b/testing/virtual-test-2.c
new file mode 100644
index 0000000..c24513f
--- /dev/null
+++ b/testing/virtual-test-2.c
@@ -0,0 +1,113 @@
+#include <libgda/libgda.h>
+#include <virtual/libgda-virtual.h>
+#include <sql-parser/gda-sql-parser.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* utility functions */
+static void          run_sql_non_select (GdaConnection *cnc, const gchar *sql);
+static GdaDataModel *run_sql_select (GdaConnection *cnc, const gchar *sql);
+static gint          run_and_show_sql_select (GdaConnection *cnc, const gchar *sql, const gchar *title);
+int
+main (int argc, char *argv [])
+{
+	GError *error = NULL;
+	GdaConnection *hub, *cnc;
+        GdaVirtualProvider *provider;
+
+	gda_init ();
+
+	/* open connection */
+	cnc = gda_connection_open_from_dsn ("SalesTest", NULL,
+					    GDA_CONNECTION_OPTIONS_READ_ONLY, &error);
+	if (!cnc) {
+		g_print ("Could not open connection to the SalesTest DSN: %s\n", 
+			 error && error->message ? error->message : "No detail");
+		exit (1);
+	}
+
+	/* Set up Connection hub */
+        provider = gda_vprovider_hub_new ();
+        hub = gda_virtual_connection_open (provider, NULL);
+        g_assert (hub);
+
+	if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (hub), cnc, "sales", &error)) {
+		g_print ("Could not add SalesTest connection to hub: %s\n", 
+			 error && error->message ? error->message : "No detail");
+		exit (1);
+	}
+	g_object_unref (cnc);
+ 
+	/* some tests */	
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id=3", NULL);
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where name='Lew Bonito'", NULL);
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id >= 4 order by name DESC", NULL);
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id >= 5 order by name DESC", NULL);
+	/*
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id=3 OR id=4", NULL);
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id=3 AND id=4", NULL);
+	run_and_show_sql_select (hub, "SELECT * FROM sales.customers where id=3 AND id=4", NULL);
+	*/
+
+	g_object_unref (hub);
+
+	return 0;
+}
+
+static gint
+run_and_show_sql_select (GdaConnection *cnc, const gchar *sql, const gchar *title)
+{
+	GdaDataModel *model;
+	gint nrows;
+
+	model = run_sql_select (cnc, sql);
+	g_print ("\n====== %s ======\n", title ? title : sql);
+	nrows = gda_data_model_get_n_rows (model);
+	if (nrows != 0)
+		gda_data_model_dump (model, stdout);
+	else
+		g_print ("None\n");
+	g_object_unref (model);
+	g_print ("\n");
+	return nrows;
+}
+
+static GdaDataModel *
+run_sql_select (GdaConnection *cnc, const gchar *sql)
+{
+	GdaStatement *stmt;
+	GError *error = NULL;
+	GdaDataModel *res;
+	GdaSqlParser *parser;
+
+	parser = gda_connection_create_parser (cnc);
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+	g_object_unref (parser);
+
+        res = gda_connection_statement_execute_select (cnc, stmt, NULL, &error);
+        g_object_unref (stmt);
+	if (!res) 
+		g_error ("Could not execute query: %s\n", 
+			 error && error->message ? error->message : "no detail");
+	return res;
+}
+
+static void
+run_sql_non_select (GdaConnection *cnc, const gchar *sql)
+{
+	GdaStatement *stmt;
+        GError *error = NULL;
+        gint nrows;
+	GdaSqlParser *parser;
+
+	parser = gda_connection_create_parser (cnc);
+	stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+	g_object_unref (parser);
+
+	nrows = gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, &error);
+	g_object_unref (stmt);
+        if (nrows == -1) 
+                g_error ("NON SELECT error: %s\n", error && error->message ? error->message : "no detail");
+}
diff --git a/libgda/sqlite/virtual/virtual-test.c b/testing/virtual-test.c
similarity index 90%
rename from libgda/sqlite/virtual/virtual-test.c
rename to testing/virtual-test.c
index ff480ff..535dba1 100644
--- a/libgda/sqlite/virtual/virtual-test.c
+++ b/testing/virtual-test.c
@@ -1,6 +1,6 @@
 #include <libgda/libgda.h>
-#include <gda-vprovider-data-model.h>
-#include <gda-vconnection-data-model.h>
+#include <virtual/gda-vprovider-data-model.h>
+#include <virtual/gda-vconnection-data-model.h>
 #include <sql-parser/gda-sql-parser.h>
 
 static gboolean test_sql_select (GdaConnection *cnc, const gchar *sql);
@@ -29,29 +29,24 @@ main (int argc, char **argv)
 		 gda_connection_is_opened (cnc) ? "Opened" : "Closed");
 
 	/* create RW data model */
+	g_print ("Creating @rw_model from hand made array\n");
 	rw_model = gda_data_model_array_new_with_g_types (2, G_TYPE_INT, G_TYPE_STRING);
 	gda_data_model_set_column_name (rw_model, 0, "id");
 	gda_data_model_set_column_name (rw_model, 1, "a_string");
 	gda_data_model_dump (rw_model, stdout);
 
 	/* load test data model */
+	g_print ("Creating @xml_model from loading \"test_model.xml\"\n");
 	xml_model = gda_data_model_import_new_file ("test_model.xml", TRUE, NULL);
-	if (gda_data_model_get_access_flags (xml_model) & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD) {
+	if (gda_data_model_get_access_flags (xml_model) & GDA_DATA_MODEL_ACCESS_CURSOR_BACKWARD)
 		gda_data_model_dump (xml_model, stdout);
-
-		GdaDataModelIter *iter;
-		iter = gda_data_model_create_iter (xml_model);
-		gda_data_model_iter_move_to_row (iter, -1);
-		for (gda_data_model_iter_move_next (iter); gda_data_model_iter_is_valid (iter); 
-		     gda_data_model_iter_move_next (iter)) {
-			g_print ("has row %d\n", gda_data_model_iter_get_row (iter));
-		}
-		g_object_unref (iter);
-	}
 	else
 		g_print ("Data model cannot go backwards => don't yet print its contents\n");
+
+	g_print ("Creating @proxy from proxying @xml_model\n");
 	proxy = (GdaDataModel *) gda_data_proxy_new (xml_model);
 	g_object_set (G_OBJECT (proxy), "defer-sync", FALSE, NULL);
+	gda_data_model_dump (proxy, NULL);
 	
 	/* load CVS data */
 	/*
@@ -72,18 +67,21 @@ main (int argc, char **argv)
 		g_print ("Data model cannot go backwards => don't yet print its contents\n");
 	*/
 
+	g_print ("Creating virtual table 'rwtable' from @rw_model\n");
 	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (cnc), rw_model, "rwtable", &error)) {
 		g_print ("Add RW model error: %s\n", error && error->message ? error->message : "no detail");
 		g_error_free (error);
 		error = NULL;
 		goto theend;
 	}
+	g_print ("Creating virtual table 'mytable' from @proxy\n");
 	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (cnc), proxy, "mytable", &error)) {
 		g_print ("Add Proxy model error: %s\n", error && error->message ? error->message : "no detail");
 		g_error_free (error);
 		error = NULL;
 		goto theend;
 	}
+	g_print ("Creating virtual table 'copytable' from @xml_model\n");
 	if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (cnc), xml_model, "copytable", &error)) {
 		g_print ("Add XML model error: %s\n", error && error->message ? error->message : "no detail");
 		g_error_free (error);
@@ -208,7 +206,10 @@ test_sql_non_select (GdaConnection *cnc, const gchar *sql)
 		return FALSE;
 	}
 	else {
-		g_print ("Impacted rows: %d\n", nrows);
+		gchar *msg = "";
+		if (nrows == -2)
+			msg = " (unknown number of impacted rows)";
+		g_print ("Impacted rows: %d%s\n", nrows, msg);
 		return TRUE;
 	}
 }



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