[libgda] Virtual connection correction & improvements



commit ed6494525c54214e87d11ad3cad426c6f944a7fd
Author: Vivien Malerba <malerba gnome-db org>
Date:   Thu Apr 14 20:42:57 2011 +0200

    Virtual connection correction & improvements
    
    correctly handle the list of virtual tables and emit signals when a
    virtual table is added or removed

 libgda/gda-connection-internal.h                   |   12 ++-
 libgda/gda-connection.c                            |   79 ++++++++++++++-
 libgda/sqlite/virtual/gda-vconnection-data-model.c |  105 ++++++++++++++++++--
 libgda/sqlite/virtual/gda-vconnection-data-model.h |    5 +
 libgda/sqlite/virtual/gda-vconnection-hub.c        |    6 +
 libgda/sqlite/virtual/gda-virtual-connection.c     |    3 +-
 libgda/sqlite/virtual/gda-vprovider-data-model.c   |   75 ++++++++++++++
 7 files changed, 269 insertions(+), 16 deletions(-)
---
diff --git a/libgda/gda-connection-internal.h b/libgda/gda-connection-internal.h
index 40edfeb..36274e0 100644
--- a/libgda/gda-connection-internal.h
+++ b/libgda/gda-connection-internal.h
@@ -1,5 +1,5 @@
-/* GDA library
- * Copyright (C) 2009 The GNOME Foundation.
+/*
+ * Copyright (C) 2009 - 2011 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -73,6 +73,14 @@ typedef struct {
 void               _gda_connection_force_transaction_status (GdaConnection *cnc, GdaConnection *wrapped_cnc);
 GdaServerProvider *_gda_connection_get_internal_thread_provider (void);
 
+/*
+ * Used by virtual connections to keep meta data up to date when a table
+ * is added or removed, without using the update_meta_store_after_statement_exec()
+ * because it may not be called everytime
+ */
+void               _gda_connection_signal_meta_table_update (GdaConnection *cnc, const gchar *table_name);
+gchar             *_gda_connection_compute_table_virtual_name (GdaConnection *cnc, const gchar *table_name);
+
 G_END_DECLS
 
 #endif
diff --git a/libgda/gda-connection.c b/libgda/gda-connection.c
index 0ea62a1..435554f 100644
--- a/libgda/gda-connection.c
+++ b/libgda/gda-connection.c
@@ -48,6 +48,7 @@
 #include <libgda/thread-wrapper/gda-thread-provider.h>
 #include <libgda/gda-repetitive-statement.h>
 #include <gda-statement-priv.h>
+#include <sqlite/virtual/gda-vconnection-data-model.h>
 
 #include <glib/gstdio.h>
 #include <fcntl.h>
