[tracker] SPARQL: Return generated URIs for inserted blank nodes



commit 7b6fe2b360484db9568f55dbb8102a0c8a3cfe8e
Author: Jürg Billeter <j bitron ch>
Date:   Fri Nov 20 12:02:13 2009 +0100

    SPARQL: Return generated URIs for inserted blank nodes

 data/dbus/tracker-resources.xml               |    8 ++
 src/libtracker-client/tracker.c               |   43 +++++++
 src/libtracker-client/tracker.h               |    7 +
 src/libtracker-data/tracker-data-update.c     |   42 ++++++-
 src/libtracker-data/tracker-data-update.h     |    3 +
 src/libtracker-data/tracker-sparql-query.vala |  168 +++++++++++++++++--------
 src/tracker-store/tracker-resources.c         |   38 ++++++
 src/tracker-store/tracker-resources.h         |    4 +
 src/tracker-store/tracker-store.c             |   30 +++++
 src/tracker-store/tracker-store.h             |    2 +
 src/tracker-utils/tracker-sparql.c            |   26 ++++-
 11 files changed, 319 insertions(+), 52 deletions(-)
---
diff --git a/data/dbus/tracker-resources.xml b/data/dbus/tracker-resources.xml
index 7b28ac5..d9c1a1e 100644
--- a/data/dbus/tracker-resources.xml
+++ b/data/dbus/tracker-resources.xml
@@ -24,6 +24,14 @@
       <arg type="s" name="query" direction="in" />
     </method>
 
+    <!-- SPARQL Update extensions, insert and delete,
+         return generated URIs for inserted blank nodes -->
+    <method name="SparqlUpdateBlank">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="query" direction="in" />
+      <arg type="aaa{ss}" name="result" direction="out" />
+    </method>
+
     <!-- SPARQL Update as part of a batch, use this method when sending a
          possibly large amount of updates to improve performance, may delay
          database commit until receiving BatchCommit -->
diff --git a/src/libtracker-client/tracker.c b/src/libtracker-client/tracker.c
index 3280e5f..051ff2a 100644
--- a/src/libtracker-client/tracker.c
+++ b/src/libtracker-client/tracker.c
@@ -365,6 +365,23 @@ tracker_resources_sparql_update (TrackerClient  *client,
                                                          &*error);
 }
 
