[tracker] Add SPARQL query engine



commit 0ce92d07efc761d31ffce987971d550b5ab9dfa3
Author: Jürg Billeter <j bitron ch>
Date:   Thu Apr 9 10:15:31 2009 +0200

    Add SPARQL query engine
---
 configure.ac                                  |    3 +
 data/dbus/tracker-indexer.xml                 |    6 +
 data/dbus/tracker-resources.xml               |   15 +
 po/POTFILES.in                                |    1 +
 src/libtracker-common/Makefile.am             |    5 +-
 src/libtracker-common/libtracker-common.vapi  |   72 ++
 src/libtracker-data/.gitignore                |    2 +
 src/libtracker-data/Makefile.am               |   20 +-
 src/libtracker-data/libtracker-data.vapi      |   31 +
 src/libtracker-data/tracker-data-query.c      |   12 +-
 src/libtracker-data/tracker-data-update.c     |    9 +-
 src/libtracker-data/tracker-sparql-query.vala | 1168 +++++++++++++++++++++++++
 src/libtracker-db/Makefile.am                 |    5 +-
 src/libtracker-db/libtracker-db.vapi          |   55 ++
 src/libtracker/tracker.c                      |   48 +
 src/libtracker/tracker.h                      |    4 +
 src/rasqal/rasqal.vapi                        |  210 +++++
 src/tracker-indexer/tracker-indexer.c         |   36 +
 src/tracker-indexer/tracker-indexer.h         |    4 +
 src/tracker-utils/.gitignore                  |    1 +
 src/tracker-utils/Makefile.am                 |    6 +-
 src/tracker-utils/tracker-info.c              |  291 ++-----
 src/tracker-utils/tracker-search.c            |  175 +---
 src/tracker-utils/tracker-sparql.c            |  181 ++++
 src/trackerd/tracker-resources.c              |   80 ++
 src/trackerd/tracker-resources.h              |    8 +
 26 files changed, 2087 insertions(+), 361 deletions(-)

diff --git a/configure.ac b/configure.ac
index dfa89a8..36dd472 100644
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,9 @@ AM_CONDITIONAL(RASQAL_QUERY_RDQL, true)
 AM_CONDITIONAL(RASQAL_QUERY_LAQRS, true)
 AM_CONDITIONAL(RASQAL_QUERY_SPARQL, true)
 
+AC_PATH_PROG(VALAC, valac, valac)
+AC_SUBST(VALAC)
+
 # Check we have the DBUS binding tool we need
 AC_PATH_PROG(DBUSBINDINGTOOL, dbus-binding-tool)
 if test -z $DBUSBINDINGTOOL; then
diff --git a/data/dbus/tracker-indexer.xml b/data/dbus/tracker-indexer.xml
index a42f963..0c26ef9 100644
--- a/data/dbus/tracker-indexer.xml
+++ b/data/dbus/tracker-indexer.xml
@@ -65,6 +65,12 @@
       <arg type="s" name="object" direction="in" />
     </method>
 
+    <!-- SPARQL Update extensions, allows bulk insert and delete -->
+    <method name="SparqlUpdate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="query" direction="in" />
+    </method>
+
     <method name="Pause">
       <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
     </method>
diff --git a/data/dbus/tracker-resources.xml b/data/dbus/tracker-resources.xml
index 74f8e25..233fc64 100644
--- a/data/dbus/tracker-resources.xml
+++ b/data/dbus/tracker-resources.xml
@@ -25,5 +25,20 @@
       <arg type="s" name="uri" direction="in" />
     </method>
 
+    <!-- SPARQL Query without updates -->
+    <method name="SparqlQuery">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" 
+		  value="QVector&lt;QStringList&gt;"/>
+      <arg type="s" name="query" direction="in" />
+      <arg type="aas" name="result" direction="out" />
+    </method>
+
+    <!-- SPARQL Update extensions, allows bulk insert and delete -->
+    <method name="SparqlUpdate">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+      <arg type="s" name="query" direction="in" />
+    </method>
+
   </interface>
 </node>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 076264c..73db1d1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -29,6 +29,7 @@ src/tracker-utils/tracker-info.c
 src/tracker-utils/tracker-meta-folder.c
 src/tracker-utils/tracker-processes.c
 src/tracker-utils/tracker-search.c
+src/tracker-utils/tracker-sparql.c
 src/tracker-utils/tracker-stats.c
 src/tracker-utils/tracker-status.c
 src/tracker-utils/tracker-tag.c
diff --git a/src/libtracker-common/Makefile.am b/src/libtracker-common/Makefile.am
index a267173..d4a9335 100644
--- a/src/libtracker-common/Makefile.am
+++ b/src/libtracker-common/Makefile.am
@@ -112,4 +112,7 @@ BUILT_SOURCES =						\
 
 CLEANFILES = $(BUILT_SOURCES)
 