@@ -6214,6 +6215,12 @@ get_next_word (gchar *str, gboolean for_ident, gchar **out_next)
 static GdaMetaContext *
 meta_data_context_from_statement (GdaConnection *cnc, GdaStatement *stmt, GdaSet *params)
 {
+	if (GDA_IS_VCONNECTION_DATA_MODEL (cnc))
+		/* meta data is updated when the virtual connection emits the
+		 * "vtable-created" or "vtable-dropped" signals
+		 */
+		return NULL;
+
 	GdaMetaContext *context = NULL;
 	gchar *sql, *current, *next;
 	sql = gda_statement_to_sql (stmt, params, NULL);
@@ -6223,18 +6230,20 @@ meta_data_context_from_statement (GdaConnection *cnc, GdaStatement *stmt, GdaSet
 	current = get_next_word (sql, FALSE, &next);
 	if (current && (!strcmp (current, "CREATE") || !strcmp (current, "DROP") ||
 			!strcmp (current, "ALTER"))) {
+		const gchar *tname = NULL;
 		current = get_next_word (next, FALSE, &next);
-		if (current && (!strcmp (current, "TABLE") || !strcmp (current, "VIEW"))) {
+		if (current && (!strcmp (current, "TABLE") || !strcmp (current, "VIEW")))
+			tname = get_next_word (next, TRUE, &next);
+		if (tname) {
 			gchar *tmp;
-			current = get_next_word (next, TRUE, &next);
-			/*g_print ("CONTEXT: update for table [%s]\n", current);*/
+			/*g_print ("CONTEXT: update for table [%s]\n", tname);*/
 			context = g_new0 (GdaMetaContext, 1);
 			context->table_name = "_tables";
 			context->size = 1;
 			context->column_names = g_new0 (gchar *, 1);
 			context->column_names[0] = "table_name";
 			context->column_values = g_new0 (GValue *, 1);
-			tmp = gda_sql_identifier_quote (current, cnc, cnc->priv->provider_obj,
+			tmp = gda_sql_identifier_quote (tname, cnc, cnc->priv->provider_obj,
 							TRUE,
 							cnc->priv->options & GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE);
 			g_value_take_string ((context->column_values[0] = gda_value_new (G_TYPE_STRING)),
@@ -6325,6 +6334,34 @@ update_meta_store_after_statement_exec (GdaConnection *cnc, GdaStatement *stmt,
 	}
 }
 
+void
+_gda_connection_signal_meta_table_update (GdaConnection *cnc, const gchar *table_name)
+{
+	GdaMetaContext *context;
+	gchar *tmp;
+	/*g_print ("CONTEXT: update for table [%s]\n", tname);*/
+	context = g_new0 (GdaMetaContext, 1);
+	context->table_name = "_tables";
+	context->size = 1;
+	context->column_names = g_new0 (gchar *, 1);
+	context->column_names[0] = "table_name";
+	context->column_values = g_new0 (GValue *, 1);
+	tmp = gda_sql_identifier_quote (table_name, cnc, cnc->priv->provider_obj,
+					TRUE,
+					cnc->priv->options & GDA_CONNECTION_OPTIONS_SQL_IDENTIFIERS_CASE_SENSITIVE);
+	g_value_take_string ((context->column_values[0] = gda_value_new (G_TYPE_STRING)),
+			     tmp);
+
+	GError *lerror = NULL;
+	if (! gda_connection_update_meta_store (cnc, context, &lerror))
+		add_connection_event_from_error (cnc, &lerror);
+	
+	if (cnc->priv->trans_meta_context)
+		g_array_prepend_val (cnc->priv->trans_meta_context, context);
+	else
+		auto_update_meta_context_free (context);
+}
+
 /*
  * Free @context which must have been created by meta_data_context_from_statement()
  */
@@ -6339,3 +6376,37 @@ auto_update_meta_context_free (GdaMetaContext *context)
 	g_free (context->column_values);
 	g_free (context);
 }
+
+
+/*
+ * _gda_connection_get_table_virtual_name
+ * @table_name: a non %NULL string
+ *
+ * Returns: a new string.
+ */
+gchar *
+_gda_connection_compute_table_virtual_name (GdaConnection *cnc, const gchar *table_name)
+{
+	gchar **array;
+	gchar *tmp;
+	GString *string = NULL;
+	gint i;
+
+	g_assert (table_name && *table_name);
+	array = gda_sql_identifier_split (table_name);
+	for (i = 0; ; i++) {
+		if (array [i]) {
+			tmp = gda_sql_identifier_quote (array[i], cnc, NULL, TRUE, FALSE);
+			if (string) {
+				g_string_append_c (string, '.');
+				g_string_append (string, tmp);
+			}
+			else
+				string = g_string_new (tmp);
+		}
+		else
+			break;
+	}
+	g_strfreev (array);
+	return g_string_free (string, FALSE);
+}
diff --git a/libgda/sqlite/virtual/gda-vconnection-data-model.c b/libgda/sqlite/virtual/gda-vconnection-data-model.c
index 091ebf0..fe0f285 100644
--- a/libgda/sqlite/virtual/gda-vconnection-data-model.c
+++ b/libgda/sqlite/virtual/gda-vconnection-data-model.c
@@ -26,6 +26,7 @@
 #include "gda-vconnection-data-model-private.h"
 #include "gda-virtual-provider.h"
 #include <libgda/gda-connection-private.h>
+#include <libgda/gda-connection-internal.h>
 #include "../gda-sqlite.h"
 
 struct _GdaVconnectionDataModelPrivate {
@@ -35,8 +36,52 @@ struct _GdaVconnectionDataModelPrivate {
 static void gda_vconnection_data_model_class_init (GdaVconnectionDataModelClass *klass);
 static void gda_vconnection_data_model_init       (GdaVconnectionDataModel *cnc, GdaVconnectionDataModelClass *klass);
 static void gda_vconnection_data_model_dispose   (GObject *object);
+
+enum {
+	VTABLE_CREATED,
+	VTABLE_DROPPED,
+	LAST_SIGNAL
+};
+
+static gint gda_vconnection_data_model_signals[LAST_SIGNAL] = { 0, 0 };
+
 static GObjectClass  *parent_class = NULL;
 
+#ifdef GDA_DEBUG
+static void
+dump_all_tables (GdaVconnectionDataModel *cnc)
+{
+	GSList *list;
+	g_print ("GdaVconnectionDataModel's tables:\n");
+	for (list = cnc->priv->table_data_list; list; list = list->next) {
+		GdaVConnectionTableData *td = (GdaVConnectionTableData *) list->data;
+		g_print ("    table %s, td=%p, spec=%p\n", td->table_name, td, td->spec);
+	}
+}
+#endif
+
+static void
+vtable_created (GdaVconnectionDataModel *cnc, const gchar *table_name)
+{
+	_gda_connection_signal_meta_table_update ((GdaConnection *)cnc, table_name);
+#ifdef GDA_DEBUG
+	dump_all_tables (cnc);
+#endif
+}
+
+static void
+vtable_dropped (GdaVconnectionDataModel *cnc, const gchar *table_name)
+{
+	GdaVConnectionTableData *td;
+	td = gda_vconnection_get_table_data_by_name (cnc, table_name);
+	if (td)
+		cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
+	_gda_connection_signal_meta_table_update ((GdaConnection *)cnc, table_name);
+#ifdef GDA_DEBUG
+	dump_all_tables (cnc);
+#endif
+}
+
 /*
  * GdaVconnectionDataModel class implementation
  */
@@ -47,6 +92,40 @@ gda_vconnection_data_model_class_init (GdaVconnectionDataModelClass *klass)
 
 	parent_class = g_type_class_peek_parent (klass);
 
+	/**
+	 * GdaVconnectionDataModel::vtable-created
+	 * @cnc: the #GdaVconnectionDataModel connection
+	 * @spec: the #GdaVconnectionDataModelSpec for the new virtual table
+	 *
+	 * Signal emitted when a new virtual table has been declared
+	 */
+	gda_vconnection_data_model_signals[VTABLE_CREATED] =
+		g_signal_new ("vtable-created",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdaVconnectionDataModelClass, vtable_created),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1, G_TYPE_STRING);
+	/**
+	 * GdaVconnectionDataModel::vtable-dropped
+	 * @cnc: the #GdaVconnectionDataModel connection
+	 * @spec: the #GdaVconnectionDataModelSpec for the new virtual table
+	 *
+	 * Signal emitted when a new virtual table has been undeclared
+	 */
+	gda_vconnection_data_model_signals[VTABLE_DROPPED] =
+		g_signal_new ("vtable-dropped",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdaVconnectionDataModelClass, vtable_dropped),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1, G_TYPE_STRING);
+
+	klass->vtable_created = vtable_created;
+	klass->vtable_dropped = vtable_dropped;
+
 	object_class->dispose = gda_vconnection_data_model_dispose;
 }
 
@@ -122,7 +201,7 @@ spec_destroy_func (GdaVconnectionDataModelSpec *spec)
 }
 
 static GList *
-create_columns (GdaVconnectionDataModelSpec *spec, GError **error)
+create_columns (GdaVconnectionDataModelSpec *spec, G_GNUC_UNUSED GError **error)
 {
 	g_return_val_if_fail (spec->data_model, NULL);
 
@@ -231,13 +310,13 @@ gda_vconnection_data_model_add (GdaVconnectionDataModel *cnc, GdaVconnectionData
 	td = g_new0 (GdaVConnectionTableData, 1);
 	td->spec = spec;
 	td->spec_free_func = spec_free_func;
-	td->table_name = g_strdup (table_name);
+	td->table_name = _gda_connection_compute_table_virtual_name (GDA_CONNECTION (cnc), table_name);
 	td->unique_name = g_strdup_printf ("Spec%d", counter++);
 	cnc->priv->table_data_list = g_slist_append (cnc->priv->table_data_list, td);
 
 	/* actually create the virtual table in @cnc */
 	prov = (GdaVirtualProvider *) gda_connection_get_provider (GDA_CONNECTION (cnc));
-	str = g_strdup_printf ("CREATE VIRTUAL TABLE %s USING %s ('%s')", table_name, G_OBJECT_TYPE_NAME (prov), td->unique_name);
+	str = g_strdup_printf ("CREATE VIRTUAL TABLE %s USING %s ('%s')", td->table_name, G_OBJECT_TYPE_NAME (prov), td->unique_name);
 	rc = SQLITE3_CALL (sqlite3_exec) (scnc->connection, str, NULL, 0, &zErrMsg);
 	g_free (str);
 	if (rc != SQLITE_OK) {
@@ -247,10 +326,11 @@ gda_vconnection_data_model_add (GdaVconnectionDataModel *cnc, GdaVconnectionData
 		cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
 		retval = FALSE;
 	}
-	/*
-	else 
-		g_print ("Virtual connection: added table %s (model = %p)\n", td->table_name, td->spec->data_model);
-	*/
+	else {
+		g_signal_emit (G_OBJECT (cnc), gda_vconnection_data_model_signals[VTABLE_CREATED], 0,
+			       td->table_name);
+		/*g_print ("Virtual connection: added table %s (spec = %p)\n", td->table_name, td->spec);*/
+	}
 
 	return retval;
 }
@@ -306,6 +386,8 @@ gda_vconnection_data_model_remove (GdaVconnectionDataModel *cnc, const gchar *ta
 	else {
 		/* clean the cnc->priv->table_data_list list */
 		cnc->priv->table_data_list = g_slist_remove (cnc->priv->table_data_list, td);
+		g_signal_emit (G_OBJECT (cnc), gda_vconnection_data_model_signals[VTABLE_DROPPED], 0,
+			       td->table_name);
 		/*g_print ("Virtual connection: removed table %s (%p)\n", td->table_name, td->spec->data_model);*/
 		gda_vconnection_data_model_table_data_free (td);
 		return TRUE;
@@ -432,10 +514,17 @@ GdaVConnectionTableData *
 gda_vconnection_get_table_data_by_name (GdaVconnectionDataModel *cnc, const gchar *table_name)
 {
 	GSList *list;
+	gchar *quoted;
+	if (!table_name || !*table_name)
+		return NULL;
+	quoted = _gda_connection_compute_table_virtual_name (GDA_CONNECTION (cnc), table_name);
 	for (list = cnc->priv->table_data_list; list; list = list->next) {
-		if (!strcmp (((GdaVConnectionTableData*) list->data)->table_name, table_name))
+		if (!strcmp (((GdaVConnectionTableData*) list->data)->table_name, quoted)) {
+			g_free (quoted);
 			return (GdaVConnectionTableData*) list->data;
+		}
 	}
+	g_free (quoted);
 	return NULL;
 }
 
diff --git a/libgda/sqlite/virtual/gda-vconnection-data-model.h b/libgda/sqlite/virtual/gda-vconnection-data-model.h
index a617c39..f69fea1 100644
--- a/libgda/sqlite/virtual/gda-vconnection-data-model.h
+++ b/libgda/sqlite/virtual/gda-vconnection-data-model.h
@@ -184,6 +184,11 @@ struct _GdaVconnectionDataModel {
 struct _GdaVconnectionDataModelClass {
 	GdaVirtualConnectionClass       parent_class;
 
+	void                          (*vtable_created) (GdaVconnectionDataModel *cnc,
+							  const gchar *table_name);
+	void                          (*vtable_dropped) (GdaVconnectionDataModel *cnc,
+							    const gchar *table_name);
+
 	/*< private >*/
 	/* Padding for future expansion */
 	void (*_gda_reserved1) (void);
diff --git a/libgda/sqlite/virtual/gda-vconnection-hub.c b/libgda/sqlite/virtual/gda-vconnection-hub.c
index efda161..06bc17e 100644
--- a/libgda/sqlite/virtual/gda-vconnection-hub.c
+++ b/libgda/sqlite/virtual/gda-vconnection-hub.c
@@ -768,6 +768,12 @@ dict_table_create_model_func (GdaVconnectionDataModelSpec *spec, int idxNum, con
 		g_assert (stmt);
 	}
 	GError *lerror = NULL;
+#ifdef GDA_DEBUG_NO
+	gchar *sql;
+	sql = gda_statement_to_sql (stmt, params, NULL);
+	g_print ("Executed: [%s]\n", sql);
+	g_free (sql);
+#endif
 	model = gda_connection_statement_execute_select_full (lspec->hc->cnc, stmt, params,
 							      GDA_STATEMENT_MODEL_CURSOR_FORWARD, NULL,
 							      &lerror);
diff --git a/libgda/sqlite/virtual/gda-virtual-connection.c b/libgda/sqlite/virtual/gda-virtual-connection.c
index 74eaebb..92c00a6 100644
--- a/libgda/sqlite/virtual/gda-virtual-connection.c
+++ b/libgda/sqlite/virtual/gda-virtual-connection.c
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2007 - 2010 The GNOME Foundation.
+ * Copyright (C) 2007 - 2011 The GNOME Foundation.
  *
  * AUTHORS:
  *      Vivien Malerba <malerba gnome-db org>
@@ -22,7 +22,6 @@
 
 #include <glib/gi18n-lib.h>
 #include <string.h>
-#include <sqlite3.h>
 #include "gda-virtual-connection.h"
 #include <gda-connection-private.h>
 #include <libgda/gda-connection-internal.h>
diff --git a/libgda/sqlite/virtual/gda-vprovider-data-model.c b/libgda/sqlite/virtual/gda-vprovider-data-model.c
index 8b6f423..c5b4e3a 100644
--- a/libgda/sqlite/virtual/gda-vprovider-data-model.c
+++ b/libgda/sqlite/virtual/gda-vprovider-data-model.c
@@ -27,6 +27,7 @@
 #include "gda-vconnection-data-model-private.h"
 #include <sqlite3.h>
 #include <libgda/gda-connection-private.h>
+#include <libgda/gda-connection-internal.h>
 #include <libgda/gda-data-model-iter.h>
 #include <libgda/gda-blob-op.h>
 #include "../gda-sqlite.h"
@@ -51,6 +52,12 @@ static gboolean       gda_vprovider_data_model_open_connection (GdaServerProvide
 								gpointer cb_data);
 static gboolean       gda_vprovider_data_model_close_connection (GdaServerProvider *provider,
 								 GdaConnection *cnc);
+static GObject        *gda_vprovider_data_model_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+								   GdaStatement *stmt, GdaSet *params,
+								   GdaStatementModelUsage model_usage,
+								   GType *col_types, GdaSet **last_inserted_row,
+								   guint *task_id, GdaServerProviderExecCallback async_cb,
+								   gpointer cb_data, GError **error);
 static const gchar   *gda_vprovider_data_model_get_name (GdaServerProvider *provider);
 
 /*
@@ -68,6 +75,7 @@ gda_vprovider_data_model_class_init (GdaVproviderDataModelClass *klass)
 	server_class->create_connection = gda_vprovider_data_model_create_connection;
 	server_class->open_connection = gda_vprovider_data_model_open_connection;
 	server_class->close_connection = gda_vprovider_data_model_close_connection;
+	server_class->statement_execute = gda_vprovider_data_model_statement_execute;
 
 	server_class->get_name = gda_vprovider_data_model_get_name;
 
@@ -313,6 +321,73 @@ gda_vprovider_data_model_close_connection (GdaServerProvider *provider, GdaConne
 	return GDA_SERVER_PROVIDER_CLASS (parent_class)->close_connection (GDA_SERVER_PROVIDER (provider), cnc);
 }
 
+static GObject *
+gda_vprovider_data_model_statement_execute (GdaServerProvider *provider, GdaConnection *cnc,
+					    GdaStatement *stmt, GdaSet *params,
+					    GdaStatementModelUsage model_usage,
+					    GType *col_types, GdaSet **last_inserted_row,
+					    guint *task_id, GdaServerProviderExecCallback async_cb,
+					    gpointer cb_data, GError **error)
+{
+	GObject *retval;
+	if (async_cb) {
+                g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
+                             "%s", _("Provider does not support asynchronous statement execution"));
+                return NULL;
+        }
+        
+	retval = GDA_SERVER_PROVIDER_CLASS (parent_class)->statement_execute (provider, cnc, stmt, params,
+									      model_usage, col_types,
+									      last_inserted_row, task_id,
+									      async_cb, cb_data, error);
+	if (retval) {
+		gchar *sql;
+		sql = gda_statement_to_sql (stmt, params, NULL);
+		if (sql) {
+			gchar *ptr = NULL;
+			/* look for DROP TABLE to signal table removal */
+			if (! g_ascii_strncasecmp (sql, "DROP", 4))
+				ptr = sql + 4;
+			else if (! g_ascii_strncasecmp (sql, "CREATE", 6))
+				ptr = sql + 6;
+			if (ptr) {
+				/* skip spaces */
+				for (; *ptr && (g_ascii_isspace (*ptr) || (*ptr == '\n')); ptr++);
+
+				if (! g_ascii_strncasecmp (ptr, "TABLE", 5)) {
+					/* skip spaces */
+					gchar delim = 0;
+					gchar *table_name, *quoted;
+					for (ptr = ptr+5;
+					     *ptr && (g_ascii_isspace (*ptr) || (*ptr == '\n'));
+					     ptr++);
+					if (*ptr == '\'') {
+						delim = '\'';
+						ptr++;
+					}
+					else if (*ptr == '"') {
+						delim = '"';
+						ptr++;
+					}
+					table_name = ptr;
+					if (delim)
+						for (; *ptr && (*ptr != delim) ; ptr++);
+					else
+						for (; *ptr && g_ascii_isalnum (*ptr); ptr++);
+					*ptr = 0;
+					quoted = _gda_connection_compute_table_virtual_name (GDA_CONNECTION (cnc), table_name);
+					/*g_print ("%s() emits the 'vtable-dropped' signal for table [%s]\n",
+					  __FUNCTION__, quoted);*/
+					g_signal_emit_by_name (cnc, "vtable-dropped", quoted);
+					g_free (quoted);
+				}
+			}
+			g_free (sql);
+		}
+	}
+	return retval;
+}
+
 static const gchar *
 gda_vprovider_data_model_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
 {



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