[tracker/wip/sam/resource: 13/17] libtracker-sparql: Add SPARQL serialisation to tracker-resource



commit 8b57883b1f7ca4646d075d2289e3b9f31eabba94
Author: Sam Thursfield <sam afuera me uk>
Date:   Fri Apr 8 15:49:06 2016 +0100

    libtracker-sparql: Add SPARQL serialisation to tracker-resource

 src/libtracker-sparql/tracker-resource.c |  212 ++++++++++++++++++++++++++++++
 src/libtracker-sparql/tracker-resource.h |    4 +
 2 files changed, 216 insertions(+), 0 deletions(-)
---
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index 0335386..c0cc206 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -520,3 +520,215 @@ tracker_resource_print_jsonld_string (TrackerResource *resource)
        }*/
        return string;
 }
+
+typedef struct {
+       TrackerSparqlBuilder *builder;
+       const char *graph_id;
+       GList *done_list;
+} GenerateSparqlRelationsData;
+
+typedef struct {
+       TrackerSparqlBuilder *builder;
+       GHashTable *overwrite_flags;
+} GenerateSparqlDeletesData;
+
+static void
+generate_sparql_relations_foreach (gpointer key,
+                                   gpointer value_ptr,
+                                   gpointer user_data)
+{
+       const char *property = key;
+       const GValue *value = value_ptr;
+       GenerateSparqlRelationsData *data = user_data;
+       GError *error = NULL;
+
+       if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+               TrackerResource *relation = g_value_get_object (value);
+
+               if (g_list_find (data->done_list, relation) == NULL) {
+                       tracker_resource_generate_sparql_update (relation,
+                                                                data->builder,
+                                                                data->graph_id,
+                                                                &error);
+                       data->done_list = g_list_prepend (data->done_list, relation);
+               }
+       }
+}
+
+static char *
+variable_name_for_property (const char *property) {
+       return g_strcanon (g_strdup (property),
+                          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
+                          '_');
+}
+
+static void
+generate_sparql_deletes_foreach (gpointer key,
+                                 gpointer value_ptr,
+                                 gpointer user_data)
+{
+       const char *property = key;
+       const GValue *value = value_ptr;
+       GenerateSparqlDeletesData *data = user_data;
+
+       /* Whether to generate the DELETE is based on whether set_value was ever
+        * called for this property. That's tracked in a hash table.
+        */
+       if (g_hash_table_lookup (data->overwrite_flags, property)) {
+               char *variable_name = variable_name_for_property (property);
+               tracker_sparql_builder_predicate (data->builder, property);
+               tracker_sparql_builder_object_variable (data->builder, variable_name);
+               g_free (variable_name);
+       }
+}
+
+static void
+append_value_to_sparql_builder (const GValue *value,
+                                TrackerSparqlBuilder *builder)
+{
+       GType type = G_VALUE_TYPE (value);
+       if (type == G_TYPE_BOOLEAN) {
+               tracker_sparql_builder_object_boolean (builder, g_value_get_boolean (value));
+       } else if (type == G_TYPE_DATE || type == G_TYPE_DATE_TIME) {
+               g_warning ("Ignoring date/datetime value, not yet implemented.");
+               /* FIXME: date & datetime ... tricky because we need to get a time_t */
+               tracker_sparql_builder_object (builder, "null");
+       } else if (type == G_TYPE_DOUBLE) {
+               tracker_sparql_builder_object_double (builder, g_value_get_double (value));
+       } else if (type == G_TYPE_FLOAT) {
+               tracker_sparql_builder_object_double (builder, g_value_get_float (value));
+       } else if (type == G_TYPE_CHAR) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_schar (value));
+       } else if (type == G_TYPE_INT) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_int (value));
+       } else if (type == G_TYPE_INT64) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_int64 (value));
+       } else if (type == G_TYPE_LONG) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_long (value));
+       } else if (type == G_TYPE_UCHAR) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_uchar (value));
+       } else if (type == G_TYPE_UINT) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_uint (value));
+       } else if (type == G_TYPE_ULONG) {
+               tracker_sparql_builder_object_int64 (builder, g_value_get_ulong (value));
+       } else if (type == G_TYPE_UINT64) {
+               g_warning ("Cannot serialize uint64 types to SPARQL. Use int64.");
+               tracker_sparql_builder_object (builder, "null");
+       } else if (type == G_TYPE_STRING) {
+               tracker_sparql_builder_object_string (builder, g_value_get_string (value));
+       } else if (type == TRACKER_TYPE_URI) {
+               tracker_sparql_builder_object_iri (builder, g_value_get_string (value));
+       } else if (type == TRACKER_TYPE_RESOURCE) {
+               TrackerResource *relation = TRACKER_RESOURCE (g_value_get_object (value));
+               tracker_sparql_builder_object_iri (builder, tracker_resource_get_identifier (relation));
+       } else {
+               g_warning ("Cannot serialize value of type %s to SPARQL", G_VALUE_TYPE_NAME (value));
+               tracker_sparql_builder_object (builder, "null");
+       }
+}
+
+static void
+generate_sparql_inserts_foreach (gpointer key,
+                                 gpointer value_ptr,
+                                 gpointer user_data)
+{
+       const char *property = key;
+       const GValue *value = value_ptr;
+       TrackerSparqlBuilder *builder = TRACKER_SPARQL_BUILDER (user_data);
+
+       /* FIXME: shouldn't be hardcoding this compact URI... */
+       if (strcmp (property, "rdf:type") == 0) {
+               tracker_sparql_builder_predicate (builder, "a");
+       } else {
+               tracker_sparql_builder_predicate (builder, property);
+       }
+
+       if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY) {
+               g_ptr_array_foreach (g_value_get_boxed (value), (GFunc)append_value_to_sparql_builder, 
builder);
+       } else {
+               append_value_to_sparql_builder (value, builder);
+       }
+}
+
+/**
+ * tracker_resource_generate_sparql_update:
+ * @self: a #TrackerResource
+ * @builder: a #TrackerSparqlBuilder where the result will be returned
+ * @error: address where an error can be returned
+ *
+ * Generates a SPARQL command to update a database with the information
+ * stored in @resource.
+ *
+ * Since: 1.10
+ */
+/* FIXME: cycles between resources will cause this to infinite loop. It should
+ * exit with an error instead, or better yet do the clever trickery in
+ * order to actually insert them. */
+void
+tracker_resource_generate_sparql_update (TrackerResource *resource,
+                                         TrackerSparqlBuilder *builder,
+                                         const char *graph_id,
+                                         GError **error)
+{
+       TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+       GenerateSparqlRelationsData relations_data;
+       GenerateSparqlDeletesData deletes_data;
+
+       if (! priv->identifier) {
+               /* FIXME: use GError? */
+               g_warning ("Main resource must have an identifier.");
+               return;
+       }
+
+       g_return_if_fail (tracker_sparql_builder_get_state (builder) == TRACKER_SPARQL_BUILDER_STATE_UPDATE);
+
+       /* First, forget everything you know */
+       tracker_sparql_builder_delete_open (builder, NULL);
+       tracker_sparql_builder_delete_close (builder);
+       tracker_sparql_builder_where_open (builder);
+       if (graph_id) {
+               tracker_sparql_builder_graph_open (builder, graph_id);
+       }
+
+       tracker_sparql_builder_subject_iri (builder, priv->identifier);
+
+       deletes_data.builder = builder;
+       deletes_data.overwrite_flags = priv->overwrite;
+
+       g_hash_table_foreach (priv->properties, generate_sparql_deletes_foreach, &deletes_data);
+
+       if (graph_id){
+               tracker_sparql_builder_graph_close (builder);
+       }
+       tracker_sparql_builder_where_close (builder);
+
+       /* Now emit any sub-resources. */
+       relations_data.builder = builder;
+       relations_data.graph_id = graph_id;
+       relations_data.done_list = NULL;
+
+       g_hash_table_foreach (priv->properties, generate_sparql_relations_foreach, &relations_data);
+
+       /* Finally insert the rest of the data */
+
+       /* Passing the graph directly to insert_open causes it to generate a
+        * non-standard 'INSERT INTO <graph>' statement, while calling graph_open
+        * separately causes it to generate INSERT { GRAPH { .. } }. See
+        * <https://bugzilla.gnome.org/show_bug.cgi?id=658838>.
+        */
+       tracker_sparql_builder_insert_open (builder, NULL);
+       if (graph_id) {
+               tracker_sparql_builder_graph_open (builder, graph_id);
+       }
+
+       tracker_sparql_builder_subject_iri (builder, priv->identifier);
+
+       g_hash_table_foreach (priv->properties, generate_sparql_inserts_foreach, builder);
+
+       if (graph_id){
+               tracker_sparql_builder_graph_close (builder);
+       }
+       tracker_sparql_builder_insert_close (builder);
+}
+
+/* FIXME: serialize_json should be generate_json for consistency with this */
diff --git a/src/libtracker-sparql/tracker-resource.h b/src/libtracker-sparql/tracker-resource.h
index ff43028..7ced0bd 100644
--- a/src/libtracker-sparql/tracker-resource.h
+++ b/src/libtracker-sparql/tracker-resource.h
@@ -22,6 +22,8 @@
 
 #include <glib-object.h>
 
+#include <libtracker-sparql/tracker-generated.h>
+
 G_BEGIN_DECLS
 
 #define TRACKER_TYPE_RESOURCE tracker_resource_get_type()
@@ -62,6 +64,8 @@ const char *tracker_resource_get_first_uri (TrackerResource *self, const char *p
 const char *tracker_resource_get_identifier (TrackerResource *self);
 gint tracker_resource_identifier_compare_func (TrackerResource *resource, const char *identifier);
 
+void tracker_resource_generate_sparql_update (TrackerResource *self, TrackerSparqlBuilder *builder, const 
char *graph_id, GError **error);
+
 G_END_DECLS
 
 #endif /* __LIBTRACKER_RESOURCE_H__ */


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