-EXTRA_DIST = tracker-marshal.list
+EXTRA_DIST = 						\
+	tracker-marshal.list				\
+	libtracker-common.vapi
+
diff --git a/src/libtracker-common/libtracker-common.vapi b/src/libtracker-common/libtracker-common.vapi
new file mode 100644
index 0000000..1939efc
--- /dev/null
+++ b/src/libtracker-common/libtracker-common.vapi
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (cheader_filename = "libtracker-common/tracker-class.h")]
+	public class Class : GLib.Object {
+		public string name { get; set; }
+		public string uri { get; set; }
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-namespace.h")]
+	public class Namespace : GLib.Object {
+		public string prefix { get; set; }
+		public string uri { get; set; }
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-property.h")]
+	public class Property : GLib.Object {
+		public string name { get; set; }
+		public string uri { get; set; }
+		public PropertyType data_type { get; set; }
+		public Class domain { get; set; }
+		public Class range { get; set; }
+		public bool multiple_values { get; set; }
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-property.h")]
+	public enum PropertyType {
+		STRING,
+		BOOLEAN,
+		INTEGER,
+		DOUBLE,
+		DATE,
+		DATETIME,
+		BLOB,
+		STRUCT,
+		RESOURCE,
+		FULLTEXT
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-ontology.h")]
+	namespace Ontology {
+		public weak Class get_class_by_uri (string class_uri);
+		public weak Property get_property_by_uri (string property_uri);
+		[CCode (array_length = false, array_null_terminated = true)]
+		public weak Namespace[] get_namespaces ();
+		[CCode (array_length = false, array_null_terminated = true)]
+		public weak Class[] get_classes ();
+		[CCode (array_length = false, array_null_terminated = true)]
+		public weak Property[] get_properties ();
+	}
+
+	[CCode (cheader_filename = "libtracker-common/tracker-type-utils.h")]
+	public int string_to_date (string date_string);
+}
+
diff --git a/src/libtracker-data/.gitignore b/src/libtracker-data/.gitignore
new file mode 100644
index 0000000..0275713
--- /dev/null
+++ b/src/libtracker-data/.gitignore
@@ -0,0 +1,2 @@
+tracker-sparql-query.c
+tracker-sparql-query.h
diff --git a/src/libtracker-data/Makefile.am b/src/libtracker-data/Makefile.am
index caf61ad..4318784 100644
--- a/src/libtracker-data/Makefile.am
+++ b/src/libtracker-data/Makefile.am
@@ -10,11 +10,17 @@ INCLUDES =								\
 	$(DBUS_CFLAGS)							\
 	$(UUID_CFLAGS)							\
 	$(RAPTOR_CFLAGS)						\
+	-I$(top_srcdir)/src/rasqal					\
 	$(GCOV_CFLAGS)
 
+BUILT_SOURCES = libtracker-data.vala.stamp
+
 libtracker_datadir = $(libdir)/tracker-$(TRACKER_API_VERSION)
 libtracker_data_LTLIBRARIES = libtracker-data.la
 
+libtracker_data_la_VALASOURCES = 					\
+	tracker-sparql-query.vala
+
 libtracker_data_la_SOURCES = 						\
 	tracker-data-backup.c						\
 	tracker-data-manager.c						\
@@ -22,7 +28,9 @@ libtracker_data_la_SOURCES = 						\
 	tracker-data-search.c						\
 	tracker-data-update.c						\
 	tracker-query-tree.c						\
-	tracker-turtle.c
+	libtracker-data.vala.stamp					\
+	tracker-turtle.c						\
+	$(libtracker_data_la_VALASOURCES:.vala=.c)
 
 noinst_HEADERS =							\
 	tracker-data-backup.h						\
@@ -31,8 +39,13 @@ noinst_HEADERS =							\
 	tracker-data-search.h						\
 	tracker-data-update.h						\
 	tracker-query-tree.h						\
+	tracker-sparql-query.h						\
 	tracker-turtle.h
 
+libtracker-data.vala.stamp: $(libtracker_data_la_VALASOURCES)
+	$(VALAC) -C -H tracker-sparql-query.h ../rasqal/rasqal.vapi ../libtracker-common/libtracker-common.vapi libtracker-data.vapi ../libtracker-db/libtracker-db.vapi $^
+	touch $@
+
 libtracker_data_la_LDFLAGS =						\
 	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
 
@@ -43,6 +56,11 @@ libtracker_data_la_LIBADD = 						\
 	$(GLIB2_LIBS)							\
 	$(UUID_LIBS)							\
 	$(RAPTOR_LIBS)							\
+	$(top_builddir)/src/rasqal/librasqal.la				\
 	$(GCOV_LIBS)							\
 	-lz
 
+EXTRA_DIST = $(libtracker_data_la_VALASOURCES)				\
+	libtracker-data.vala.stamp					\
+	libtracker-data.vapi
+
diff --git a/src/libtracker-data/libtracker-data.vapi b/src/libtracker-data/libtracker-data.vapi
new file mode 100644
index 0000000..db0aba8
--- /dev/null
+++ b/src/libtracker-data/libtracker-data.vapi
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (cheader_filename = "libtracker-data/tracker-data-query.h,libtracker-data/tracker-data-search.h,libtracker-data/tracker-data-update.h")]
+	namespace Data {
+		public int query_resource_id (string uri);
+		public void begin_transaction ();
+		public void commit_transaction ();
+		public void delete_statement (string subject, string predicate, string object);
+		public void insert_statement (string subject, string predicate, string object);
+		public int[] search_get_matches (string search_string);
+	}
+}
+
diff --git a/src/libtracker-data/tracker-data-query.c b/src/libtracker-data/tracker-data-query.c
index 748126c..7f08bc8 100644
--- a/src/libtracker-data/tracker-data-query.c
+++ b/src/libtracker-data/tracker-data-query.c
@@ -40,6 +40,7 @@
 
 #include "tracker-data-manager.h"
 #include "tracker-data-query.h"
+#include "tracker-sparql-query.h"
 
 static gchar *
 get_string_for_value (GValue *value)
@@ -429,10 +430,17 @@ TrackerDBResultSet *
 tracker_data_query_sparql (const gchar  *query,
 			   GError      **error)
 {
+	TrackerSparqlQuery *sparql_query;
+	TrackerDBResultSet *result_set;
+
 	g_return_val_if_fail (query != NULL, NULL);
 
-	/* TODO */
+	sparql_query = tracker_sparql_query_new (query);
+
+	result_set = tracker_sparql_query_execute (sparql_query, error);
 
-	return NULL;
+	g_object_unref (sparql_query);
+
+	return result_set;
 }
 
diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c
index 6e9e3b2..7c4e8c8 100644
--- a/src/libtracker-data/tracker-data-update.c
+++ b/src/libtracker-data/tracker-data-update.c
@@ -37,6 +37,7 @@
 #include "tracker-data-manager.h"
 #include "tracker-data-update.h"
 #include "tracker-data-query.h"
+#include "tracker-sparql-query.h"
 
 #define RDF_PREFIX TRACKER_RDF_PREFIX
 #define RDFS_PREFIX TRACKER_RDFS_PREFIX
@@ -1572,8 +1573,14 @@ void
 tracker_data_update_sparql (const gchar  *update,
 			    GError      **error)
 {
+	TrackerSparqlQuery *sparql_query;
+
 	g_return_if_fail (update != NULL);
 
-	/* TODO */
+	sparql_query = tracker_sparql_query_new_update (update);
+
+	tracker_sparql_query_execute (sparql_query, error);
+
+	g_object_unref (sparql_query);
 }
 
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
new file mode 100644
index 0000000..b9ba507
--- /dev/null
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -0,0 +1,1168 @@
+/*
+ * Copyright (C) 2008-2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+public errordomain Tracker.SparqlError {
+	PARSE,
+	UNKNOWN_CLASS,
+	UNKNOWN_PROPERTY,
+	TYPE,
+	INTERNAL
+}
+
+public class Tracker.SparqlQuery : Object {
+	// Represents a SQL table
+	class DataTable : Object {
+		public string sql_db_tablename; // as in db schema
+		public string sql_query_tablename; // temp. name, generated
+		public PredicateVariable predicate_variable;
+	}
+
+	abstract class DataBinding : Object {
+		public bool is_uri;
+		public bool is_boolean;
+		public bool is_datetime;
+		public DataTable table;
+		public string sql_db_column_name;
+	}
+
+	// Represents a mapping of a SPARQL literal to a SQL table and column
+	class LiteralBinding : DataBinding {
+		public bool is_fts_match;
+		public string literal;
+		public Rasqal.Literal.Type literal_type;
+	}
+
+	// Represents a mapping of a SPARQL variable to a SQL table and column
+	class VariableBinding : DataBinding {
+		public string variable;
+		// Specified whether SQL column may contain NULL entries
+		public bool maybe_null;
+	}
+
+	class VariableBindingList : Object {
+		public List<VariableBinding> list;
+	}
+
+	// Represents a variable used as a predicate
+	class PredicateVariable : Object {
+		public string? subject;
+		public string? object;
+
+		public Class? domain;
+
+		public string get_sql_query () throws Error {
+			var sql = new StringBuilder ();
+
+			if (subject != null) {
+				// single subject
+				var subject_id = Data.query_resource_id (subject);
+
+				DBResultSet result_set = null;
+				if (subject_id > 0) {
+					var iface = DBManager.get_db_interface ();
+					var stmt = iface.create_statement ("SELECT (SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"rdf:type\") FROM \"rdfs:Resource_rdf:type\" WHERE ID = ?");
+					stmt.bind_int (0, subject_id);
+					result_set = stmt.execute ();
+				}
+
+				if (result_set != null) {
+					bool first = true;
+					do {
+						Value value;
+						result_set._get_value (0, out value);
+						var domain = Ontology.get_class_by_uri (value.get_string ());
+
+						foreach (Property prop in Ontology.get_properties ()) {
+							if (prop.domain == domain) {
+								if (first) {
+									first = false;
+								} else {
+									sql.append (" UNION ");
+								}
+								sql.append_printf ("SELECT ID, (SELECT ID FROM \"rdfs:Resource\" WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
+
+								if (prop.data_type == PropertyType.RESOURCE) {
+									sql.append_printf ("(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"%s\")", prop.name);
+								} else if (prop.data_type == PropertyType.INTEGER || prop.data_type == PropertyType.DOUBLE) {
+									sql.append_printf ("CAST (\"%s\" AS TEXT)", prop.name);
+								} else if (prop.data_type == PropertyType.BOOLEAN) {
+									sql.append_printf ("CASE \"%s\" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END", prop.name);
+								} else if (prop.data_type == PropertyType.DATETIME) {
+									sql.append_printf ("strftime (\"%%Y-%%m-%%dT%%H:%%M:%%S\", \"%s\", \"unixepoch\")", prop.name);
+								} else {
+									sql.append_printf ("\"%s\"", prop.name);
+								}
+
+								sql.append (" AS \"object\" FROM ");
+								if (prop.multiple_values) {
+									sql.append_printf ("\"%s_%s\"", prop.domain.name, prop.name);
+								} else {
+									sql.append_printf ("\"%s\"", prop.domain.name);
+								}
+
+								sql.append_printf (" WHERE ID = %d", subject_id);
+							}
+						}
+					} while (result_set.iter_next ());
+				} else {
+					/* no match */
+					sql.append ("SELECT NULL AS ID, NULL AS \"predicate\", NULL AS \"object\"");
+				}
+			} else if (object != null) {
+				// single object
+				var object_id = Data.query_resource_id (object);
+
+				var iface = DBManager.get_db_interface ();
+				var stmt = iface.create_statement ("SELECT (SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"rdf:type\") FROM \"rdfs:Resource_rdf:type\" WHERE ID = ?");
+				stmt.bind_int (0, object_id);
+				var result_set = stmt.execute ();
+
+				bool first = true;
+				if (result_set != null) {
+					do {
+						Value value;
+						result_set._get_value (0, out value);
+						var range = Ontology.get_class_by_uri (value.get_string ());
+
+						foreach (Property prop in Ontology.get_properties ()) {
+							if (prop.range == range) {
+								if (first) {
+									first = false;
+								} else {
+									sql.append (" UNION ");
+								}
+								sql.append_printf ("SELECT ID, (SELECT ID FROM \"rdfs:Resource\" WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
+
+								if (prop.data_type == PropertyType.RESOURCE) {
+									sql.append_printf ("(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"%s\")", prop.name);
+								} else if (prop.data_type == PropertyType.INTEGER || prop.data_type == PropertyType.DOUBLE) {
+									sql.append_printf ("CAST (\"%s\" AS TEXT)", prop.name);
+								} else if (prop.data_type == PropertyType.BOOLEAN) {
+									sql.append_printf ("CASE \"%s\" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END", prop.name);
+								} else if (prop.data_type == PropertyType.DATETIME) {
+									sql.append_printf ("strftime (\"%%Y-%%m-%%dT%%H:%%M:%%S\", \"%s\", \"unixepoch\")", prop.name);
+								} else {
+									sql.append_printf ("\"%s\"", prop.name);
+								}
+
+								sql.append (" AS \"object\" FROM ");
+								if (prop.multiple_values) {
+									sql.append_printf ("\"%s_%s\"", prop.domain.name, prop.name);
+								} else {
+									sql.append_printf ("\"%s\"", prop.domain.name);
+								}
+							}
+						}
+					} while (result_set.iter_next ());
+				}
+			} else if (domain != null) {
+				// any subject, predicates limited to a specific domain
+				bool first = true;
+				foreach (Property prop in Ontology.get_properties ()) {
+					if (prop.domain == domain) {
+						if (first) {
+							first = false;
+						} else {
+							sql.append (" UNION ");
+						}
+						sql.append_printf ("SELECT ID, (SELECT ID FROM \"rdfs:Resource\" WHERE Uri = '%s') AS \"predicate\", ", prop.uri);
+
+						if (prop.data_type == PropertyType.RESOURCE) {
+							sql.append_printf ("(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"%s\")", prop.name);
+						} else if (prop.data_type == PropertyType.INTEGER || prop.data_type == PropertyType.DOUBLE) {
+							sql.append_printf ("CAST (\"%s\" AS TEXT)", prop.name);
+						} else if (prop.data_type == PropertyType.BOOLEAN) {
+							sql.append_printf ("CASE \"%s\" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END", prop.name);
+						} else if (prop.data_type == PropertyType.DATETIME) {
+							sql.append_printf ("strftime (\"%%Y-%%m-%%dT%%H:%%M:%%S\", \"%s\", \"unixepoch\")", prop.name);
+						} else {
+							sql.append_printf ("\"%s\"", prop.name);
+						}
+
+						sql.append (" AS \"object\" FROM ");
+						if (prop.multiple_values) {
+							sql.append_printf ("\"%s_%s\"", prop.domain.name, prop.name);
+						} else {
+							sql.append_printf ("\"%s\"", prop.domain.name);
+						}
+					}
+				}
+			} else {
+				// UNION over all properties would exceed SQLite limits
+				throw new SparqlError.INTERNAL ("Unrestricted predicate variables not supported");
+			}
+			return sql.str;
+		}
+	}
+
+	string query_string;
+	bool update_extensions;
+
+	StringBuilder pattern_sql = new StringBuilder ();
+
+	// All SQL tables
+	List<DataTable> tables;
+	HashTable<string,DataTable> table_map;
+
+	// All SPARQL literals
+	List<LiteralBinding> bindings;
+	List<LiteralBinding> pattern_bindings;
+
+	// All SPARQL variables
+	HashTable<string,VariableBinding> var_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
+	List<string> pattern_variables;
+	HashTable<string,VariableBindingList> pattern_var_map;
+
+	// Variables used as predicates
+	HashTable<string,PredicateVariable> predicate_variable_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
+
+	int counter;
+
+	int bnodeid = 0;
+	// base UUID used for blank nodes
+	uchar[] base_uuid;
+
+	string error_message;
+
+	public SparqlQuery (string query) {
+		this.query_string = query;
+	}
+
+	public SparqlQuery.update (string query) {
+		this (query);
+		this.update_extensions = true;
+	}
+
+	string get_sql_for_literal (Rasqal.Literal literal) {
+		assert (literal.type == Rasqal.Literal.Type.VARIABLE);
+
+		string variable_name = literal.as_variable ().name;
+
+		return "\"%s\"".printf (variable_name);
+	}
+
+	string get_sql_for_expression (Rasqal.Expression expr) {
+		if (expr.op == Rasqal.Op.COUNT) {
+			return "COUNT(%s)".printf (get_sql_for_expression (expr.arg1));
+		} else if (expr.op == Rasqal.Op.SUM) {
+			return "SUM(%s)".printf (get_sql_for_expression (expr.arg1));
+		} else if (expr.op == Rasqal.Op.AVG) {
+			return "AVG(%s)".printf (get_sql_for_expression (expr.arg1));
+		} else if (expr.op == Rasqal.Op.MIN) {
+			return "MIN(%s)".printf (get_sql_for_expression (expr.arg1));
+		} else if (expr.op == Rasqal.Op.MAX) {
+			return "MAX(%s)".printf (get_sql_for_expression (expr.arg1));
+		} else if (expr.op == Rasqal.Op.VARSTAR) {
+			return "*";
+		} else if (expr.op == Rasqal.Op.LITERAL) {
+			return get_sql_for_literal (expr.literal);
+		}
+		return "NULL";
+	}
+
+	string generate_bnodeid_handler (Rasqal.Query? query, 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 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));
+		}
+	}
+
+	void error_handler (Raptor.Locator? locator, string message) {
+		if (error_message == null) {
+			// return first, not last, error message
+			error_message = message;
+		}
+	}
+
+	public DBResultSet? execute () throws Error {
+		var world = new Rasqal.World ();
+		world.open ();
+
+		// use LAQRS - extension to SPARQL - to support aggregation
+		var query = new Rasqal.Query (world, "laqrs", null);
+
+		foreach (Namespace ns in Ontology.get_namespaces ()) {
+			query.add_prefix (new Rasqal.Prefix (world, ns.prefix, ns.uri));
+		}
+
+		query.declare_prefixes ();
+
+		base_uuid = new uchar[16];
+		uuid_generate (base_uuid);
+		query.set_generate_bnodeid_handler (generate_bnodeid_handler);
+
+		query.set_warning_handler (error_handler);
+		query.set_error_handler (error_handler);
+		query.set_fatal_error_handler (error_handler);
+
+		query.prepare (this.query_string, null);
+		if (error_message != null) {
+			throw new SparqlError.PARSE (error_message);
+		}
+
+		if (!update_extensions) {
+			if (query.get_verb () == Rasqal.QueryVerb.SELECT) {
+				return execute_select (query);
+			} else if (query.get_verb () == Rasqal.QueryVerb.CONSTRUCT) {
+				throw new SparqlError.INTERNAL ("CONSTRUCT is not supported");
+			} else if (query.get_verb () == Rasqal.QueryVerb.DESCRIBE) {
+				throw new SparqlError.INTERNAL ("DESCRIBE is not supported");
+			} else if (query.get_verb () == Rasqal.QueryVerb.ASK) {
+				throw new SparqlError.INTERNAL ("ASK is not supported");
+			} else {
+				throw new SparqlError.PARSE ("DELETE and INSERT are not supported in query mode");
+			}
+		} else {
+			if (query.get_verb () == Rasqal.QueryVerb.INSERT) {
+				execute_insert (query);
+				return null;
+			} else if (query.get_verb () == Rasqal.QueryVerb.DELETE) {
+				execute_delete (query);
+				return null;
+			} else {
+				throw new SparqlError.PARSE ("SELECT, CONSTRUCT, DESCRIBE, and ASK are not supported in update mode");
+			}
+		}
+	}
+
+	string get_sql_for_variable (string variable_name) {
+		var binding = var_map.lookup (variable_name);
+		assert (binding != null);
+		if (binding.is_uri) {
+			return "(SELECT Uri FROM \"rdfs:Resource\" WHERE ID = \"%s\")".printf (variable_name);
+		} else if (binding.is_boolean) {
+			return "(CASE \"%s\" WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE NULL END)".printf (variable_name);
+		} else if (binding.is_datetime) {
+			return "strftime (\"%%Y-%%m-%%dT%%H:%%M:%%S\", \"%s\", \"unixepoch\")".printf (variable_name);
+		} else {
+			return "\"%s\"".printf (variable_name);
+		}
+	}
+
+	DBResultSet? exec_sql (string sql) throws Error {
+		var iface = DBManager.get_db_interface ();
+		var stmt = iface.create_statement ("%s", sql);
+
+		// set literals specified in query
+		int i = 0;
+		foreach (LiteralBinding binding in bindings) {
+			if (binding.is_boolean) {
+				if (binding.literal == "true" || binding.literal == "1") {
+					stmt.bind_int (i, 1);
+				} else if (binding.literal == "false" || binding.literal == "0") {
+					stmt.bind_int (i, 0);
+				} else {
+					throw new SparqlError.TYPE ("`%s' is not a valid boolean".printf (binding.literal));
+				}
+			} else if (binding.is_datetime) {
+				stmt.bind_int (i, string_to_date (binding.literal));
+			} else if (binding.literal_type == Rasqal.Literal.Type.INTEGER) {
+				stmt.bind_int (i, binding.literal.to_int ());
+			} else {
+				stmt.bind_text (i, binding.literal);
+			}
+			i++;
+		}
+
+		return stmt.execute ();
+	}
+
+	DBResultSet? execute_select (Rasqal.Query query) throws Error {
+		// SELECT query
+
+		// process WHERE clause
+		visit_graph_pattern (query.get_query_graph_pattern ());
+
+		// build SQL
+		var sql = new StringBuilder ();
+
+		sql.append ("SELECT ");
+		if (query.get_distinct ()) {
+			sql.append ("DISTINCT ");
+		}
+		bool first = true;
+		for (int var_idx = 0; true; var_idx++) {
+			weak Rasqal.Variable variable = query.get_variable (var_idx);
+			if (variable == null) {
+				break;
+			}
+
+			if (!first) {
+				sql.append (", ");
+			} else {
+				first = false;
+			}
+			
+			if (variable.expression != null) {
+				// LAQRS aggregate expression
+				sql.append (get_sql_for_expression (variable.expression));
+			} else {
+				sql.append (get_sql_for_variable (variable.name));
+			}
+		}
+
+		// select from results of WHERE clause
+		sql.append (" FROM (");
+		sql.append (pattern_sql.str);
+		sql.append (")");
+
+		// GROUP BY (SPARQL extension, LAQRS)
+		first = true;
+		for (int group_idx = 0; true; group_idx++) {
+			weak Rasqal.Expression group = query.get_group_condition (group_idx);
+			if (group == null) {
+				break;
+			}
+
+			if (!first) {
+				sql.append (", ");
+			} else {
+				sql.append (" GROUP BY ");
+				first = false;
+			}
+			assert (group.op == Rasqal.Op.GROUP_COND_ASC || group.op == Rasqal.Op.GROUP_COND_DESC);
+			assert (group.arg1.op == Rasqal.Op.LITERAL);
+			assert (group.arg1.literal.type == Rasqal.Literal.Type.VARIABLE);
+			string variable_name = group.arg1.literal.as_variable ().name;
+
+			sql.append (get_sql_for_variable (variable_name));
+
+			if (group.op == Rasqal.Op.GROUP_COND_DESC) {
+				sql.append (" DESC");
+			}
+		}
+
+		// ORDER BY
+		first = true;
+		for (int order_idx = 0; true; order_idx++) {
+			weak Rasqal.Expression order = query.get_order_condition (order_idx);
+			if (order == null) {
+				break;
+			}
+
+			if (!first) {
+				sql.append (", ");
+			} else {
+				sql.append (" ORDER BY ");
+				first = false;
+			}
+			assert (order.op == Rasqal.Op.ORDER_COND_ASC || order.op == Rasqal.Op.ORDER_COND_DESC);
+			assert (order.arg1.op == Rasqal.Op.LITERAL);
+			assert (order.arg1.literal.type == Rasqal.Literal.Type.VARIABLE);
+			string variable_name = order.arg1.literal.as_variable ().name;
+
+			sql.append (get_sql_for_variable (variable_name));
+
+			if (order.op == Rasqal.Op.ORDER_COND_DESC) {
+				sql.append (" DESC");
+			}
+		}
+
+		// LIMIT and OFFSET
+		if (query.get_limit () >= 0) {
+			sql.append_printf (" LIMIT %d", query.get_limit ());
+			if (query.get_offset () >= 0) {
+				sql.append_printf (" OFFSET %d", query.get_offset ());
+			}
+		}
+
+		return exec_sql (sql.str);
+	}
+
+	void execute_insert (Rasqal.Query query) throws Error {
+		execute_update (query, false);
+	}
+
+	void execute_delete (Rasqal.Query query) throws Error {
+		execute_update (query, true);
+	}
+
+	void execute_update (Rasqal.Query query, bool delete_statements) throws Error {
+		// INSERT or DELETE
+
+		var sql = new StringBuilder ();
+
+		// process WHERE clause
+		if (query.get_query_graph_pattern () != null) {
+			visit_graph_pattern (query.get_query_graph_pattern ());
+
+			// build SQL
+			sql.append ("SELECT ");
+			bool first = true;
+			foreach (VariableBinding binding in var_map.get_values ()) {
+				if (!first) {
+					sql.append (", ");
+				} else {
+					first = false;
+				}
+			
+				sql.append (get_sql_for_variable (binding.variable));
+			}
+
+			// select from results of WHERE clause
+			sql.append (" FROM (");
+			sql.append (pattern_sql.str);
+			sql.append (")");
+		} else {
+			sql.append ("SELECT 1");
+		}
+
+		var result_set = exec_sql (sql.str);
+
+		// all updates should be committed in one transaction
+		Data.begin_transaction ();
+
+		// iterate over all solutions
+		if (result_set != null) {
+			do {
+				// 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);
+				int var_idx = 0;
+				foreach (string var_name in var_map.get_keys ()) {
+					Value value;
+					result_set._get_value (var_idx++, out value);
+					var_value_map.insert (var_name, get_string_for_value (value));
+				}
+
+				// iterate over each triple in the template
+				for (int triple_idx = 0; true; triple_idx++) {
+					weak Rasqal.Triple triple = query.get_construct_triple (triple_idx);
+					if (triple == null) {
+						break;
+					}
+					
+					string subject, predicate, object;
+
+					if (triple.subject.type == Rasqal.Literal.Type.VARIABLE) {
+						subject = var_value_map.lookup (triple.subject.as_variable ().name);
+					} else {
+						subject = get_string_from_literal (triple.subject);
+					}
+
+					if (triple.predicate.type == Rasqal.Literal.Type.VARIABLE) {
+						predicate = var_value_map.lookup (triple.predicate.as_variable ().name);
+					} else {
+						predicate = get_string_from_literal (triple.predicate);
+					}
+
+					if (triple.object.type == Rasqal.Literal.Type.VARIABLE) {
+						object = var_value_map.lookup (triple.object.as_variable ().name);
+					} else {
+						object = get_string_from_literal (triple.object);
+					}
+
+					if (delete_statements) {
+						// delete triple from database
+						Data.delete_statement (subject, predicate, object);
+					} else {
+						// insert triple into database
+						Data.insert_statement (subject, predicate, object);
+					}
+				}
+			} while (result_set.iter_next ());
+		}
+
+		Data.commit_transaction ();
+	}
+
+	void visit_graph_pattern (Rasqal.GraphPattern graph_pattern) throws SparqlError {
+		bool first_where = true;
+		if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.BASIC) {
+			tables = new List<DataTable> ();
+			table_map = new HashTable<string,DataTable>.full (str_hash, str_equal, g_free, g_object_unref);
+
+			pattern_variables = new List<string> ();
+			pattern_var_map = new HashTable<string,VariableBindingList>.full (str_hash, str_equal, g_free, g_object_unref);
+
+			pattern_bindings = new List<LiteralBinding> ();
+
+			pattern_sql.append ("SELECT ");
+
+			for (int triple_idx = 0; true; triple_idx++) {
+				weak Rasqal.Triple triple = graph_pattern.get_triple (triple_idx);
+				if (triple == null) {
+					break;
+				}
+
+				visit_triple (triple);
+			}
+
+			// remove last comma and space
+			pattern_sql.truncate (pattern_sql.len - 2);
+
+			pattern_sql.append (" FROM ");
+			bool first = true;
+			foreach (DataTable table in tables) {
+				if (!first) {
+					pattern_sql.append (", ");
+				} else {
+					first = false;
+				}
+				if (table.sql_db_tablename != null) {
+					pattern_sql.append_printf ("\"%s\"", table.sql_db_tablename);
+				} else {
+					pattern_sql.append_printf ("(%s)", table.predicate_variable.get_sql_query ());
+				}
+				pattern_sql.append_printf (" AS \"%s\"", table.sql_query_tablename);
+			}
+
+			foreach (string variable in pattern_variables) {
+				bool maybe_null = true;
+				string last_name = null;
+				foreach (VariableBinding binding in pattern_var_map.lookup (variable).list) {
+					string name = "\"%s\".\"%s\"".printf (binding.table.sql_query_tablename, binding.sql_db_column_name);
+					if (last_name != null) {
+						if (!first_where) {
+							pattern_sql.append (" AND ");
+						} else {
+							pattern_sql.append (" WHERE ");
+							first_where = false;
+						}
+						pattern_sql.append (last_name);
+						pattern_sql.append (" = ");
+						pattern_sql.append (name);
+					}
+					last_name = name;
+					if (!binding.maybe_null) {
+						maybe_null = false;
+					}
+				}
+
+				if (maybe_null) {
+					// ensure that variable is bound in case it could return NULL in SQL
+					// assuming SPARQL variable is not optional
+					if (!first_where) {
+						pattern_sql.append (" AND ");
+					} else {
+						pattern_sql.append (" WHERE ");
+						first_where = false;
+					}
+					pattern_sql.append_printf ("%s IS NOT NULL", variable);
+				}
+			}
+			foreach (LiteralBinding binding in pattern_bindings) {
+				if (!first_where) {
+					pattern_sql.append (" AND ");
+				} else {
+					pattern_sql.append (" WHERE ");
+					first_where = false;
+				}
+				pattern_sql.append ("\"");
+				pattern_sql.append (binding.table.sql_query_tablename);
+				pattern_sql.append ("\".\"");
+				pattern_sql.append (binding.sql_db_column_name);
+				pattern_sql.append ("\"");
+				if (binding.is_fts_match) {
+					pattern_sql.append (" IN (");
+
+					// include matches from fulltext search
+					first = true;
+					foreach (int match_id in Data.search_get_matches (binding.literal)) {
+						if (!first) {
+							pattern_sql.append (",");
+						} else {
+							first = false;
+						}
+						pattern_sql.append_printf ("%d", match_id);
+					}
+
+					pattern_sql.append (")");
+				} else {
+					pattern_sql.append (" = ");
+					if (binding.is_uri) {
+						pattern_sql.append ("(SELECT ID FROM \"rdfs:Resource\" WHERE Uri = ?)");
+					} else {
+						pattern_sql.append ("?");
+					}
+				}
+			}
+
+			tables = null;
+			table_map = null;
+			pattern_variables = null;
+			pattern_var_map = null;
+			pattern_bindings = null;
+		} else if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.GROUP
+		           || graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.OPTIONAL) {
+			pattern_sql.append ("SELECT * FROM (");
+			long subgraph_start = pattern_sql.len;
+			int subgraph_idx = 0;
+			for (int pattern_idx = 0; true; pattern_idx++) {
+				weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
+				if (sub_graph_pattern == null) {
+					break;
+				}
+
+				if (sub_graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.FILTER) {
+					// ignore filters, processed later
+					continue;
+				}
+
+				if (subgraph_idx > 1) {
+					// additional (SELECT * FROM ...) necessary
+					// when using more than two subgraphs to
+					// work around SQLite bug with NATURAL JOINs
+					pattern_sql.insert (subgraph_start, "(SELECT * FROM ");
+					pattern_sql.append (")");
+				}
+
+				if (subgraph_idx > 0) {
+					if (sub_graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.OPTIONAL) {
+						pattern_sql.append (" NATURAL LEFT JOIN ");
+					} else {
+						pattern_sql.append (" NATURAL CROSS JOIN ");
+					}
+				}
+				pattern_sql.append ("(");
+				visit_graph_pattern (sub_graph_pattern);
+				pattern_sql.append (")");
+
+				// differs from pattern_idx due to filters
+				subgraph_idx++;
+			}
+			pattern_sql.append (")");
+		} else if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.UNION) {
+			for (int pattern_idx = 0; true; pattern_idx++) {
+				weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
+				if (sub_graph_pattern == null) {
+					break;
+				}
+
+				if (sub_graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.FILTER) {
+					// ignore filters, processed later
+					continue;
+				}
+
+				if (pattern_idx > 0) {
+					pattern_sql.append (" UNION ");
+				}
+				visit_graph_pattern (sub_graph_pattern);
+			}
+		}
+
+		// process filters
+		for (int pattern_idx = 0; true; pattern_idx++) {
+			weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
+			if (sub_graph_pattern == null) {
+				break;
+			}
+
+			if (sub_graph_pattern.get_operator () != Rasqal.GraphPattern.Operator.FILTER) {
+				// ignore non-filter subgraphs
+				continue;
+			}
+
+			weak Rasqal.Expression filter = sub_graph_pattern.get_filter_expression ();
+
+			if (!first_where) {
+				pattern_sql.append (" AND ");
+			} else {
+				pattern_sql.append (" WHERE ");
+				first_where = false;
+			}
+
+			visit_filter (filter);
+		}
+	}
+
+	string get_string_from_literal (Rasqal.Literal lit, bool is_subject = false) {
+		if (lit.type == Rasqal.Literal.Type.BLANK) {
+			if (!is_subject && lit.as_string ().has_prefix (":")) {
+				// anonymous blank node, libtracker-data will
+				// generate appropriate uri
+				return lit.as_string ();
+			} else {
+				return generate_bnodeid_handler (null, lit.as_string ());
+			}
+		} else {
+			return lit.as_string ();
+		}
+	}
+
+	void visit_triple (Rasqal.Triple triple) throws SparqlError {
+		string subject;
+		if (triple.subject.type == Rasqal.Literal.Type.VARIABLE) {
+			subject = "?" + triple.subject.as_variable ().name;
+		} else {
+			subject = get_string_from_literal (triple.subject, true);
+		}
+
+		string db_table;
+		bool rdftype = false;
+		bool share_table = true;
+
+		bool newtable;
+		DataTable table;
+		Property prop = null;
+
+		if (triple.predicate.type == Rasqal.Literal.Type.URI) {
+			prop = Ontology.get_property_by_uri (triple.predicate.as_string ());
+
+			if (triple.predicate.as_string () == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
+			    && triple.object.type == Rasqal.Literal.Type.URI) {
+				// rdf:type query
+				rdftype = true;
+				var cl = Ontology.get_class_by_uri (triple.object.as_string ());
+				if (cl == null) {
+					throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (triple.object.as_string ()));
+				}
+				db_table = cl.name;
+			} else if (prop == null) {
+				if (triple.predicate.as_string () == "http://www.tracker-project.org/ontologies/fts#match";) {
+					// fts:match
+					db_table = "rdfs:Resource";
+				} else {
+					throw new SparqlError.UNKNOWN_PROPERTY ("Unknown property `%s'".printf (triple.predicate.as_string ()));
+				}
+			} else {
+				if (triple.predicate.as_string () == "http://www.w3.org/2000/01/rdf-schema#domain";
+				    && triple.subject.type == Rasqal.Literal.Type.VARIABLE
+				    && triple.object.type == Rasqal.Literal.Type.URI) {
+					// rdfs:domain
+					var domain = Ontology.get_class_by_uri (triple.object.as_string ());
+					if (domain == null) {
+						throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (triple.object.as_string ()));
+					}
+					var pv = predicate_variable_map.lookup (triple.subject.as_variable ().name);
+					if (pv == null) {
+						pv = new PredicateVariable ();
+						predicate_variable_map.insert (triple.subject.as_variable ().name, pv);
+					}
+					pv.domain = domain;
+				}
+
+				if (prop.multiple_values) {
+					db_table = "%s_%s".printf (prop.domain.name, prop.name);
+					// we can never share the table with multiple triples
+					// for multi value properties as a property may consist of multiple rows
+					share_table = false;
+				} else {
+					db_table = prop.domain.name;
+				}
+			}
+			table = get_table (subject, db_table, share_table, out newtable);
+		} else {
+			// variable in predicate
+			newtable = true;
+			table = new DataTable ();
+			table.predicate_variable = predicate_variable_map.lookup (triple.predicate.as_variable ().name);
+			if (table.predicate_variable == null) {
+				table.predicate_variable = new PredicateVariable ();
+				predicate_variable_map.insert (triple.predicate.as_variable ().name, table.predicate_variable);
+			}
+			if (triple.subject.type == Rasqal.Literal.Type.URI) {
+				// single subject
+				table.predicate_variable.subject = subject;
+			}
+			if (triple.object.type == Rasqal.Literal.Type.URI) {
+				// single object
+				table.predicate_variable.object = get_string_from_literal (triple.object);
+			}
+			table.sql_query_tablename = triple.predicate.as_variable ().name + (++counter).to_string ();
+			tables.append (table);
+
+			// add to variable list
+			var binding = new VariableBinding ();
+			binding.is_uri = true;
+			binding.variable = triple.predicate.as_variable ().name;
+			binding.table = table;
+			binding.sql_db_column_name = "predicate";
+			var binding_list = pattern_var_map.lookup (binding.variable);
+			if (binding_list == null) {
+				binding_list = new VariableBindingList ();
+				pattern_variables.append (binding.variable);
+				pattern_var_map.insert (binding.variable, binding_list);
+
+				pattern_sql.append_printf ("\"%s\".\"%s\" AS \"%s\", ",
+					binding.table.sql_query_tablename,
+					binding.sql_db_column_name,
+					binding.variable);
+			}
+			binding_list.list.append (binding);
+			if (var_map.lookup (binding.variable) == null) {
+				var_map.insert (binding.variable, binding);
+			}
+		}
+		
+		if (newtable) {
+			if (triple.subject.type == Rasqal.Literal.Type.VARIABLE) {
+				var binding = new VariableBinding ();
+				binding.is_uri = true;
+				binding.variable = triple.subject.as_variable ().name;
+				binding.table = table;
+				binding.sql_db_column_name = "ID";
+				var binding_list = pattern_var_map.lookup (binding.variable);
+				if (binding_list == null) {
+					binding_list = new VariableBindingList ();
+					pattern_variables.append (binding.variable);
+					pattern_var_map.insert (binding.variable, binding_list);
+
+					pattern_sql.append_printf ("\"%s\".\"%s\" AS \"%s\", ",
+						binding.table.sql_query_tablename,
+						binding.sql_db_column_name,
+						binding.variable);
+				}
+				binding_list.list.append (binding);
+				if (var_map.lookup (binding.variable) == null) {
+					var_map.insert (binding.variable, binding);
+				}
+			} else {
+				var binding = new LiteralBinding ();
+				binding.is_uri = true;
+				binding.literal = get_string_from_literal (triple.subject);
+				binding.literal_type = triple.subject.type;
+				binding.table = table;
+				binding.sql_db_column_name = "ID";
+				pattern_bindings.append (binding);
+				bindings.append (binding);
+			}
+		}
+		
+		if (!rdftype) {
+			if (triple.object.type == Rasqal.Literal.Type.VARIABLE) {
+				var binding = new VariableBinding ();
+				binding.variable = triple.object.as_variable ().name;
+				binding.table = table;
+				if (prop != null) {
+					if (prop.data_type == PropertyType.RESOURCE) {
+						binding.is_uri = true;
+					} else if (prop.data_type == PropertyType.BOOLEAN) {
+						binding.is_boolean = true;
+					} else if (prop.data_type == PropertyType.DATE) {
+						binding.is_datetime = true;
+					} else if (prop.data_type == PropertyType.DATETIME) {
+						binding.is_datetime = true;
+					}
+					binding.sql_db_column_name = prop.name;
+					if (!prop.multiple_values) {
+						// for single value properties, row may have NULL
+						// in any column except the ID column
+						binding.maybe_null = true;
+					}
+				} else {
+					// variable as predicate
+					binding.sql_db_column_name = "object";
+					binding.maybe_null = true;
+				}
+
+				var binding_list = pattern_var_map.lookup (binding.variable);
+				if (binding_list == null) {
+					binding_list = new VariableBindingList ();
+					pattern_variables.append (binding.variable);
+					pattern_var_map.insert (binding.variable, binding_list);
+
+					pattern_sql.append_printf ("\"%s\".\"%s\" AS %s, ",
+						binding.table.sql_query_tablename,
+						binding.sql_db_column_name,
+						binding.variable);
+				}
+				binding_list.list.append (binding);
+				if (var_map.lookup (binding.variable) == null) {
+					var_map.insert (binding.variable, binding);
+				}
+			} else if (triple.predicate.as_string () == "http://www.tracker-project.org/ontologies/fts#match";) {
+				var binding = new LiteralBinding ();
+				binding.is_fts_match = true;
+				binding.literal = triple.object.as_string ();
+				binding.literal_type = triple.object.type;
+				binding.table = table;
+				binding.sql_db_column_name = "ID";
+				pattern_bindings.append (binding);
+			} else {
+				var binding = new LiteralBinding ();
+				binding.literal = triple.object.as_string ();
+				binding.literal_type = triple.object.type;
+				binding.table = table;
+				if (prop != null) {
+					if (prop.data_type == PropertyType.RESOURCE) {
+						binding.is_uri = true;
+						binding.literal = get_string_from_literal (triple.object);
+					} else if (prop.data_type == PropertyType.BOOLEAN) {
+						binding.is_boolean = true;
+					} else if (prop.data_type == PropertyType.DATE) {
+						binding.is_datetime = true;
+					} else if (prop.data_type == PropertyType.DATETIME) {
+						binding.is_datetime = true;
+					}
+					binding.sql_db_column_name = prop.name;
+				} else {
+					// variable as predicate
+					binding.sql_db_column_name = "object";
+				}
+				pattern_bindings.append (binding);
+				bindings.append (binding);
+			}
+		}
+	}
+
+	DataTable get_table (string subject, string db_table, bool share_table, out bool newtable) {
+		string tablestring = "%s.%s".printf (subject, db_table);
+		DataTable table = null;
+		newtable = false;
+		if (share_table) {
+			table = table_map.lookup (tablestring);
+		}
+		if (table == null) {
+			newtable = true;
+			table = new DataTable ();
+			table.sql_db_tablename = db_table;
+			table.sql_query_tablename = db_table + (++counter).to_string ();
+			tables.append (table);
+			table_map.insert (tablestring, table);
+		}
+		return table;
+	}
+
+	string sql_operator (Rasqal.Op op) {
+		switch (op) {
+		case Rasqal.Op.AND: return " AND ";
+		case Rasqal.Op.OR: return " OR ";
+		case Rasqal.Op.EQ: return " = ";
+		case Rasqal.Op.NEQ: return " <> ";
+		case Rasqal.Op.LT: return " < ";
+		case Rasqal.Op.GT: return " > ";
+		case Rasqal.Op.LE: return " <= ";
+		case Rasqal.Op.GE: return " >= ";
+		case Rasqal.Op.PLUS: return " + ";
+		case Rasqal.Op.MINUS: return " - ";
+		case Rasqal.Op.STAR: return " * ";
+		case Rasqal.Op.SLASH: return " / ";
+		case Rasqal.Op.REM: return " % ";
+		case Rasqal.Op.STR_EQ: return " = ";
+		case Rasqal.Op.STR_NEQ: return " <> ";
+		}
+		return "";
+	}
+
+	bool is_datetime_variable (Rasqal.Expression expr) {
+		if (expr.op == Rasqal.Op.LITERAL) {
+			if (expr.literal.type == Rasqal.Literal.Type.VARIABLE) {
+				string variable_name = expr.literal.as_variable ().name;
+				var binding = var_map.lookup (variable_name);
+				if (binding != null && binding.is_datetime) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	void visit_filter (Rasqal.Expression expr, bool is_datetime = false) {
+		switch (expr.op) {
+		case Rasqal.Op.AND:
+		case Rasqal.Op.OR:
+		case Rasqal.Op.EQ:
+		case Rasqal.Op.NEQ:
+		case Rasqal.Op.LT:
+		case Rasqal.Op.GT:
+		case Rasqal.Op.LE:
+		case Rasqal.Op.GE:
+		case Rasqal.Op.PLUS:
+		case Rasqal.Op.MINUS:
+		case Rasqal.Op.STAR:
+		case Rasqal.Op.SLASH:
+		case Rasqal.Op.REM:
+		case Rasqal.Op.STR_EQ:
+		case Rasqal.Op.STR_NEQ:
+			pattern_sql.append ("(");
+			visit_filter (expr.arg1, is_datetime_variable (expr.arg2));
+			pattern_sql.append (sql_operator (expr.op));
+			visit_filter (expr.arg2, is_datetime_variable (expr.arg1));
+			pattern_sql.append (")");
+			break;
+		case Rasqal.Op.UMINUS:
+			pattern_sql.append ("-(");
+			visit_filter (expr.arg1);
+			pattern_sql.append (")");
+			break;
+		case Rasqal.Op.BANG:
+			pattern_sql.append ("NOT (");
+			visit_filter (expr.arg1);
+			pattern_sql.append (")");
+			break;
+		case Rasqal.Op.LITERAL:
+			if (expr.literal.type == Rasqal.Literal.Type.VARIABLE) {
+				string variable_name = expr.literal.as_variable ().name;
+				pattern_sql.append (variable_name);
+			} else {
+				if (expr.literal.type == Rasqal.Literal.Type.URI) {
+					pattern_sql.append ("(SELECT ID FROM \"rdfs:Resource\" WHERE Uri = ?)");
+				} else {
+					pattern_sql.append ("?");
+				}
+
+				var binding = new LiteralBinding ();
+				binding.literal = expr.literal.as_string ();
+				binding.literal_type = expr.literal.type;
+				binding.is_datetime = is_datetime;
+				bindings.append (binding);
+			}
+			break;
+		case Rasqal.Op.BOUND:
+			pattern_sql.append ("(");
+			visit_filter (expr.arg1);
+			pattern_sql.append (") IS NOT NULL");
+			break;
+		case Rasqal.Op.REGEX:
+			pattern_sql.append ("SparqlRegex(");
+			visit_filter (expr.arg1);
+			pattern_sql.append (", ");
+			visit_filter (expr.arg2);
+			pattern_sql.append (", ");
+			if (expr.arg3 != null) {
+				visit_filter (expr.arg3);
+			} else {
+				pattern_sql.append ("''");
+			}
+			pattern_sql.append (")");
+			break;
+		}
+	}
+
+	static string? get_string_for_value (Value value)
+	{
+		switch (value.type ()) {
+		case typeof (int):
+			return value.get_int ().to_string ();
+		case typeof (double):
+			return value.get_double ().to_string ();
+		case typeof (string):
+			return value.get_string ();
+		default:
+			return null;
+		}
+	}
+
+	[CCode (cname = "uuid_generate")]
+	public extern static void uuid_generate ([CCode (array_length = false)] uchar[] uuid);
+}
+
diff --git a/src/libtracker-db/Makefile.am b/src/libtracker-db/Makefile.am
index a6203bb..4cfb835 100644
--- a/src/libtracker-db/Makefile.am
+++ b/src/libtracker-db/Makefile.am
@@ -47,4 +47,7 @@ libtracker_db_la_LIBADD = 						\
 	$(DBUS_LIBS)							\
 	$(GCOV_LIBS)							\
 	$(GLIB2_LIBS)							\
-	-lz
\ No newline at end of file
+	-lz
+
+EXTRA_DIST = libtracker-db.vapi
+
diff --git a/src/libtracker-db/libtracker-db.vapi b/src/libtracker-db/libtracker-db.vapi
new file mode 100644
index 0000000..8936e0d
--- /dev/null
+++ b/src/libtracker-db/libtracker-db.vapi
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008-2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+namespace Tracker {
+	[CCode (cheader_filename = "libtracker-db/tracker-db-manager.h")]
+	public enum DB {
+		UNKNOWN,
+		COMMON,
+		CACHE,
+		METADATA,
+		CONTENTS
+	}
+
+	[CCode (cheader_filename = "libtracker-db/tracker-db-interface.h")]
+	public interface DBInterface : GLib.Object {
+		[PrintfFormat]
+		public abstract DBStatement create_statement (string query, ...);
+	}
+
+	[CCode (cheader_filename = "libtracker-db/tracker-db-manager.h")]
+	namespace DBManager {
+		public weak DBInterface get_db_interface ();
+	}
+
+	[CCode (cheader_filename = "libtracker-db/tracker-db-interface.h")]
+	public class DBResultSet : GLib.Object {
+		public void _get_value (uint column, out GLib.Value value);
+		public bool iter_next ();
+	}
+
+	[CCode (cheader_filename = "libtracker-db/tracker-db-interface.h")]
+	public interface DBStatement : GLib.Object {
+		public abstract void bind_double (int index, double value);
+		public abstract void bind_int (int index, int value);
+		public abstract void bind_text (int index, string value);
+		public abstract DBResultSet execute () throws GLib.Error;
+	}
+}
+
diff --git a/src/libtracker/tracker.c b/src/libtracker/tracker.c
index 8f8c106..b9750bf 100644
--- a/src/libtracker/tracker.c
+++ b/src/libtracker/tracker.c
@@ -289,6 +289,26 @@ tracker_resources_load (TrackerClient *client, const char *uri, GError **error)
 }
 
 
+GPtrArray *
+tracker_resources_sparql_query (TrackerClient *client, const char *query, GError **error)
+{
+	GPtrArray *table;
+
+	if (!org_freedesktop_Tracker_Resources_sparql_query (client->proxy_resources, query, &table, &*error)) {
+		return NULL;
+	}
+
+	return table;
+}
+
+
+void
+tracker_resources_sparql_update (TrackerClient *client, const char *query, GError **error)
+{
+	org_freedesktop_Tracker_Resources_sparql_update (client->proxy_resources, query, &*error);
+}
+
+
 char *
 tracker_search_get_snippet (TrackerClient *client, const char *uri, const char *search_text, GError **error)
 {
@@ -425,6 +445,34 @@ tracker_resources_load_async (TrackerClient *client, const char *uri, TrackerVoi
 
 
 void
+tracker_resources_sparql_query_async (TrackerClient *client, const char *query, TrackerGPtrArrayReply callback, gpointer user_data)
+{
+	GPtrArrayCallBackStruct *callback_struct;
+
+	callback_struct = g_new (GPtrArrayCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Resources_sparql_query_async (client->proxy_resources, query, tracker_GPtrArray_reply, callback_struct);
+
+}
+
+
+void
+tracker_resources_sparql_update_async (TrackerClient *client, const char *query, TrackerVoidReply callback, gpointer user_data)
+{
+	VoidCallBackStruct *callback_struct;
+
+	callback_struct = g_new (VoidCallBackStruct, 1);
+	callback_struct->callback = callback;
+	callback_struct->data = user_data;
+
+	client->last_pending_call = org_freedesktop_Tracker_Resources_sparql_update_async (client->proxy_resources, query, tracker_void_reply, callback_struct);
+
+}
+
+
+void
 tracker_search_get_snippet_async (TrackerClient *client, const char *uri, const char *search_text, TrackerStringReply callback, gpointer user_data)
 {
 	StringCallBackStruct *callback_struct;
diff --git a/src/libtracker/tracker.h b/src/libtracker/tracker.h
index 56934b3..9e9f0f9 100644
--- a/src/libtracker/tracker.h
+++ b/src/libtracker/tracker.h
@@ -70,6 +70,8 @@ void		tracker_shutdown				(TrackerClient *client, gboolean reindex, GError **err
 void		tracker_prompt_index_signals			(TrackerClient *client, GError **error);
 
 void		tracker_resources_load				(TrackerClient *client, const char *uri, GError **error);
+GPtrArray *	tracker_resources_sparql_query			(TrackerClient *client, const char *query, GError **error);
+void		tracker_resources_sparql_update			(TrackerClient *client, const char *query, GError **error);
 
 
 char *		tracker_search_get_snippet			(TrackerClient *client, const char *uri, const char *search_text, GError **error);
@@ -89,6 +91,8 @@ void		tracker_shutdown_async					(TrackerClient *client, gboolean reindex, Track
 void		tracker_prompt_index_signals_async			(TrackerClient *client, TrackerVoidReply callback, gpointer user_data);
 
 void		tracker_resources_load_async				(TrackerClient *client, const char *uri, TrackerVoidReply callback, gpointer user_data);
+void		tracker_resources_sparql_query_async			(TrackerClient *client, const char *query, TrackerGPtrArrayReply callback, gpointer user_data);
+void		tracker_resources_sparql_update_async			(TrackerClient *client, const char *query, TrackerVoidReply callback, gpointer user_data);
 
 void		tracker_search_get_snippet_async			(TrackerClient *client, const char *uri, const char *search_text, TrackerStringReply callback, gpointer user_data);
 void		tracker_search_suggest_async				(TrackerClient *client, const char *search_text, int maxdist, TrackerStringReply callback, gpointer user_data);
diff --git a/src/rasqal/rasqal.vapi b/src/rasqal/rasqal.vapi
new file mode 100644
index 0000000..b9d5350
--- /dev/null
+++ b/src/rasqal/rasqal.vapi
@@ -0,0 +1,210 @@
+/* rasqal.vapi
+ *
+ * Copyright (C) 2008-2009  Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ * 	Jürg Billeter <j bitron ch>
+ */
+
+[CCode (cheader_filename = "raptor.h")]
+namespace Raptor {
+	[Compact]
+	[CCode (cname = "raptor_locator")]
+	public class Locator {
+		public weak string file;
+		public int line;
+		public int column;
+		public int byte;
+	}
+
+	[CCode (cname = "raptor_message_handler", instance_pos = 0)]
+	public delegate void MessageHandler (Locator locator, string message);
+}
+
+[CCode (cheader_filename = "rasqal.h")]
+namespace Rasqal {
+	[Compact]
+	[CCode (cname = "rasqal_graph_pattern")]
+	public class GraphPattern {
+		public enum Operator {
+			BASIC,
+			OPTIONAL,
+			UNION,
+			GROUP,
+			GRAPH,
+			FILTER
+		}
+
+		public weak Expression get_filter_expression ();
+		public Operator get_operator ();
+		public weak GraphPattern get_sub_graph_pattern (int idx);
+		public weak Triple get_triple (int idx);
+		public void print (GLib.FileStream fh);
+	}
+
+	[CCode (cname = "rasqal_op", cprefix = "RASQAL_EXPR_")]
+	public enum Op {
+		AND,
+		OR,
+		EQ,
+		NEQ,
+		LT,
+		GT,
+		LE,
+		GE,
+		UMINUS,
+		PLUS,
+		MINUS,
+		STAR,
+		SLASH,
+		REM,
+		STR_EQ,
+		STR_NEQ,
+		STR_MATCH,
+		STR_NMATCH,
+		TILDE,
+		BANG,
+		LITERAL,
+		FUNCTION,
+		BOUND,
+		STR,
+		LANG,
+		DATATYPE,
+		ISURI,
+		ISBLANK,
+		ISLITERAL,
+		CAST,
+		ORDER_COND_ASC,
+		ORDER_COND_DESC,
+		LANGMATCHES,
+		REGEX,
+		GROUP_COND_ASC,
+		GROUP_COND_DESC,
+		COUNT,
+		VARSTAR,
+		SAMETERM,
+		SUM,
+		AVG,
+		MIN,
+		MAX
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_expression", free_function = "rasqal_free_expression")]
+	public class Expression {
+		public Op op;
+		public Expression? arg1;
+		public Expression? arg2;
+		public Expression? arg3;
+		public Literal? literal;
+	}
+
+	[CCode (cname = "rasqal_generate_bnodeid_handler", instance_pos = 1.1)]
+	public delegate string GenerateBnodeidHandler (Rasqal.Query query, string? user_bnodeid);
+
+	[Compact]
+	[CCode (cname = "rasqal_literal", free_function = "rasqal_free_literal")]
+	public class Literal {
+		[CCode (cname = "rasqal_literal_type", cprefix = "RASQAL_LITERAL_")]
+		public enum Type {
+			BLANK,
+			URI,
+			STRING,
+			BOOLEAN,
+			INTEGER,
+			DOUBLE,
+			FLOAT,
+			DECIMAL,
+			DATETIME,
+			PATTERN,
+			QNAME,
+			VARIABLE
+		}
+
+		public Type type;
+
+		public weak string? as_string ();
+		public weak Variable? as_variable ();
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_prefix", free_function = "rasqal_free_prefix")]
+	public class Prefix {
+		[CCode (cname = "rasqal_new_prefix")]
+		public Prefix (World world, string# prefix, string# uri);
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_query", free_function = "rasqal_free_query")]
+	public class Query {
+		[CCode (cname = "rasqal_new_query")]
+		public Query (World world, string? name, string? uri);
+		public void add_prefix (Prefix# prefix);
+		public void declare_prefixes ();
+		public weak Triple get_construct_triple (int idx);
+		public bool get_distinct ();
+		public int get_limit ();
+		public int get_offset ();
+		public QueryVerb get_verb ();
+		public weak Expression? get_group_condition (int idx);
+		public weak Expression? get_order_condition (int idx);
+		public weak GraphPattern get_query_graph_pattern ();
+		public weak Variable? get_variable (int idx);
+		public int prepare (string? query_string, string? base_uri);
+		public void print (GLib.FileStream fh);
+		public void set_error_handler ([CCode (delegate_target_pos = 0.9)] Raptor.MessageHandler handler);
+		public void set_fatal_error_handler ([CCode (delegate_target_pos = 0.9)] Raptor.MessageHandler handler);
+		public void set_generate_bnodeid_handler ([CCode (delegate_target_pos = 0.9)] GenerateBnodeidHandler handler);
+		public void set_warning_handler ([CCode (delegate_target_pos = 0.9)] Raptor.MessageHandler handler);
+	}
+
+	public enum QueryVerb {
+		SELECT,
+		CONSTRUCT,
+		DESCRIBE,
+		ASK,
+		DELETE,
+		INSERT
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_triple", free_function = "rasqal_free_triple")]
+	public class Triple {
+		public Literal subject;
+		public Literal predicate;
+		public Literal object;
+		public Literal origin;
+
+		public void print (GLib.FileStream fh);
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_variable", free_function = "rasqal_free_variable")]
+	public class Variable {
+		public weak string? name;
+		public Expression? expression;
+	}
+
+	[Compact]
+	[CCode (cname = "rasqal_world", free_function = "rasqal_free_world")]
+	public class World {
+		[CCode (cname = "rasqal_new_world")]
+		public World ();
+		public void open ();
+	}
+}
+
diff --git a/src/tracker-indexer/tracker-indexer.c b/src/tracker-indexer/tracker-indexer.c
index 1e54281..5153325 100644
--- a/src/tracker-indexer/tracker-indexer.c
+++ b/src/tracker-indexer/tracker-indexer.c
@@ -2492,6 +2492,42 @@ tracker_indexer_delete_statement (TrackerIndexer	     *indexer,
 	tracker_dbus_request_success (request_id);
 }
 
+void
+tracker_indexer_sparql_update (TrackerIndexer	         *indexer,
+			       const gchar	         *update,
+			       DBusGMethodInvocation	 *context,
+			       GError			**error)
+{
+	GError 		     *actual_error = NULL;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (update != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request for SPARQL Update, "
+				  "update:'%s'",
+				  update);
+
+	schedule_flush (indexer, TRUE);
+
+	tracker_data_update_sparql (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);
+
+	tracker_dbus_request_success (request_id);
+}
+
 static void
 restore_backup_cb (const gchar *subject,
 		   const gchar *predicate,
diff --git a/src/tracker-indexer/tracker-indexer.h b/src/tracker-indexer/tracker-indexer.h
index 8c78293..11740b2 100644
--- a/src/tracker-indexer/tracker-indexer.h
+++ b/src/tracker-indexer/tracker-indexer.h
@@ -149,6 +149,10 @@ void            tracker_indexer_delete_statement    (TrackerIndexer         *ind
 						     const gchar            *object,
 						     DBusGMethodInvocation  *context,
 						     GError                **error);
+void		tracker_indexer_sparql_update	    (TrackerIndexer         *indexer,
+						     const gchar	    *update,
+						     DBusGMethodInvocation  *context,
+						     GError		   **error);
 void            tracker_indexer_restore_backup      (TrackerIndexer         *indexer,
 						     const gchar            *backup_file,
 						     DBusGMethodInvocation  *context,
diff --git a/src/tracker-utils/.gitignore b/src/tracker-utils/.gitignore
index 3cf2682..81de0cf 100644
--- a/src/tracker-utils/.gitignore
+++ b/src/tracker-utils/.gitignore
@@ -5,6 +5,7 @@ tracker-processes
 tracker-query
 tracker-search
 tracker-services
+tracker-sparql
 tracker-stats
 tracker-status
 tracker-tag
diff --git a/src/tracker-utils/Makefile.am b/src/tracker-utils/Makefile.am
index b817ac8..b77f8a3 100644
--- a/src/tracker-utils/Makefile.am
+++ b/src/tracker-utils/Makefile.am
@@ -25,7 +25,8 @@ bin_PROGRAMS = 							\
 	tracker-tag						\
 	tracker-status						\
 	tracker-info						\
-	tracker-processes
+	tracker-processes					\
+	tracker-sparql
 
 tracker_search_SOURCES = tracker-search.c
 tracker_search_LDADD = $(libs)
@@ -45,3 +46,6 @@ tracker_info_LDADD = $(libs)
 tracker_processes_SOURCES = tracker-processes.c
 tracker_processes_LDADD = $(libs)
 
+tracker_sparql_SOURCES = tracker-sparql.c
+tracker_sparql_LDADD = $(libs)
+
diff --git a/src/tracker-utils/tracker-info.c b/src/tracker-utils/tracker-info.c
index efdca89..ea57857 100644
--- a/src/tracker-utils/tracker-info.c
+++ b/src/tracker-utils/tracker-info.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
- * Copyright (C) 2008, Nokia
+ * Copyright (C) 2008-2009, Nokia
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -28,75 +28,56 @@
 
 #include <glib.h>
 #include <glib/gi18n.h>
-#include <gio/gio.h>
 
 #include <libtracker/tracker.h>
 #include <libtracker-common/tracker-common.h>
 
-static gchar	     *service;
-static gchar        **metadata;
-static gchar        **uris;
+static gchar        **filenames = NULL;
 
 static GOptionEntry   entries[] = {
-	{ "service", 's', 0, G_OPTION_ARG_STRING, &service,
-	  N_("Service type of the file"),
-	  N_("Files")
-	},
-	{ "metadata", 'm', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_STRING_ARRAY, &metadata,
-	  N_("Metadata to request (optional, multiple calls allowed)"),
-	  N_("File:Size")
-	},
-	{ G_OPTION_REMAINING, 0, G_OPTION_FLAG_FILENAME, G_OPTION_ARG_FILENAME_ARRAY, &uris,
-	  N_("FILE..."),
-	  N_("FILE")
-	},
+	{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames,
+	  N_("FILE"),
+	  N_("FILE")},
 	{ NULL }
 };
 
 static void
-print_property_value (gpointer data,
-		      gpointer user_data)
+print_property_value (gpointer value)
 {
-	GStrv results;
-	GStrv uris_used;
-
-	results = data;
-	uris_used = user_data;
-	
-	if (uris_used) { 
-		static gint uri_id = 0;
-		GStrv p;
-		gint i;
-
-		g_print ("%s\n", uris_used[uri_id]);
-		
-		if (!results) {
-			g_print ("  %s\n", _("No metadata available"));
-			return;
-		}
+	gchar **pair;
 
-		for (p = results, i = 0; *p; p++, i++) {
-			g_print ("  '%s' = '%s'\n", metadata[i], *p);
-		}
+	pair = value;
 
-		uri_id++;
-	} else {
-		g_print ("  '%s' = '%s'\n", results[0], results[1]);
+	g_print ("  '%s' = '%s'\n", pair[0], pair[1]);
+}
+
+static gboolean
+has_valid_uri_scheme (const gchar *uri)
+{
+	const gchar *s;
+
+	s = uri;
+
+	if (!g_ascii_isalpha (*s)) {
+		return FALSE;
 	}
+
+	do {
+		s++;
+	} while (g_ascii_isalnum (*s) || *s == '+' || *s == '.' || *s == '-');
+
+	return (*s == ':');
 }
 
 int
 main (int argc, char **argv)
 {
 	TrackerClient	*client;
-	GFile           *file;
-	gchar           *summary;
-	gchar           *path;
+	gchar           *query;
 	GOptionContext	*context;
 	GError		*error = NULL;
-	guint            count;
-	gint             i;
-	gint             exit_result = EXIT_SUCCESS;
+	GPtrArray	*results;
+	char		*uri;
 
 	setlocale (LC_ALL, "");
 
@@ -110,34 +91,14 @@ main (int argc, char **argv)
 
 	/* Translators: this message will appear after the usage string */
 	/* and before the list of options.				*/
-	summary = g_strconcat (_("For a list of services and metadata that "
-				 "can be used here, see tracker-services."),
-			       NULL);
-
-	g_option_context_set_summary (context, summary);
 	g_option_context_add_main_entries (context, entries, NULL);
 	g_option_context_parse (context, &argc, &argv, NULL);
-	g_free (summary);
 
-	if (!uris) {
+	if (!filenames) {
 		gchar *help;
 
-		g_printerr (_("URI missing"));
-		g_printerr ("\n\n");
-
-		help = g_option_context_get_help (context, TRUE, NULL);
-		g_option_context_free (context);
-		g_printerr ("%s", help);
-		g_free (help);
-
-		return EXIT_FAILURE;
-	}
-
-	if (!metadata && g_strv_length (uris) > 1) {
-		gchar *help;
-
-		g_printerr (_("Requesting ALL information about multiple files is not supported"));
-		g_printerr ("\n\n");
+		g_printerr ("%s\n\n",
+			    _("File missing"));
 
 		help = g_option_context_get_help (context, TRUE, NULL);
 		g_option_context_free (context);
@@ -152,173 +113,61 @@ main (int argc, char **argv)
 	client = tracker_connect (FALSE);
 
 	if (!client) {
-		g_printerr (_("Could not establish a DBus connection to Tracker"));
-		g_printerr ("\n");
-
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
 		return EXIT_FAILURE;
 	}
 
-	/* TODO: Port to SPARQL */
-#if 0
-	if (!service) {
-		g_print (_("Defaulting to 'files' service"));
-		g_print ("\n");
-
-		type = SERVICE_FILES;
+	/* support both, URIs and local file paths */
+	if (has_valid_uri_scheme (filenames[0])) {
+		uri = g_strdup (filenames[0]);
 	} else {
-		type = tracker_class_name_to_type (service);
+		GFile *file;
 
-		if (type == SERVICE_OTHER_FILES && g_ascii_strcasecmp (service, "Other")) {
-			g_printerr (_("Service type not recognized, using 'Other' ..."));
-			g_printerr ("\n");
-		}
+		file = g_file_new_for_commandline_arg (filenames[0]);
+		uri = g_file_get_uri (file);
+		g_object_unref (file);
 	}
 
-	count = g_strv_length (uris);
+	query = g_strdup_printf ("SELECT ?predicate ?object WHERE { <%s> ?predicate ?object }", uri);
 
-	if (count > 1 && metadata != NULL) {
-		gchar     **strv;
-		GPtrArray  *results;
+	results = tracker_resources_sparql_query (client, query, &error);
 
-		strv = g_new (gchar*, count + 1);
+	g_free (uri);
+	g_free (query);
 
-		/* Convert all files to real paths */
-		for (i = 0; i < count; i++) {
-			file = g_file_new_for_commandline_arg (uris[i]);
-			path = g_file_get_path (file);
-			g_object_unref (file);
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Unable to retrieve data for uri"),
+			    error->message);
 		
-			strv[i] = path;
-		}
-
-		strv[i] = NULL;
+		g_error_free (error);
+		tracker_disconnect (client);
 		
-		results = tracker_metadata_get_multiple (client,
-							 type,
-							 (const gchar **) strv,
-							 (const gchar **) metadata,
-							 &error);
-
-		if (error) {
-			g_printerr (tracker_dngettext (NULL,
-						       _("Unable to retrieve data for %d uri"), 
-						       _("Unable to retrieve data for %d uris"), 
-						       count),
-				    count);
-			g_printerr (", %s\n",
-				    error->message);
-			
-			g_error_free (error);
-			
-			exit_result = EXIT_FAILURE;
-		} else if (!results) {
-			g_printerr (tracker_dngettext (NULL,
-						       _("No metadata available for all %d uri"),
-						       _("No metadata available for all %d uris"),
-						       count),
-				    count);
-			g_print ("\n");
-		} else {
-			/* NOTE: This should be the same as count was before */
-			count = g_strv_length ((gchar**) results);
-
-			g_print (tracker_dngettext (NULL,
-						    _("Result:"),
-						    _("Results:"),
-						    count),
-				 count);
-			g_print ("\n");
-			
-			g_ptr_array_foreach (results, print_property_value, strv);
-			g_ptr_array_foreach (results, (GFunc) g_strfreev, NULL);
-			g_ptr_array_free (results, TRUE);
-		}
-
-		g_strfreev (strv);
+		return EXIT_FAILURE;
+	}
+
+	if (!results) {
+		g_print ("%s\n",
+			 _("No metadata available for that uri"));
 	} else {
-		GPtrArray *results;
+		gint length;
 
-		file = g_file_new_for_commandline_arg (uris[0]);
-		path = g_file_get_path (file);
-		
-		if (G_LIKELY (!metadata)) {
-			results = tracker_metadata_get_all (client,
-							    type,
-							    path,
-							    &error);
-
-			if (error) {
-				g_printerr ("%s, %s\n",
-					    _("Unable to retrieve data for uri"),
-					    error->message);
-
-				g_error_free (error);
-				exit_result = EXIT_FAILURE;
-			} else if (!results) {
-				g_print (_("No metadata available for that uri"));
-				g_print ("\n");
-			} else {
-				gint length;
-				
-				length = results->len;
-				
-				g_print (tracker_dngettext (NULL,
-							    _("Result: %d for '%s'"), 
-							    _("Results: %d for '%s'"),
-							    length),
-					 length,
-					 path);
-				g_print ("\n");
-				
-				g_ptr_array_foreach (results, print_property_value, NULL);
-				g_ptr_array_foreach (results, (GFunc) g_strfreev, NULL);
-				g_ptr_array_free (results, TRUE);
-			}		
-		} else {
-			GStrv results;
-
-			results = tracker_metadata_get (client,
-							type,
-							path,
-							(const gchar **) metadata,
-							&error);
-			if (error) {
-				g_printerr ("%s, %s\n",
-					    _("Unable to retrieve data for uri"),
-					    error->message);
-
-				g_error_free (error);
-				exit_result = EXIT_FAILURE;
-			} else if (!results) {
-				g_print (_("No metadata available for that uri"));
-				g_print ("\n");
-			} else {
-				gint i;
-				
-				count = g_strv_length (results);
-				
-				g_print (tracker_dngettext (NULL,
-							    _("Result:"),
-							    _("Results:"),
-							    count),
-					 count);
-				g_print ("\n");
-
-				for (i = 0; i < count; i++) {
-					g_print ("  '%s' = '%s'\n",
-						 metadata[i], results[i]);
-				}
-				
-				g_strfreev (results);
-			}		
-		}
+		length = results->len;
 
-		g_object_unref (file);
-		g_free (path);
+		g_print (tracker_dngettext (NULL,
+					    _("Result: %d"), 
+					    _("Results: %d"),
+					    length),
+			 length);
+		g_print ("\n");
+		
+		g_ptr_array_foreach (results, (GFunc) print_property_value, NULL);
+		g_ptr_array_foreach (results, (GFunc) g_strfreev, NULL);
+		g_ptr_array_free (results, TRUE);
 	}
-#endif
 
 	tracker_disconnect (client);
 
-	return exit_result;
+	return EXIT_SUCCESS;
 }
diff --git a/src/tracker-utils/tracker-search.c b/src/tracker-utils/tracker-search.c
index 2dacd71..5580805 100644
--- a/src/tracker-utils/tracker-search.c
+++ b/src/tracker-utils/tracker-search.c
@@ -64,46 +64,35 @@ static GOptionEntry   entries[] = {
 };
 
 static void
-get_meta_table_data (gpointer value)
+get_meta_table_data (gpointer value, gpointer user_data)
 {
+	gboolean detailed = GPOINTER_TO_INT (user_data);
 	gchar **meta;
 	gchar **p;
-	gchar  *str;
 	gint	i;
 
 	meta = value;
 
 	for (p = meta, i = 0; *p; p++, i++) {
-		switch (i) {
-		case 0:
-			str = g_filename_from_utf8 (*p, -1, NULL, NULL, NULL);
-			g_print ("  %s:'%s'", _("Path"), str);
-			g_free (str);
-			break;
-		case 1:
-			g_print (", %s:'%s'", _("Service"), *p);
-			break;
-		case 2:
-			g_print (", %s:'%s'", _("MIME-type"), *p);
-			break;
-		default:
-			break;
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else if (detailed) {
+			g_print (", %s", *p);
 		}
 	}
 
 	g_print ("\n");
 }
 
+
 int
 main (int argc, char **argv)
 {
 	TrackerClient	*client;
 	GOptionContext	*context;
 	GError		*error = NULL;
-	gchar		*search;
+	gchar		*search, *temp, *query;
 	gchar		*summary;
-	gchar	       **strv;
-	gchar	       **p;
 	GPtrArray	*array;
 
 	setlocale (LC_ALL, "");
@@ -175,129 +164,49 @@ main (int argc, char **argv)
 		limit = 512;
 	}
 
-	/* TODO: Port to SPARQL */
-#if 0
-	if (!service) {
-		g_print ("%s\n",
-			 _("Defaulting to 'files' service"));
+	temp = g_strjoinv (" ", terms);
+	search = g_strdup (temp); /* replace with escape function */
+	g_free (temp);
 
-		type = SERVICE_FILES;
+	if (detailed) {
+		query = g_strdup_printf ("SELECT ?s ?type ?mimeType WHERE { ?s fts:match \"%s\" ; rdf:type ?type . "
+					 "OPTIONAL { ?s nie:mimeType ?mimeType } } OFFSET %d LIMIT %d",
+					 search, offset, limit);
 	} else {
-		type = tracker_class_name_to_type (service);
-
-		if (type == SERVICE_OTHER_FILES && g_ascii_strcasecmp (service, "Other")) {
-			g_printerr ("%s\n",
-				    _("Service not recognized, searching in other files..."));
-		}
+		query = g_strdup_printf ("SELECT ?s WHERE { ?s fts:match \"%s\" } OFFSET %d LIMIT %d",
+					 search, offset, limit);
 	}
 
-	search = g_strjoinv (" ", terms);
+	array = tracker_resources_sparql_query (client, query, &error);
 
-	if (detailed) {
-		array = tracker_search_text_detailed (client,
-						      time (NULL),
-						      type,
-						      search,
-						      offset,
-						      limit,
-						      &error);
-		g_free (search);
-
-		if (error) {
-			g_printerr ("%s, %s\n",
-				    _("Could not get find detailed results by text"),
-				    error->message);
-
-			g_error_free (error);
-			tracker_disconnect (client);
-
-			return EXIT_FAILURE;
-		}
+	g_free (search);
 
-		if (!array) {
-			g_print ("%s\n",
-				 _("No results found matching your query"));
-		} else {
-			g_print (tracker_dngettext (NULL,
-						    _("Result: %d"), 
-						    _("Results: %d"),
-						    array->len),
-				 array->len);
-			g_print ("\n");
-
-			g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
-			g_ptr_array_free (array, TRUE);
-		}
-	} else {
-		strv = tracker_search_text (client,
-					    time (NULL),
-					    type,
-					    search,
-					    offset,
-					    limit,
-					    &error);
-		g_free (search);
-
-		if (error) {
-			g_printerr ("%s, %s\n",
-				    _("Could not get find results by text"),
-				    error->message);
-
-			g_error_free (error);
-			tracker_disconnect (client);
-
-			return EXIT_FAILURE;
-		}
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not get find detailed results by text"),
+			    error->message);
 
-		if (!strv) {
-			g_print ("%s\n",
-				 _("No results found matching your query"));
-		} else {
-			gint length;
-
-			length = g_strv_length (strv);
-			
-			g_print (tracker_dngettext (NULL,
-						    _("Result: %d"), 
-						    _("Results: %d"),
-						    length),
-				 length);
-			g_print ("\n");
-
-			for (p = strv; *p; p++) {
-				gchar *s;
-
-				s = g_locale_from_utf8 (*p, -1, NULL, NULL, NULL);
-
-				if (!s) {
-					continue;
-				}
-
-				g_print ("  %s\n", s);
-				g_free (s);
-			}
-
-
-			if (length >= limit) {
-				/* Display '...' so the user thinks there is
-				 * more items.
-				 */
-				g_print ("  ...\n");
-				
-				/* Display warning so the user knows this is
-				 * not the WHOLE data set.
-				 */
-				g_printerr ("\n"
-					    "%s\n",
-					    _("NOTE: Limit was reached, there are more items in the database not listed here"));
-			}
-			
-			g_free (strv);
-		}
+		g_error_free (error);
+		tracker_disconnect (client);
+
+		return EXIT_FAILURE;
 	}
-#endif
 
-	tracker_disconnect (client);
+	if (!array) {
+		g_print ("%s\n",
+			 _("No results found matching your query"));
+	} else {
+		g_print (tracker_dngettext (NULL,
+					    _("Result: %d"), 
+					    _("Results: %d"),
+					    array->len),
+			 array->len);
+		g_print ("\n");
+			g_ptr_array_foreach (array, get_meta_table_data, 
+					     GINT_TO_POINTER (detailed));
+		g_ptr_array_free (array, TRUE);
+	}
 
+	tracker_disconnect (client);
 	return EXIT_SUCCESS;
 }
diff --git a/src/tracker-utils/tracker-sparql.c b/src/tracker-utils/tracker-sparql.c
new file mode 100644
index 0000000..a3c6214
--- /dev/null
+++ b/src/tracker-utils/tracker-sparql.c
@@ -0,0 +1,181 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc gnome org)
+ * Copyright (C) 2008, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <libtracker/tracker.h>
+
+#ifdef G_OS_WIN32
+#include <trackerd/mingw-compat.h>
+#endif /* G_OS_WIN32 */
+
+static gchar	     *path;
+static gchar	     *query;
+static gboolean	      update;
+
+static GOptionEntry   entries[] = {
+	{ "path", 'p', 0, G_OPTION_ARG_FILENAME, &path,
+	  N_("Path to use in query"),
+	  NULL,
+	},
+	{ "query", 'q', 0, G_OPTION_ARG_STRING, &query,
+	  N_("SPARQL query"),
+	  NULL
+	},
+	{ "update", 'u', 0, G_OPTION_ARG_NONE, &update,
+	  N_("SPARQL update extensions"),
+	  NULL
+	},
+	{ NULL }
+};
+
+static void
+get_meta_table_data (gpointer value)
+{
+	gchar **meta;
+	gchar **p;
+	gint	i;
+
+	meta = value;
+
+	for (p = meta, i = 0; *p; p++, i++) {
+		if (i == 0) {
+			g_print ("  %s", *p);
+		} else {
+			g_print (", %s", *p);
+		}
+	}
+
+	g_print ("\n");
+}
+
+int
+main (int argc, char **argv)
+{
+	TrackerClient	*client;
+	GOptionContext	*context;
+	GError		*error = NULL;
+	gchar		*path_in_utf8;
+	gsize		 size;
+	GPtrArray	*array;
+
+	setlocale (LC_ALL, "");
+
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	context = g_option_context_new (_("- Query using SPARQL"));
+
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_option_context_parse (context, &argc, &argv, NULL);
+
+	if ((!path && !query)
+	    || (path && query)) {
+		gchar *help;
+
+		g_printerr ("%s\n\n",
+			    _("Either path or query needs to be specified"));
+
+		help = g_option_context_get_help (context, TRUE, NULL);
+		g_option_context_free (context);
+		g_printerr ("%s", help);
+		g_free (help);
+
+		return EXIT_FAILURE;
+	}
+
+	g_option_context_free (context);
+
+	client = tracker_connect (FALSE);
+
+	if (!client) {
+		g_printerr ("%s\n",
+			    _("Could not establish a DBus connection to Tracker"));
+		return EXIT_FAILURE;
+	}
+
+	if (path) {
+		path_in_utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, &error);
+		if (error) {
+			g_printerr ("%s:'%s', %s\n",
+				    _("Could not get UTF-8 path from path"),
+				    path,
+				    error->message);
+			g_error_free (error);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		g_file_get_contents (path_in_utf8, &query, &size, &error);
+		if (error) {
+			g_printerr ("%s:'%s', %s\n",
+				    _("Could not read file"),
+				    path_in_utf8,
+				    error->message);
+			g_error_free (error);
+			g_free (path_in_utf8);
+			tracker_disconnect (client);
+
+			return EXIT_FAILURE;
+		}
+
+		g_free (path_in_utf8);
+	}
+
+	if (!update) {
+		array = tracker_resources_sparql_query (client, query, &error);
+	} else {
+		tracker_resources_sparql_update (client, query, &error);
+	}
+
+	if (error) {
+		g_printerr ("%s, %s\n",
+			    _("Could not query search"),
+			    error->message);
+		g_error_free (error);
+
+		return EXIT_FAILURE;
+	}
+
+	if (!update) {
+		if (!array) {
+			g_print ("%s\n",
+				 _("No results found matching your query"));
+		} else {
+			g_ptr_array_foreach (array, (GFunc) get_meta_table_data, NULL);
+			g_ptr_array_free (array, TRUE);
+		}
+	}
+
+	tracker_disconnect (client);
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/trackerd/tracker-resources.c b/src/trackerd/tracker-resources.c
index c066564..a822cd5 100644
--- a/src/trackerd/tracker-resources.c
+++ b/src/trackerd/tracker-resources.c
@@ -181,3 +181,83 @@ tracker_resources_load (TrackerResources	 *object,
 	tracker_dbus_request_success (request_id);
 }
 
+void
+tracker_resources_sparql_query (TrackerResources	 *self,
+				const gchar	         *query,
+				DBusGMethodInvocation	 *context,
+				GError			**error)
+{
+	TrackerDBResultSet   *result_set;
+	GError 		     *actual_error = NULL;
+	guint		      request_id;
+	GPtrArray            *values;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (query != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request for SPARQL Query, "
+				  "query:'%s'",
+				  query);
+
+	result_set = tracker_data_query_sparql (query, &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;
+	}
+
+	values = tracker_dbus_query_result_to_ptr_array (result_set);
+
+	dbus_g_method_return (context, values);
+
+	tracker_dbus_results_ptr_array_free (&values);
+
+	if (result_set) {
+		g_object_unref (result_set);
+	}
+
+	tracker_dbus_request_success (request_id);
+}
+
+void
+tracker_resources_sparql_update (TrackerResources	 *self,
+				 const gchar	         *update,
+				 DBusGMethodInvocation	 *context,
+				 GError			**error)
+{
+	GError 		     *actual_error = NULL;
+	guint		      request_id;
+
+	request_id = tracker_dbus_get_next_request_id ();
+
+	tracker_dbus_async_return_if_fail (update != NULL, context);
+
+	tracker_dbus_request_new (request_id,
+				  "DBus request for SPARQL Update, "
+				  "update:'%s'",
+				  update);
+
+	org_freedesktop_Tracker_Indexer_sparql_update (tracker_dbus_indexer_get_proxy (),
+						       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);
+
+	tracker_dbus_request_success (request_id);
+}
+
diff --git a/src/trackerd/tracker-resources.h b/src/trackerd/tracker-resources.h
index 61d0316..a673b62 100644
--- a/src/trackerd/tracker-resources.h
+++ b/src/trackerd/tracker-resources.h
@@ -68,6 +68,14 @@ void		 tracker_resources_load			 (TrackerResources	 *object,
 							  const gchar		 *uri,
 							  DBusGMethodInvocation  *context,
 							  GError		**error);
+void		 tracker_resources_sparql_query		 (TrackerResources       *object,
+							  const gchar		 *query,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
+void		 tracker_resources_sparql_update	 (TrackerResources       *object,
+							  const gchar		 *update,
+							  DBusGMethodInvocation  *context,
+							  GError		**error);
 
 G_END_DECLS
 



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