From 00524e7be7d1018664a56ea67b9b5281d5ce0f80 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 18 Nov 2020 01:49:08 +0100 Subject: libtracker-data: Add utility function to deserialize a resource to DB This is a bypass of all SPARQL, plugging directly TrackerResource (representing RDF and alll that) into TrackerData delete/insert statements. --- src/libtracker-data/tracker-data-update.c | 343 ++++++++++++++++++++++++++++++ src/libtracker-data/tracker-data-update.h | 6 + 2 files changed, 349 insertions(+) diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c index 8bced383b..8883a7f6b 100644 --- a/src/libtracker-data/tracker-data-update.c +++ b/src/libtracker-data/tracker-data-update.c @@ -164,6 +164,12 @@ static gboolean resource_buffer_switch (TrackerData *data, const gchar *subject, gint subject_id, GError **error); +static gboolean update_resource_single (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + GHashTable *visited, + GHashTable *bnodes, + GError **error); void tracker_data_insert_statement_with_uri (TrackerData *data, const gchar *graph, @@ -1618,6 +1624,101 @@ bytes_to_gvalue (GBytes *bytes, } } +static const gchar * +get_bnode_for_resource (GHashTable *bnodes, + TrackerData *data, + TrackerResource *resource) +{ + TrackerDBInterface *iface; + const gchar *identifier; + gchar *bnode; + + bnode = g_hash_table_lookup (bnodes, resource); + if (bnode) + return bnode; + + iface = tracker_data_manager_get_writable_db_interface (data->manager); + bnode = tracker_data_query_unused_uuid (data->manager, + iface); + identifier = tracker_resource_get_identifier (resource); + g_hash_table_insert (bnodes, g_strdup (identifier), bnode); + + return bnode; +} + +static void +bytes_from_gvalue (GValue *gvalue, + GBytes **bytes, + TrackerData *data, + GHashTable *bnodes) +{ + gchar *str; + + if (G_VALUE_HOLDS_BOOLEAN (gvalue)) { + if (g_value_get_boolean (gvalue)) { + *bytes = g_bytes_new_static ("true", strlen ("true") + 1); + } else { + *bytes = g_bytes_new_static ("false", strlen ("false") + 1); + } + } else if (G_VALUE_HOLDS_INT (gvalue)) { + str = g_strdup_printf ("%d", g_value_get_int (gvalue)); + *bytes = g_bytes_new_take (str, strlen (str) + 1); + } else if (G_VALUE_HOLDS_INT64 (gvalue)) { + str = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (gvalue)); + *bytes = g_bytes_new_take (str, strlen (str) + 1); + } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { + gchar buffer[G_ASCII_DTOSTR_BUF_SIZE]; + g_ascii_dtostr (buffer, G_ASCII_DTOSTR_BUF_SIZE, + g_value_get_double (gvalue)); + *bytes = g_bytes_new (buffer, strlen (buffer) + 1); + } else if (g_strcmp0 (G_VALUE_TYPE_NAME (gvalue), "TrackerUri") == 0) { + /* FIXME: We can't access TrackerUri GType here */ + const gchar *uri; + gchar *expanded; + + uri = g_value_get_string (gvalue); + + if (g_str_has_prefix (uri, "_:")) { + gchar *bnode; + + bnode = g_hash_table_lookup (bnodes, uri); + + if (!bnode) { + TrackerDBInterface *iface; + + iface = tracker_data_manager_get_writable_db_interface (data->manager); + bnode = tracker_data_query_unused_uuid (data->manager, + iface); + g_hash_table_insert (bnodes, g_strdup (uri), bnode); + } + + *bytes = g_bytes_new (bnode, strlen (bnode) + 1); + } else if (tracker_data_manager_expand_prefix (data->manager, + g_value_get_string (gvalue), + NULL, NULL, + &expanded)) { + *bytes = g_bytes_new_take (expanded, strlen (expanded) + 1); + } else { + *bytes = g_bytes_new (uri, strlen (uri) + 1); + } + } else if (G_VALUE_HOLDS_STRING (gvalue)) { + const gchar *ptr; + ptr = g_value_get_string (gvalue); + *bytes = g_bytes_new (ptr, strlen (ptr) + 1); + } else if (G_VALUE_HOLDS (gvalue, TRACKER_TYPE_RESOURCE)) { + TrackerResource *res; + const gchar *object; + + res = g_value_get_object (gvalue); + object = tracker_resource_get_identifier (res); + + if (!object || g_str_has_prefix (object, "_:")) + object = get_bnode_for_resource (bnodes, data, res); + + *bytes = g_bytes_new (object, strlen (object) + 1); + } +} + static gboolean resource_in_domain_index_class (TrackerData *data, TrackerClass *domain_index_class) @@ -2169,6 +2270,66 @@ tracker_data_delete_statement (TrackerData *data, } } +static void +tracker_data_delete_all (TrackerData *data, + const gchar *graph, + const gchar *subject, + const gchar *predicate, + GError **error) +{ + gint subject_id = 0; + TrackerOntologies *ontologies; + TrackerProperty *property; + GArray *old_values; + GError *inner_error = NULL; + guint i; + + g_return_if_fail (subject != NULL); + g_return_if_fail (predicate != NULL); + g_return_if_fail (data->in_transaction); + + subject_id = query_resource_id (data, subject); + + if (subject_id == 0) { + /* subject not in database */ + return; + } + + if (!resource_buffer_switch (data, graph, subject, subject_id, error)) + return; + + ontologies = tracker_data_manager_get_ontologies (data->manager); + property = tracker_ontologies_get_property_by_uri (ontologies, + predicate); + old_values = get_old_property_values (data, property, &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return; + } + + for (i = 0; i < old_values->len; i++) { + GValue *value; + GBytes *bytes; + + value = &g_array_index (old_values, GValue, i); + bytes_from_gvalue (value, + &bytes, + data, + NULL); + + tracker_data_delete_statement (data, graph, subject, + predicate, bytes, + &inner_error); + g_bytes_unref (bytes); + + if (inner_error) + break; + } + + if (inner_error) + g_propagate_error (error, inner_error); +} + static gboolean delete_single_valued (TrackerData *data, const gchar *graph, @@ -2776,3 +2937,185 @@ tracker_data_delete_graph (TrackerData *data, return TRUE; } + +static gboolean +resource_maybe_reset_property (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + const gchar *subject_uri, + const gchar *property_uri, + GHashTable *bnodes, + GError **error) +{ + GError *inner_error = NULL; + const gchar *subject; + + /* If the subject is a blank node, this is a whole new insertion. + * We don't need deleting anything then. + */ + subject = tracker_resource_get_identifier (resource); + if (g_str_has_prefix (subject, "_:")) + return TRUE; + + tracker_data_delete_all (data, + graph, subject_uri, property_uri, + &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +static gboolean +update_resource_property (TrackerData *data, + const gchar *graph_uri, + TrackerResource *resource, + const gchar *subject, + const gchar *property, + GHashTable *visited, + GHashTable *bnodes, + GError **error) +{ + GList *values, *v; + gchar *property_uri; + GError *inner_error = NULL; + + values = tracker_resource_get_values (resource, property); + tracker_data_manager_expand_prefix (data->manager, + property, + NULL, NULL, + &property_uri); + + if (tracker_resource_get_property_overwrite (resource, property) && + !resource_maybe_reset_property (data, graph_uri, resource, + subject, property_uri, + bnodes, error)) { + g_free (property_uri); + return FALSE; + } + + for (v = values; v && !inner_error; v = v->next) { + GBytes *bytes = NULL; + + if (G_VALUE_HOLDS (v->data, TRACKER_TYPE_RESOURCE)) { + update_resource_single (data, + graph_uri, + g_value_get_object (v->data), + visited, + bnodes, + &inner_error); + if (inner_error) + break; + } + + bytes_from_gvalue (v->data, + &bytes, + data, + bnodes); + + tracker_data_insert_statement (data, + graph_uri, + subject, + property_uri, + bytes, + &inner_error); + g_bytes_unref (bytes); + } + + g_list_free (values); + g_free (property_uri); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +static gboolean +update_resource_single (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + GHashTable *visited, + GHashTable *bnodes, + GError **error) +{ + GList *properties, *l; + GError *inner_error = NULL; + const gchar *subject; + gchar *graph_uri = NULL; + + if (g_hash_table_lookup (visited, resource)) + return TRUE; + + g_hash_table_add (visited, resource); + + properties = tracker_resource_get_properties (resource); + + subject = tracker_resource_get_identifier (resource); + if (!subject || g_str_has_prefix (subject, "_:")) + subject = get_bnode_for_resource (bnodes, data, resource); + + if (graph) { + tracker_data_manager_expand_prefix (data->manager, + graph, NULL, NULL, + &graph_uri); + } + + if (g_list_find_custom (properties, "rdf:type", (GCompareFunc) g_strcmp0)) { + update_resource_property (data, graph_uri, resource, + subject, "rdf:type", + visited, bnodes, + &inner_error); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + } + + for (l = properties; l; l = l->next) { + if (!update_resource_property (data, graph_uri, resource, + subject, l->data, + visited, bnodes, + &inner_error)) + break; + } + + g_list_free (properties); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +gboolean +tracker_data_update_resource (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + GHashTable *bnodes, + GError **error) +{ + GHashTable *visited; + gboolean retval; + + visited = g_hash_table_new (NULL, NULL); + + if (bnodes) + g_hash_table_ref (bnodes); + else + bnodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + retval = update_resource_single (data, graph, resource, visited, bnodes, error); + + g_hash_table_unref (visited); + g_hash_table_unref (bnodes); + + return retval; +} diff --git a/src/libtracker-data/tracker-data-update.h b/src/libtracker-data/tracker-data-update.h index 00588fa00..26fc3d971 100644 --- a/src/libtracker-data/tracker-data-update.h +++ b/src/libtracker-data/tracker-data-update.h @@ -137,6 +137,12 @@ void tracker_data_remove_rollback_statement_callback (TrackerData TrackerCommitCallback callback, gpointer user_data); +gboolean tracker_data_update_resource (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + GHashTable *bnodes, + GError **error); + GType tracker_data_get_type (void) G_GNUC_CONST; TrackerData * tracker_data_new (TrackerDataManager *manager); -- cgit v1.2.1