[libgda] Optimized virtual tables usage
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda] Optimized virtual tables usage
- Date: Tue, 26 Oct 2010 18:59:46 +0000 (UTC)
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 (×tamp, 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, ×tamp);
+ }
+ }
+ 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, ¶ms, 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]