summaryrefslogtreecommitdiff
path: root/src/libtracker-sparql/tracker-resource.c
diff options
context:
space:
mode:
authorSam Thursfield <ssssam@gmail.com>2016-03-27 00:15:33 +0000
committerSam Thursfield <sam@afuera.me.uk>2016-07-14 13:20:48 +0100
commit8cc026da8cdebba4d5b17ed23841ec59d79f60e3 (patch)
treeeb5af6ba2150d6dcd86a70582ab736c9eb48b3ce /src/libtracker-sparql/tracker-resource.c
parent032e2a7ed874bacce4ca5f0dd9ddbad6ab8f5f81 (diff)
downloadtracker-8cc026da8cdebba4d5b17ed23841ec59d79f60e3.tar.gz
Use TrackerResource instead of TrackerSparqlBuilder in all extractorswip/sam/resource-rebase-6
For a long time, all the Tracker extractors have manually constructed a SPARQL update command using TrackerSparqlBuilder to represent their output. This commit changes all of them to use the TrackerResource class instead, which makes the code a lot more concise and readable. This introduces some API breaks in the internal libtracker-extract library. This has been a private library since Tracker 0.16 or earlier, so it's fine. If the extractors only output SPARQL then they are only useful to people who are using a SPARQL store. Now we can output a serialization format like Turtle as well. This will hopefully make the extract modules useful outside of Tracker itself. I've tried to preserve the behaviour of the extractors as much as possible, but there are two things that are now handled differently: * nao:Tag resources are given a fixed URI based on the tag label, such as <urn:tag:My_Tag>. Previously they were inserted as blank nodes, so tracker-store would give them unique IDs like <urn:uuid:1234...> * All extractors created nco:Contact resources for content publishers, but previously some would assign fixed URIs based on the name <urn:contact:James%20Joyce>, while others would insert them as blank nodes so they would be assigned unique IDs like <urn:uuid:1234...>. Now, all extractors create nco:Contact resources with fixed URIs based on the content creator's name. https://bugzilla.gnome.org/show_bug.cgi?id=767472
Diffstat (limited to 'src/libtracker-sparql/tracker-resource.c')
-rw-r--r--src/libtracker-sparql/tracker-resource.c451
1 files changed, 238 insertions, 213 deletions
diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c
index c6845c1f0..bf8e66dd3 100644
--- a/src/libtracker-sparql/tracker-resource.c
+++ b/src/libtracker-sparql/tracker-resource.c
@@ -213,7 +213,7 @@ tracker_resource_new (const char *identifier)
{
TrackerResource *resource;
- resource = g_object_new (TRACKER_TYPE_RESOURCE, NULL,
+ resource = g_object_new (TRACKER_TYPE_RESOURCE,
"identifier", identifier,
NULL);
@@ -706,6 +706,12 @@ typedef struct {
void generate_turtle (TrackerResource *resource, GenerateTurtleData *data);
+gboolean
+is_blank_node (const char *uri_or_curie_or_blank)
+{
+ return (strncmp(uri_or_curie_or_blank, "_:", 2) == 0);
+}
+
void
generate_nested_turtle_resource (TrackerResource *resource,
GenerateTurtleData *data)
@@ -749,50 +755,123 @@ generate_turtle_resources_foreach (gpointer key,
}
static void
-generate_turtle_value (const GValue *value,
- GenerateTurtleData *data)
+generate_turtle_uri_value (const char *uri_or_curie_or_blank,
+ GString *string,
+ TrackerNamespaceManager *all_namespaces,
+ TrackerNamespaceManager *our_namespaces)
+{
+ /* The tracker_resource_set_uri() function accepts URIs
+ * (such as http://example.com/) and compact URIs (such as nie:DataObject),
+ * and blank node identifiers (_:0). The tracker_resource_set_identifier()
+ * function works the same.
+ *
+ * We could expand all CURIEs, but the generated Turtle or SPARQL will be
+ * clearer if we leave them be. We still need to attempt to expand them
+ * internally in order to know whether they need <> brackets around them.
+ */
+ if (is_blank_node (uri_or_curie_or_blank)) {
+ g_string_append (string, uri_or_curie_or_blank);
+ } else {
+ char *prefix = g_uri_parse_scheme (uri_or_curie_or_blank);
+
+ if (prefix && tracker_namespace_manager_has_prefix (all_namespaces, prefix)) {
+ /* It's a compact URI and we know the prefix */
+ if (our_namespaces != NULL) {
+ maybe_intern_prefix_of_compact_uri (all_namespaces, our_namespaces, uri_or_curie_or_blank);
+ };
+
+ g_string_append (string, uri_or_curie_or_blank);
+ } else {
+ /* It's a full URI (or something invalid, but we can't really tell that here) */
+ g_string_append_printf (string, "<%s>", uri_or_curie_or_blank);
+ }
+ }
+}
+
+static void
+generate_turtle_value (const GValue *value,
+ GString *string,
+ TrackerNamespaceManager *all_namespaces,
+ TrackerNamespaceManager *our_namespaces)
{
GType type = G_VALUE_TYPE (value);
if (type == TRACKER_TYPE_URI) {
- const char *uri = g_value_get_string (value);
- maybe_intern_prefix_of_compact_uri (data->all_namespaces, data->our_namespaces, uri);
- g_string_append_printf(data->string, "%s", uri);
+ generate_turtle_uri_value (g_value_get_string (value),
+ string,
+ all_namespaces,
+ our_namespaces);
} else if (type == TRACKER_TYPE_RESOURCE) {
TrackerResource *relation = TRACKER_RESOURCE (g_value_get_object (value));
- g_string_append_printf(data->string, "<%s>", tracker_resource_get_identifier (relation));
+ generate_turtle_uri_value (tracker_resource_get_identifier (relation),
+ string,
+ all_namespaces,
+ our_namespaces);
} else if (type == G_TYPE_STRING) {
- g_string_append_printf(data->string, "\"%s\"", g_value_get_string (value));
+ char *escaped = tracker_sparql_escape_string (g_value_get_string (value));
+ g_string_append_printf(string, "\"%s\"", escaped);
+ g_free (escaped);
+ } else if (type == G_TYPE_DATE) {
+ char date_string[256];
+ g_date_strftime (date_string, 256,
+ "\"%Y-%m-%d%z\"^^<http://www.w3.org/2001/XMLSchema#date>",
+ g_value_get_boxed (value));
+ g_string_append (string, date_string);
+ } else if (type == G_TYPE_DATE_TIME) {
+ char *datetime_string;
+ datetime_string = g_date_time_format (g_value_get_boxed (value),
+ "\"%Y-%m-%dT%H:%M:%s%z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>");
+ g_string_append (string, datetime_string);
+ g_free (datetime_string);
+ } else if (type == G_TYPE_DOUBLE || type == G_TYPE_FLOAT) {
+ /* We can't use GValue transformations here; they're locale-dependent. */
+ char buffer[256];
+ g_ascii_dtostr (buffer, 255, g_value_get_double (value));
+ g_string_append (string, buffer);
} else {
GValue str_value = G_VALUE_INIT;
g_value_init (&str_value, G_TYPE_STRING);
if (g_value_transform (value, &str_value)) {
- g_string_append (data->string, g_value_get_string (&str_value));
+ g_string_append (string, g_value_get_string (&str_value));
} else {
- g_warning ("Cannot serialize value of type %s to Turtle", G_VALUE_TYPE_NAME (value));
+ g_warning ("Cannot serialize value of type %s to Turtle/SPARQL",
+ G_VALUE_TYPE_NAME (value));
}
g_value_unset (&str_value);
}
}
void
-generate_turtle_property (const char *property,
- const GValue *value,
- GenerateTurtleData *data)
+generate_turtle_property (const char *property,
+ const GValue *value,
+ GString *string,
+ TrackerNamespaceManager *all_namespaces,
+ TrackerNamespaceManager *our_namespaces)
{
- g_string_append (data->string, property);
- g_string_append (data->string, " ");
+ if (strcmp (property, TRACKER_PREFIX_RDF "type") == 0 || strcmp (property, "rdf:type") == 0) {
+ g_string_append (string, "a");
+ } else {
+ g_string_append (string, property);
+ }
+
+ g_string_append (string, " ");
if (G_VALUE_HOLDS (value, G_TYPE_PTR_ARRAY)) {
int i;
GPtrArray *array = g_value_get_boxed (value);
if (array->len > 0) {
- generate_turtle_value (g_ptr_array_index (array, 0), data);
+ generate_turtle_value (g_ptr_array_index (array, 0),
+ string,
+ all_namespaces,
+ our_namespaces);
for (i = 1; i < array->len; i++) {
- g_string_append (data->string, " , ");
- generate_turtle_value (g_ptr_array_index (array, i), data);
+ g_string_append (string, " , ");
+ generate_turtle_value (g_ptr_array_index (array, i),
+ string,
+ all_namespaces,
+ our_namespaces);
}
}
} else {
- generate_turtle_value (value, data);
+ generate_turtle_value (value, string, all_namespaces, our_namespaces);
}
}
@@ -809,12 +888,14 @@ generate_turtle (TrackerResource *resource,
/* First we recurse to any relations that aren't already in the done list */
g_hash_table_foreach (priv->properties, generate_turtle_resources_foreach, data);
- g_string_append_printf (data->string, "<%s> ", priv->identifier);
+ generate_turtle_uri_value (tracker_resource_get_identifier(resource),
+ data->string, data->all_namespaces, data->our_namespaces);
+ g_string_append (data->string, " ");
g_hash_table_iter_init (&iter, priv->properties);
if (g_hash_table_iter_next (&iter, (gpointer *)&property, (gpointer *)&value))
while (TRUE) {
- generate_turtle_property (property, value, data);
+ generate_turtle_property (property, value, data->string, data->all_namespaces, data->our_namespaces);
maybe_intern_prefix_of_compact_uri (data->all_namespaces, data->our_namespaces, property);
@@ -851,15 +932,22 @@ char *
tracker_resource_print_turtle (TrackerResource *self,
TrackerNamespaceManager *namespaces)
{
+ TrackerResourcePrivate *priv;
GenerateTurtleData context;
char *prefixes;
g_return_val_if_fail (TRACKER_IS_RESOURCE (self), "");
+ priv = GET_PRIVATE (self);
+
if (namespaces == NULL) {
namespaces = tracker_namespace_manager_get_default ();
}
+ if (g_hash_table_size (priv->properties) == 0) {
+ return g_strdup("");
+ }
+
context.all_namespaces = namespaces;
context.our_namespaces = tracker_namespace_manager_new ();
context.string = g_string_new ("");
@@ -883,255 +971,173 @@ tracker_resource_print_turtle (TrackerResource *self,
typedef struct {
TrackerNamespaceManager *namespaces;
- TrackerSparqlBuilder *builder;
+ GString *string;
const char *graph_id;
GList *done_list;
- GHashTable *overwrite_flags;
} GenerateSparqlData;
-void generate_sparql_update (TrackerResource *resource, GenerateSparqlData *data);
+static void generate_sparql_deletes (TrackerResource *resource, GenerateSparqlData *data);
+static void generate_sparql_insert_pattern (TrackerResource *resource, GenerateSparqlData *data);
static void
-generate_sparql_relations_foreach (gpointer key,
- gpointer value_ptr,
- gpointer user_data)
+generate_sparql_relation_deletes_foreach (gpointer key,
+ gpointer value_ptr,
+ gpointer user_data)
{
const char *property = key;
const GValue *value = value_ptr;
GenerateSparqlData *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_custom (data->done_list, relation, (GCompareFunc) tracker_resource_compare) == NULL) {
- generate_sparql_update (relation, data);
+ generate_sparql_deletes (relation, data);
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)
+generate_sparql_relation_inserts_foreach (gpointer key,
+ gpointer value_ptr,
+ gpointer user_data)
{
const char *property = key;
const GValue *value = value_ptr;
GenerateSparqlData *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
-generate_sparql_uri_value (const char *uri_or_curie,
- GenerateSparqlData *data)
-{
- /* The tracker_resource_set_uri() function accepts both URIs
- * (such as http://example.com/) and compact URIs (such as nie:DataObject).
- * We could expand them here, but since the tracker-store can understand them
- * as-is we leave them be and the generated SPARQL is clearer as a result.
- * We still need to attempt to expand them in order to know whether they need
- * <> brackets around them.
- */
- char *prefix = g_uri_parse_scheme (uri_or_curie);
+ if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
+ TrackerResource *relation = g_value_get_object (value);
- if (prefix && tracker_namespace_manager_has_prefix (data->namespaces, prefix)) {
- /* It's a compact URI and we know the prefix */
- tracker_sparql_builder_object (data->builder, uri_or_curie);
- } else {
- /* It's a full URI (or something invalid, but we can't really tell that here) */
- tracker_sparql_builder_object_iri (data->builder, uri_or_curie);
+ if (g_list_find_custom (data->done_list, relation, (GCompareFunc) tracker_resource_compare) == NULL) {
+ generate_sparql_insert_pattern (relation, data);
+ data->done_list = g_list_prepend (data->done_list, relation);
+ }
}
}
-static void
-generate_sparql_value (const GValue *value,
- GenerateSparqlData *data)
-{
- TrackerSparqlBuilder *builder = data->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) {
- /* tracker_sparql_builder_object_date() exists, but it requires a
- * time_t, and GDate and GDateTime don't provide those conveniently.
- */
- char literal[256];
- g_date_strftime (literal, 256,
- "\"%Y-%m-%d%z\"^^<http://www.w3.org/2001/XMLSchema#date>",
- g_value_get_boxed (value));
- tracker_sparql_builder_object (builder, literal);
- } else if (type == G_TYPE_DATE_TIME) {
- char *literal;
- literal = g_date_time_format (g_value_get_boxed (value),
- "\"%Y-%m-%dT%H:%M:%s%z\"^^<http://www.w3.org/2001/XMLSchema#dateTime>");
- tracker_sparql_builder_object (builder, literal);
- g_free (literal);
- } 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) {
- generate_sparql_uri_value (g_value_get_string (value), data);
- } 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 char *
+variable_name_for_property (const char *property) {
+ return g_strcanon (g_strdup (property),
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
+ '_');
}
static void
-generate_sparql_inserts_foreach (gpointer key,
- gpointer value_ptr,
- gpointer user_data)
+generate_sparql_delete_pattern (TrackerResource *resource,
+ GHashTable *overwrite_flags,
+ GenerateSparqlData *data)
{
- const char *property = key;
- const GValue *value = value_ptr;
- GenerateSparqlData *data = user_data;
- char *full_property;
-
- full_property = tracker_namespace_manager_expand_uri (data->namespaces, property);
+ TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+ GHashTableIter iter;
+ const char *property;
+ const GValue *value;
+ gboolean had_property;
- /* The caller should have already set rdf:type */
- if (strcmp (full_property, TRACKER_PREFIX_RDF "type") == 0 || strcmp (property, "rdf:type") == 0) {
- g_free (full_property);
- return;
+ if (data->graph_id) {
+ g_string_append_printf (data->string, "GRAPH <%s> {\n", data->graph_id);
}
- tracker_sparql_builder_predicate (data->builder, property);
+ g_string_append (data->string, " ");
+ generate_turtle_uri_value (priv->identifier, data->string, data->namespaces, NULL);
+ g_string_append (data->string, "\n ");
- g_free (full_property);
+ had_property = FALSE;
+ g_hash_table_iter_init (&iter, priv->properties);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&property, (gpointer *)&value)) {
+ /* Whether to generate the DELETE is based on whether set_value was ever
+ * called for this property. That's tracked in the overwrite_flags hash table.
+ */
+ if (g_hash_table_lookup (overwrite_flags, property)) {
+ if (had_property) {
+ g_string_append (data->string, " ;\n ");
+ }
- if (G_VALUE_TYPE (value) == G_TYPE_PTR_ARRAY) {
- g_ptr_array_foreach (g_value_get_boxed (value), (GFunc)generate_sparql_value, data);
- } else {
- generate_sparql_value (value, data);
+ char *variable_name = variable_name_for_property (property);
+ g_string_append_printf (data->string, " %s ?%s", property, variable_name);
+ g_free (variable_name);
+
+ had_property = TRUE;
+ }
+ }
+
+ if (data->graph_id) {
+ g_string_append (data->string, " }");
}
}
void
-generate_sparql_update (TrackerResource *resource,
- GenerateSparqlData *data)
+generate_sparql_deletes (TrackerResource *resource,
+ GenerateSparqlData *data)
{
TrackerResourcePrivate *priv = GET_PRIVATE (resource);
- TrackerSparqlBuilder *builder = data->builder;
- GValue *type_value;
- if (!priv->identifier) {
- g_warning ("Main resource must have an identifier.");
- return;
- }
-
- g_return_if_fail (tracker_sparql_builder_get_state (builder) == TRACKER_SPARQL_BUILDER_STATE_UPDATE);
-
- /* Delete the existing data. If we don't do this, we may get constraint
- * violations due to trying to add a second value to a single-valued
- * property, and we may get old metadata hanging around.
- *
- * We have to generate a rather awkward query here, like:
+ /* We have to generate a rather awkward query here, like:
*
* DELETE { pattern } WHERE { pattern }
*
* It would be better if we could use "DELETE DATA { pattern }". This is
* allowed in SPARQL update 1.1, but not yet supported by Tracker's store.
*/
- data->overwrite_flags = priv->overwrite;
-
- tracker_sparql_builder_delete_open (builder, NULL);
- if (data->graph_id) {
- tracker_sparql_builder_graph_open (builder, data->graph_id);
- }
- tracker_sparql_builder_subject_iri (builder, priv->identifier);
- g_hash_table_foreach (priv->properties, generate_sparql_deletes_foreach, data);
- if (data->graph_id) {
- tracker_sparql_builder_graph_close (builder);
- }
- tracker_sparql_builder_delete_close (builder);
-
- tracker_sparql_builder_where_open (builder);
- if (data->graph_id) {
- tracker_sparql_builder_graph_open (builder, data->graph_id);
- }
- tracker_sparql_builder_subject_iri (builder, priv->identifier);
- g_hash_table_foreach (priv->properties, generate_sparql_deletes_foreach, data);
- if (data->graph_id) {
- tracker_sparql_builder_graph_close (builder);
+ if (! is_blank_node (priv->identifier)) {
+ if (g_hash_table_size (priv->overwrite) > 0) {
+ g_string_append (data->string, "DELETE {\n");
+ generate_sparql_delete_pattern (resource, priv->overwrite, data);
+ g_string_append (data->string, "\n}\nWHERE {\n");
+ generate_sparql_delete_pattern (resource, priv->overwrite, data);
+ g_string_append (data->string, "\n}\n");
+ }
}
- tracker_sparql_builder_where_close (builder);
/* Now emit any sub-resources. */
- g_hash_table_foreach (priv->properties, generate_sparql_relations_foreach, data);
+ g_hash_table_foreach (priv->properties, generate_sparql_relation_deletes_foreach, data);
+}
- /* Finally insert the rest of the data */
+static void
+generate_sparql_insert_pattern (TrackerResource *resource,
+ GenerateSparqlData *data)
+{
+ TrackerResourcePrivate *priv = GET_PRIVATE (resource);
+ GHashTableIter iter;
+ const char *property;
+ char *full_property;
+ const GValue *value;
+ gboolean had_property = FALSE;
- /* 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 (data->graph_id) {
- tracker_sparql_builder_graph_open (builder, data->graph_id);
- }
+ /* First, emit any sub-resources. */
+ g_hash_table_foreach (priv->properties, generate_sparql_relation_inserts_foreach, data);
- tracker_sparql_builder_subject_iri (builder, priv->identifier);
+ generate_turtle_uri_value (priv->identifier, data->string, data->namespaces, NULL);
+ g_string_append_printf (data->string, " ");
/* rdf:type needs to be first, otherwise you'll see 'subject x is not in domain y'
* errors for the properties you try to set.
*/
- type_value = g_hash_table_lookup (priv->properties, "rdf:type");
- if (type_value != NULL) {
- tracker_sparql_builder_predicate (builder, "a");
- if (G_VALUE_TYPE (type_value) == G_TYPE_PTR_ARRAY) {
- g_ptr_array_foreach (g_value_get_boxed (type_value), (GFunc)generate_sparql_value, data);
- } else {
- generate_sparql_value (type_value, data);
- }
+ value = g_hash_table_lookup (priv->properties, "rdf:type");
+ if (value != NULL) {
+ generate_turtle_property ("a", value, data->string, data->namespaces, NULL);
+ had_property = TRUE;
}
- g_hash_table_foreach (priv->properties, generate_sparql_inserts_foreach, data);
+ g_hash_table_iter_init (&iter, priv->properties);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&property, (gpointer *)&value)) {
+ full_property = tracker_namespace_manager_expand_uri (data->namespaces, property);
- if (data->graph_id) {
- tracker_sparql_builder_graph_close (builder);
+ if (strcmp (full_property, TRACKER_PREFIX_RDF "type") != 0 && strcmp (property, "rdf:type") != 0) {
+ if (had_property) {
+ g_string_append (data->string, " ; \n ");
+ }
+
+ generate_turtle_property (property, value, data->string, data->namespaces, NULL);
+
+ had_property = TRUE;
+ }
+
+ g_free (full_property);
}
- tracker_sparql_builder_insert_close (builder);
+
+ g_string_append (data->string, " .\n");
}
/**
@@ -1159,17 +1165,24 @@ tracker_resource_print_sparql_update (TrackerResource *resource,
TrackerNamespaceManager *namespaces,
const char *graph_id)
{
+ TrackerResourcePrivate *priv;
GenerateSparqlData context;
char *result;
g_return_val_if_fail (TRACKER_IS_RESOURCE (resource), "");
+ priv = GET_PRIVATE(resource);
+
if (namespaces == NULL) {
namespaces = tracker_namespace_manager_get_default ();
}
+ if (g_hash_table_size (priv->properties) == 0) {
+ return g_strdup("");
+ }
+
context.namespaces = namespaces;
- context.builder = tracker_sparql_builder_new_update ();
+ context.string = g_string_new (NULL);
context.graph_id = graph_id;
/* Resources can be recursive, and may have repeated or even cyclic
@@ -1177,15 +1190,27 @@ tracker_resource_print_sparql_update (TrackerResource *resource,
*/
context.done_list = NULL;
- generate_sparql_update (resource, &context);
+ /* Delete the existing data. If we don't do this, we may get constraint
+ * violations due to trying to add a second value to a single-valued
+ * property, and we may get old metadata hanging around.
+ */
+ generate_sparql_deletes (resource, &context);
g_list_free (context.done_list);
+ context.done_list = NULL;
- /* We could save a memcpy here by returning the SparqlBuilder instead, but
- * this way we are free to remove the TrackerSparqlBuilder code altogether
- * in future without having to change the public API of TrackerResource.
- */
- result = g_strdup (tracker_sparql_builder_get_result (context.builder));
- g_object_unref (context.builder);
- return result;
+ /* Finally insert the data */
+ g_string_append (context.string, "INSERT {\n");
+ if (graph_id) {
+ g_string_append_printf (context.string, "GRAPH <%s> {\n", graph_id);
+ }
+
+ generate_sparql_insert_pattern (resource, &context);
+
+ if (graph_id) {
+ g_string_append (context.string, "}\n");
+ }
+ g_string_append (context.string, "}\n");
+
+ return g_string_free (context.string, FALSE);
}