+GPtrArray *
+tracker_resources_sparql_update_blank (TrackerClient  *client,
+                                       const gchar    *query,
+                                       GError        **error)
+{
+	GPtrArray *result;
+
+	if (!org_freedesktop_Tracker1_Resources_sparql_update_blank (client->proxy_resources,
+                                                                     query,
+                                                                     &result,
+                                                                     &*error)) {
+		return NULL;
+	}
+
+	return result;
+}
+
 void
 tracker_resources_batch_sparql_update (TrackerClient  *client, 
                                        const gchar    *query, 
@@ -486,6 +503,32 @@ tracker_resources_sparql_update_async (TrackerClient    *client,
 }
 
 guint
+tracker_resources_sparql_update_blank_async (TrackerClient         *client,
+                                             const gchar           *query,
+                                             TrackerReplyGPtrArray  callback,
+                                             gpointer               user_data)
+{
+	CallbackGPtrArray *s;
+        DBusGProxyCall *call;
+        guint id;
+
+	s = g_new0 (CallbackGPtrArray, 1);
+	s->callback = callback;
+	s->data = user_data;
+        s->client = client;
+
+	call = org_freedesktop_Tracker1_Resources_sparql_update_blank_async (client->proxy_resources,
+                                                                             query,
+                                                                             tracker_GPtrArray_reply,
+                                                                             s);
+
+        id = pending_call_new (client, client->proxy_resources, call);
+        s->id = id;
+
+        return id;
+}
+
+guint
 tracker_resources_batch_sparql_update_async (TrackerClient    *client, 
                                              const gchar      *query, 
                                              TrackerReplyVoid  callback, 
diff --git a/src/libtracker-client/tracker.h b/src/libtracker-client/tracker.h
index a69674a..b05445e 100644
--- a/src/libtracker-client/tracker.h
+++ b/src/libtracker-client/tracker.h
@@ -63,6 +63,9 @@ GPtrArray *    tracker_resources_sparql_query              (TrackerClient
 void           tracker_resources_sparql_update             (TrackerClient          *client,
                                                             const gchar            *query,
                                                             GError                **error);
+GPtrArray *    tracker_resources_sparql_update_blank       (TrackerClient          *client,
+                                                            const gchar            *query,
+                                                            GError                **error);
 void           tracker_resources_batch_sparql_update       (TrackerClient          *client,
                                                             const gchar            *query,
                                                             GError                **error);
@@ -84,6 +87,10 @@ guint          tracker_resources_sparql_update_async       (TrackerClient
                                                             const gchar            *query,
                                                             TrackerReplyVoid        callback,
                                                             gpointer                user_data);
+guint          tracker_resources_sparql_update_blank_async (TrackerClient          *client,
+                                                            const gchar            *query,
+                                                            TrackerReplyGPtrArray   callback,
+                                                            gpointer                user_data);
 guint          tracker_resources_batch_sparql_update_async (TrackerClient          *client,
                                                             const gchar            *query,
                                                             TrackerReplyVoid        callback,
diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c
index 77a32b4..01cb9ea 100644
--- a/src/libtracker-data/tracker-data-update.c
+++ b/src/libtracker-data/tracker-data-update.c
@@ -2003,7 +2003,7 @@ tracker_data_update_sparql (const gchar  *update,
 
 	tracker_db_interface_execute_query (iface, NULL, "SAVEPOINT sparql");
 
-	tracker_sparql_query_execute (sparql_query, error);
+	tracker_sparql_query_execute_update (sparql_query, FALSE, error);
 
 	if (*error) {
 		tracker_data_update_buffer_clear ();
@@ -2024,3 +2024,43 @@ tracker_data_update_sparql (const gchar  *update,
 	g_object_unref (sparql_query);
 }
 
+
+GPtrArray *
+tracker_data_update_sparql_blank (const gchar  *update,
+			          GError      **error)
+{
+	TrackerDBInterface *iface;
+	TrackerSparqlQuery *sparql_query;
+	GPtrArray *blank_nodes;
+
+	g_return_val_if_fail (update != NULL, NULL);
+
+	iface = tracker_db_manager_get_db_interface ();
+
+	sparql_query = tracker_sparql_query_new_update (update);
+
+	tracker_db_interface_execute_query (iface, NULL, "SAVEPOINT sparql");
+
+	blank_nodes = tracker_sparql_query_execute_update (sparql_query, TRUE, error);
+
+	if (*error) {
+		tracker_data_update_buffer_clear ();
+		tracker_db_interface_execute_query (iface, NULL, "ROLLBACK TO sparql");
+
+		if (rollback_callbacks) {
+			guint n;
+			for (n = 0; n < rollback_callbacks->len; n++) {
+				TrackerCommitDelegate *delegate;
+				delegate = g_ptr_array_index (rollback_callbacks, n);
+				delegate->callback (delegate->user_data);
+			}
+		}
+	}
+
+	tracker_db_interface_execute_query (iface, NULL, "RELEASE sparql");
+
+	g_object_unref (sparql_query);
+
+	return blank_nodes;
+}
+
diff --git a/src/libtracker-data/tracker-data-update.h b/src/libtracker-data/tracker-data-update.h
index 1298cd9..00839cf 100644
--- a/src/libtracker-data/tracker-data-update.h
+++ b/src/libtracker-data/tracker-data-update.h
@@ -81,6 +81,9 @@ void     tracker_data_begin_transaction             (void);
 void     tracker_data_commit_transaction            (void);
 void     tracker_data_update_sparql                 (const gchar               *update,
 						     GError                   **error);
+GPtrArray *
+         tracker_data_update_sparql_blank           (const gchar               *update,
+						     GError                   **error);
 void     tracker_data_update_buffer_flush           (GError                   **error);
 
 /* Volume handling */
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
index 69bf488..3915577 100644
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -272,6 +272,7 @@ public class Tracker.SparqlQuery : Object {
 	int bnodeid = 0;
 	// base UUID used for blank nodes
 	uchar[] base_uuid;
+	HashTable<string,string> blank_nodes;
 
 	public SparqlQuery (string query) {
 		tokens = new TokenInfo[BUFFER_SIZE];
@@ -289,22 +290,49 @@ public class Tracker.SparqlQuery : Object {
 		this.update_extensions = true;
 	}
 
+	string get_uuid_for_name (uchar[] base_uuid, string name) {
+		var checksum = new Checksum (ChecksumType.SHA1);
+		// base UUID, unique per file
+		checksum.update (base_uuid, 16);
+
+		// node ID
+		checksum.update ((uchar[]) name, -1);
+
+		string sha1 = checksum.get_string ();
+
+		// generate name based uuid
+		return "urn:uuid:%.8s-%.4s-%.4s-%.4s-%.12s".printf (
+			sha1, sha1.offset (8), sha1.offset (12), sha1.offset (16), sha1.offset (20));
+	}
+
 	string generate_bnodeid (string? user_bnodeid) {
 		// user_bnodeid is NULL for anonymous nodes
 		if (user_bnodeid == null) {
 			return ":%d".printf (++bnodeid);
 		} else {
-			var checksum = new Checksum (ChecksumType.SHA1);
-			// base UUID, unique per file
-			checksum.update (base_uuid, 16);
-			// node ID
-			checksum.update ((uchar[]) user_bnodeid, -1);
+			string uri = null;
 
-			string sha1 = checksum.get_string ();
+			if (blank_nodes != null) {
+				uri = blank_nodes.lookup (user_bnodeid);
+				if (uri != null) {
+					return uri;
+				}
+			}
 
-			// generate name based uuid
-			return "urn:uuid:%.8s-%.4s-%.4s-%.4s-%.12s".printf (
-				sha1, sha1.offset (8), sha1.offset (12), sha1.offset (16), sha1.offset (20));
+			uri = get_uuid_for_name (base_uuid, user_bnodeid);
+
+			if (blank_nodes != null) {
+				while (Data.query_resource_id (uri) > 0) {
+					// uri collision, generate new UUID
+					uchar[] new_base_uuid = new uchar[16];
+					uuid_generate (new_base_uuid);
+					uri = get_uuid_for_name (new_base_uuid, user_bnodeid);
+				}
+
+				blank_nodes.insert (user_bnodeid, uri);
+			}
+
+			return uri;
 		}
 	}
 
@@ -418,6 +446,8 @@ public class Tracker.SparqlQuery : Object {
 	}
 
 	public DBResultSet? execute () throws Error {
+		assert (!update_extensions);
+
 		scanner = new SparqlScanner ((char*) query_string, (long) query_string.size ());
 		next ();
 
@@ -430,49 +460,71 @@ public class Tracker.SparqlQuery : Object {
 
 		parse_prologue ();
 
-		if (!update_extensions) {
+		switch (current ()) {
+		case SparqlTokenType.SELECT:
+			return execute_select ();
+		case SparqlTokenType.CONSTRUCT:
+			throw get_internal_error ("CONSTRUCT is not supported");
+		case SparqlTokenType.DESCRIBE:
+			throw get_internal_error ("DESCRIBE is not supported");
+		case SparqlTokenType.ASK:
+			return execute_ask ();
+		case SparqlTokenType.INSERT:
+		case SparqlTokenType.DELETE:
+		case SparqlTokenType.DROP:
+			throw get_error ("INSERT and DELETE are not supported in query mode");
+		default:
+			throw get_error ("expected SELECT or ASK");
+		}
+	}
+
+	public PtrArray? execute_update (bool blank) throws Error {
+		assert (update_extensions);
+
+		scanner = new SparqlScanner ((char*) query_string, (long) query_string.size ());
+		next ();
+
+		// declare fn prefix for XPath functions
+		prefix_map.insert ("fn", FN_NS);
+
+		foreach (Namespace ns in Ontology.get_namespaces ()) {
+			prefix_map.insert (ns.prefix, ns.uri);
+		}
+
+		parse_prologue ();
+
+		PtrArray blank_nodes = null;
+		if (blank) {
+			blank_nodes = new PtrArray ();
+		}
+
+		// SPARQL update supports multiple operations in a single query
+
+		while (current () != SparqlTokenType.EOF) {
 			switch (current ()) {
+			case SparqlTokenType.INSERT:
+				PtrArray* ptr = execute_insert (blank);
+				if (blank) {
+					blank_nodes.add (ptr);
+				}
+				break;
+			case SparqlTokenType.DELETE:
+				execute_delete ();
+				break;
+			case SparqlTokenType.DROP:
+				execute_drop_graph ();
+				break;
 			case SparqlTokenType.SELECT:
-				return execute_select ();
 			case SparqlTokenType.CONSTRUCT:
-				throw get_internal_error ("CONSTRUCT is not supported");
 			case SparqlTokenType.DESCRIBE:
-				throw get_internal_error ("DESCRIBE is not supported");
 			case SparqlTokenType.ASK:
-				return execute_ask ();
-			case SparqlTokenType.INSERT:
-			case SparqlTokenType.DELETE:
-			case SparqlTokenType.DROP:
-				throw get_error ("INSERT and DELETE are not supported in query mode");
+				throw get_error ("SELECT, CONSTRUCT, DESCRIBE, and ASK are not supported in update mode");
 			default:
-				throw get_error ("expected SELECT or ASK");
+				throw get_error ("expected INSERT or DELETE");
 			}
-		} else {
-			// SPARQL update supports multiple operations in a single query
-
-			while (current () != SparqlTokenType.EOF) {
-				switch (current ()) {
-				case SparqlTokenType.INSERT:
-					execute_insert ();
-					break;
-				case SparqlTokenType.DELETE:
-					execute_delete ();
-					break;
-				case SparqlTokenType.DROP:
-					execute_drop_graph ();
-					break;
-				case SparqlTokenType.SELECT:
-				case SparqlTokenType.CONSTRUCT:
-				case SparqlTokenType.DESCRIBE:
-				case SparqlTokenType.ASK:
-					throw get_error ("SELECT, CONSTRUCT, DESCRIBE, and ASK are not supported in update mode");
-				default:
-					throw get_error ("expected INSERT or DELETE");
-				}
-			}
-
-			return null;
 		}
+
+		return blank_nodes;
 	}
 
 	DBResultSet? exec_sql (string sql) throws Error {
@@ -798,25 +850,27 @@ public class Tracker.SparqlQuery : Object {
 		}
 	}
 
-	void execute_insert () throws Error {
+	PtrArray? execute_insert (bool blank) throws Error {
 		expect (SparqlTokenType.INSERT);
 		if (accept (SparqlTokenType.INTO)) {
 			parse_from_or_into_param ();
-		} else
+		} else {
 			current_graph = null;
-		execute_update (false);
+		}
+		return execute_insert_or_delete (false, blank);
 	}
 
 	void execute_delete () throws Error {
 		expect (SparqlTokenType.DELETE);
 		if (accept (SparqlTokenType.FROM)) {
 			parse_from_or_into_param ();
-		} else
+		} else {
 			current_graph = null;
-		execute_update (true);
+		}
+		execute_insert_or_delete (true, false);
 	}
 
-	void execute_update (bool delete_statements) throws Error {
+	PtrArray? execute_insert_or_delete (bool delete_statements, bool blank) throws Error {
 		// INSERT or DELETE
 
 		var pattern_sql = new StringBuilder ();
@@ -859,12 +913,19 @@ public class Tracker.SparqlQuery : Object {
 
 		this.delete_statements = delete_statements;
 
+		PtrArray update_blank_nodes = null;
+
+		if (blank) {
+			update_blank_nodes = new PtrArray ();
+		}
+
 		// iterate over all solutions
 		if (result_set != null) {
 			do {
 				// blank nodes in construct templates are per solution
 
 				uuid_generate (base_uuid);
+				blank_nodes = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
 
 				// get values of all variables to be bound
 				var var_value_map = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
@@ -879,6 +940,11 @@ public class Tracker.SparqlQuery : Object {
 
 				// iterate over each triple in the template
 				parse_construct_triples_block (var_value_map);
+
+				if (blank) {
+					HashTable<string,string>* ptr = (owned) blank_nodes;
+					update_blank_nodes.add (ptr);
+				}
 			} while (result_set.iter_next ());
 		}
 
@@ -888,6 +954,8 @@ public class Tracker.SparqlQuery : Object {
 		// ensure possible WHERE clause in next part gets the correct results
 		Data.update_buffer_flush ();
 		bindings = null;
+
+		return update_blank_nodes;
 	}
 
 	void execute_drop_graph () throws Error {
diff --git a/src/tracker-store/tracker-resources.c b/src/tracker-store/tracker-resources.c
index 57833b1..caa8d2c 100644
--- a/src/tracker-store/tracker-resources.c
+++ b/src/tracker-store/tracker-resources.c
@@ -250,6 +250,44 @@ tracker_resources_sparql_update (TrackerResources	 *self,
 	tracker_dbus_request_success (request_id);
 }
 
+void
+tracker_resources_sparql_update_blank (TrackerResources	      *self,
+				       const gchar	      *update,
+				       DBusGMethodInvocation  *context,
+				       GError		     **error)
+{
+	TrackerResourcesPrivate *priv;
+	GError 		     *actual_error = NULL;
+	guint		      request_id;
+	GPtrArray            *blank_nodes;
+
+	priv = TRACKER_RESOURCES_GET_PRIVATE (self);
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (update != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "D-Bus request for SPARQL Update, "
+				  "update:'%s'",
+				  update);
+
+	blank_nodes = tracker_store_sparql_update_blank (update, &actual_error);
+
+	if (actual_error) {
+		tracker_dbus_request_failed (request_id,
+					     &actual_error,
+					     NULL);
+		dbus_g_method_return_error (context, actual_error);
+		g_error_free (actual_error);
+		return;
+	}
+
+	dbus_g_method_return (context, blank_nodes);
+
+	tracker_dbus_request_success (request_id);
+}
+
 static void
 batch_update_callback (GError *error, gpointer user_data)
 {
diff --git a/src/tracker-store/tracker-resources.h b/src/tracker-store/tracker-resources.h
index aa135d7..8368e48 100644
--- a/src/tracker-store/tracker-resources.h
+++ b/src/tracker-store/tracker-resources.h
@@ -68,6 +68,10 @@ void              tracker_resources_sparql_update       (TrackerResources
 							 const gchar            *update,
 							 DBusGMethodInvocation  *context,
 							 GError                **error);
+void              tracker_resources_sparql_update_blank (TrackerResources       *object,
+							 const gchar            *update,
+							 DBusGMethodInvocation  *context,
+							 GError                **error);
 void              tracker_resources_batch_sparql_update (TrackerResources       *object,
 							 const gchar            *update,
 							 DBusGMethodInvocation  *context,
diff --git a/src/tracker-store/tracker-store.c b/src/tracker-store/tracker-store.c
index 0daca68..72e530e 100644
--- a/src/tracker-store/tracker-store.c
+++ b/src/tracker-store/tracker-store.c
@@ -536,6 +536,36 @@ tracker_store_sparql_update (const gchar *sparql,
 
 }
 
+GPtrArray *
+tracker_store_sparql_update_blank (const gchar *sparql,
+                                   GError     **error)
+{
+	TrackerStorePrivate *private;
+	GPtrArray *blank_nodes;
+
+	g_return_val_if_fail (sparql != NULL, NULL);
+
+	private = g_static_private_get (&private_key);
+	g_return_val_if_fail (private != NULL, NULL);
+
+	if (private->batch_mode) {
+		/* commit pending batch items */
+		tracker_data_commit_transaction ();
+		private->batch_mode = FALSE;
+		private->batch_count = 0;
+	}
+
+	tracker_data_begin_transaction ();
+	blank_nodes = tracker_data_update_sparql_blank (sparql, error);
+	tracker_data_commit_transaction ();
+
+	if (private->start_log) {
+		log_to_journal (private, sparql);
+	}
+
+	return blank_nodes;
+}
+
 TrackerDBResultSet*
 tracker_store_sparql_query (const gchar *sparql,
                             GError     **error)
diff --git a/src/tracker-store/tracker-store.h b/src/tracker-store/tracker-store.h
index a495800..da2b0c0 100644
--- a/src/tracker-store/tracker-store.h
+++ b/src/tracker-store/tracker-store.h
@@ -53,6 +53,8 @@ void         tracker_store_queue_turtle_import    (GFile         *file,
                                                    GDestroyNotify destroy);
 void         tracker_store_sparql_update          (const gchar   *sparql,
                                                    GError       **error);
+GPtrArray *  tracker_store_sparql_update_blank    (const gchar   *sparql,
+                                                   GError       **error);
 TrackerDBResultSet*
              tracker_store_sparql_query           (const gchar   *sparql,
                                                    GError       **error);
diff --git a/src/tracker-utils/tracker-sparql.c b/src/tracker-utils/tracker-sparql.c
index c4491bb..1c70ea5 100644
--- a/src/tracker-utils/tracker-sparql.c
+++ b/src/tracker-utils/tracker-sparql.c
@@ -393,7 +393,7 @@ main (int argc, char **argv)
 
 	if (query) {
 		if (G_UNLIKELY (update)) {
-			tracker_resources_sparql_update (client, query, &error);
+			results = tracker_resources_sparql_update_blank (client, query, &error);
 
 			if (error) {
 				g_printerr ("%s, %s\n",
@@ -403,6 +403,30 @@ main (int argc, char **argv)
 				
 				return FALSE;
 			}
+
+			if (results) {
+				GPtrArray *insert;
+				GHashTable *solution;
+				GHashTableIter iter;
+				gpointer key, value;
+				gint i, s, n;
+
+				for (i = 0; i < results->len; i++) {
+					insert = results->pdata[i];
+
+					for (s = 0; s < insert->len; s++) {
+						solution = insert->pdata[s];
+
+						g_hash_table_iter_init (&iter, solution);
+						n = 0;
+						while (g_hash_table_iter_next (&iter, &key, &value)) {
+							g_print ("%s%s: %s", n > 0 ? ", " : "", (const gchar *) key, (const gchar *) value);
+							n++;
+						}
+						g_print ("\n");
+					}
+				}
+			}
 		} else {
 			results = tracker_resources_sparql_query (client, query, &error);
 



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