diff options
author | Sam Thursfield <sam@afuera.me.uk> | 2020-11-27 13:35:08 +0000 |
---|---|---|
committer | Sam Thursfield <sam@afuera.me.uk> | 2020-11-27 13:35:08 +0000 |
commit | f235199ceb3dc5afcd07270760347faede0a81c0 (patch) | |
tree | 30dd0d3b75c03faf39626ba7a457dca7e1218541 | |
parent | b909e9fc05b5af5f2433c67b8bd48fae5c7c8ad8 (diff) | |
parent | abbb813542d7e9f906232f2f35e299b43d65703a (diff) | |
download | tracker-f235199ceb3dc5afcd07270760347faede0a81c0.tar.gz |
Merge branch 'wip/carlosg/batches-and-resources' into 'master'
Batches and resources
See merge request GNOME/tracker!345
29 files changed, 1680 insertions, 209 deletions
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml b/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml index 792f309e7..2599b0f5a 100644 --- a/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml +++ b/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml @@ -36,6 +36,7 @@ <xi:include href="xml/tracker-sparql-cursor.xml"/> <xi:include href="xml/tracker-notifier.xml"/> <xi:include href="xml/tracker-endpoint.xml"/> + <xi:include href="xml/tracker-batch.xml"/> <xi:include href="xml/tracker-misc.xml"/> <xi:include href="xml/tracker-version.xml"/> <xi:include href="xml/tracker-sparql-error.xml"/> @@ -67,33 +68,13 @@ <title>Index of deprecated symbols</title> <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include> </index> - <index id="api-index-0-10" role="0.10"> - <title>Index of new symbols in 0.10</title> - <xi:include href="xml/api-index-0.10.xml"><xi:fallback /></xi:include> + <index id="api-index-3-0" role="3.0"> + <title>Index of new symbols in 3.1</title> + <xi:include href="xml/api-index-3.0.xml"><xi:fallback /></xi:include> </index> - <index id="api-index-0-12" role="0.12"> - <title>Index of new symbols in 0.12</title> - <xi:include href="xml/api-index-0.12.xml"><xi:fallback /></xi:include> - </index> - <index id="api-index-1-10" role="1.10"> - <title>Index of new symbols in 1.10</title> - <xi:include href="xml/api-index-1.10.xml"><xi:fallback /></xi:include> - </index> - <index id="api-index-1-12" role="1.12"> - <title>Index of new symbols in 1.12</title> - <xi:include href="xml/api-index-1.12.xml"><xi:fallback /></xi:include> - </index> - <index id="api-index-2-0" role="2.0"> - <title>Index of new symbols in 2.0</title> - <xi:include href="xml/api-index-2.0.xml"><xi:fallback /></xi:include> - </index> - <index id="api-index-2-0-5" role="2.0.5"> - <title>Index of new symbols in 2.0.5</title> - <xi:include href="xml/api-index-2.0.5.xml"><xi:fallback /></xi:include> - </index> - <index id="api-index-2-2" role="2.2"> - <title>Index of new symbols in 2.2</title> - <xi:include href="xml/api-index-2.2.xml"><xi:fallback /></xi:include> + <index id="api-index-3-1" role="3.1"> + <title>Index of new symbols in 3.1</title> + <xi:include href="xml/api-index-3.1.xml"><xi:fallback /></xi:include> </index> <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt index e78d96b0a..93d73f2b4 100644 --- a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt +++ b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt @@ -47,6 +47,7 @@ tracker_resource_get_first_uri tracker_resource_get_identifier tracker_resource_get_values tracker_resource_get_properties +tracker_resource_get_property_overwrite tracker_resource_identifier_compare_func tracker_resource_print_sparql_update tracker_resource_print_turtle @@ -117,8 +118,12 @@ tracker_sparql_connection_update_array_finish tracker_sparql_connection_update_blank tracker_sparql_connection_update_blank_async tracker_sparql_connection_update_blank_finish +tracker_sparql_connection_update_resource +tracker_sparql_connection_update_resource_async +tracker_sparql_connection_update_resource_finish tracker_sparql_connection_get_namespace_manager tracker_sparql_connection_create_notifier +tracker_sparql_connection_create_batch tracker_sparql_connection_close tracker_sparql_connection_close_async tracker_sparql_connection_close_finish @@ -264,6 +269,27 @@ tracker_endpoint_dbus_get_type </SECTION> <SECTION> +<FILE>tracker-batch</FILE> +<TITLE>TrackerBatch</TITLE> +TrackerBatch +tracker_batch_get_connection +tracker_batch_add_sparql +tracker_batch_add_resource +tracker_batch_execute +tracker_batch_execute_async +tracker_batch_execute_finish +<SUBSECTION Standard> +TrackerBatchClass +TRACKER_BATCH +TRACKER_BATCH_CLASS +TRACKER_BATCH_GET_CLASS +TRACKER_IS_BATCH +TRACKER_IS_BATCH_CLASS +TRACKER_TYPE_BATCH +tracker_batch_get_type +</SECTION> + +<SECTION> <TITLE>Version Information</TITLE> <FILE>tracker-version</FILE> tracker_major_version @@ -274,7 +300,18 @@ tracker_interface_age tracker_check_version <SUBSECTION> +TRACKER_MAJOR_VERSION +TRACKER_MINOR_VERSION +TRACKER_MICRO_VERSION TRACKER_CHECK_VERSION <SUBSECTION Private> TRACKER_AVAILABLE_IN_ALL +TRACKER_AVAILABLE_IN_3_1 +TRACKER_DEPRECATED_IN_3_1 +TRACKER_DEPRECATED_IN_3_1_FOR +TRACKER_VERSION_MAX_ALLOWED +TRACKER_VERSION_MIN_REQUIRED +TRACKER_VERSION_CUR +TRACKER_VERSION_3_0 +TRACKER_VERSION_3_1 </SECTION> diff --git a/docs/reference/libtracker-sparql/libtracker-sparql.types b/docs/reference/libtracker-sparql/libtracker-sparql.types index 21681e1ee..4a94c21cc 100644 --- a/docs/reference/libtracker-sparql/libtracker-sparql.types +++ b/docs/reference/libtracker-sparql/libtracker-sparql.types @@ -11,3 +11,4 @@ tracker_sparql_connection_get_type tracker_sparql_connection_flags_get_type tracker_sparql_cursor_get_type tracker_sparql_statement_get_type +tracker_batch_get_type diff --git a/src/libtracker-data/tracker-data-manager.c b/src/libtracker-data/tracker-data-manager.c index bbb4d2e0e..bea674b3e 100644 --- a/src/libtracker-data/tracker-data-manager.c +++ b/src/libtracker-data/tracker-data-manager.c @@ -5108,3 +5108,68 @@ tracker_data_manager_release_memory (TrackerDataManager *manager) { tracker_db_manager_release_memory (manager->db_manager); } + +gboolean +tracker_data_manager_expand_prefix (TrackerDataManager *manager, + const gchar *term, + GHashTable *prefix_map, + gchar **prefix, + gchar **expanded) +{ + const gchar *sep, *expanded_ns = NULL; + TrackerOntologies *ontologies; + TrackerNamespace **namespaces; + guint n_namespaces, i; + gchar *ns; + + sep = strchr (term, ':'); + + if (sep) { + ns = g_strndup (term, sep - term); + sep++; + } else { + ns = g_strdup (term); + } + + if (prefix_map) + expanded_ns = g_hash_table_lookup (prefix_map, ns); + + if (!expanded_ns) { + ontologies = tracker_data_manager_get_ontologies (manager); + namespaces = tracker_ontologies_get_namespaces (ontologies, &n_namespaces); + + for (i = 0; i < n_namespaces; i++) { + if (!g_str_equal (ns, tracker_namespace_get_prefix (namespaces[i]))) + continue; + + expanded_ns = tracker_namespace_get_uri (namespaces[i]); + + if (prefix_map) + g_hash_table_insert (prefix_map, ns, g_strdup (expanded_ns)); + break; + } + } + + if (!expanded_ns) { + if (prefix) + *prefix = NULL; + if (expanded) + *expanded = g_strdup (term); + + g_free (ns); + return FALSE; + } + + if (prefix) + *prefix = g_strdup (expanded_ns); + + if (expanded) { + if (sep) { + *expanded = g_strdup_printf ("%s%s", expanded_ns, sep); + } else { + *expanded = g_strdup (expanded_ns); + } + } + + return TRUE; +} diff --git a/src/libtracker-data/tracker-data-manager.h b/src/libtracker-data/tracker-data-manager.h index db8b4e509..474e8f815 100644 --- a/src/libtracker-data/tracker-data-manager.h +++ b/src/libtracker-data/tracker-data-manager.h @@ -107,6 +107,12 @@ void tracker_data_manager_commit_graphs (TrackerDataManager *man void tracker_data_manager_release_memory (TrackerDataManager *manager); +gboolean tracker_data_manager_expand_prefix (TrackerDataManager *manager, + const gchar *term, + GHashTable *prefix_map, + gchar **prefix, + gchar **expanded); + G_END_DECLS #endif /* __LIBTRACKER_DATA_MANAGER_H__ */ diff --git a/src/libtracker-data/tracker-data-update.c b/src/libtracker-data/tracker-data-update.c index 3ce1b7897..378f434bc 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, @@ -2637,7 +2798,7 @@ update_sparql (TrackerData *data, } sparql_query = tracker_sparql_new_update (data->manager, update); - blank_nodes = tracker_sparql_execute_update (sparql_query, blank, &actual_error); + blank_nodes = tracker_sparql_execute_update (sparql_query, blank, NULL, &actual_error); g_object_unref (sparql_query); if (actual_error) { @@ -2776,3 +2937,189 @@ 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); + } + + /* Handle rdf:type first */ + 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 (g_str_equal (l->data, "rdf:type")) + continue; + + 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); diff --git a/src/libtracker-data/tracker-sparql.c b/src/libtracker-data/tracker-sparql.c index f2b81f695..45caa02df 100644 --- a/src/libtracker-data/tracker-sparql.c +++ b/src/libtracker-data/tracker-sparql.c @@ -310,58 +310,6 @@ tracker_sparql_expand_base (TrackerSparql *sparql, return g_strdup (term); } -static inline gchar * -tracker_sparql_expand_prefix (TrackerSparql *sparql, - const gchar *term) -{ - const gchar *sep; - gchar *ns, *expanded_ns; - - sep = strchr (term, ':'); - - if (sep) { - ns = g_strndup (term, sep - term); - sep++; - } else { - ns = g_strdup (term); - } - - expanded_ns = g_hash_table_lookup (sparql->prefix_map, ns); - - if (!expanded_ns && g_strcmp0 (ns, "fn") == 0) - expanded_ns = FN_NS; - - if (!expanded_ns) { - TrackerOntologies *ontologies; - TrackerNamespace **namespaces; - guint n_namespaces, i; - - ontologies = tracker_data_manager_get_ontologies (sparql->data_manager); - namespaces = tracker_ontologies_get_namespaces (ontologies, &n_namespaces); - - for (i = 0; i < n_namespaces; i++) { - if (!g_str_equal (ns, tracker_namespace_get_prefix (namespaces[i]))) - continue; - - expanded_ns = g_strdup (tracker_namespace_get_uri (namespaces[i])); - g_hash_table_insert (sparql->prefix_map, g_strdup (ns), expanded_ns); - } - - if (!expanded_ns) { - g_free (ns); - return NULL; - } - } - - g_free (ns); - - if (sep) { - return g_strdup_printf ("%s%s", expanded_ns, sep); - } else { - return g_strdup (expanded_ns); - } -} - static inline void tracker_sparql_iter_next (TrackerSparql *sparql) { @@ -1117,7 +1065,10 @@ _extract_node_string (TrackerParserNode *node, unexpanded = g_strndup (terminal_start + add_start, terminal_end - terminal_start - subtract_end); - str = tracker_sparql_expand_prefix (sparql, unexpanded); + tracker_data_manager_expand_prefix (sparql->data_manager, + unexpanded, + sparql->prefix_map, + NULL, &str); g_free (unexpanded); break; } @@ -9310,6 +9261,8 @@ tracker_sparql_init (TrackerSparql *sparql) { sparql->prefix_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert (sparql->prefix_map, g_strdup ("fn"), g_strdup (FN_NS)); + sparql->cached_bindings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); sparql->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -9616,6 +9569,7 @@ tracker_sparql_new_update (TrackerDataManager *manager, GVariant * tracker_sparql_execute_update (TrackerSparql *sparql, gboolean blank, + GHashTable *bnode_map, GError **error) { TrackerSparqlState state = { 0 }; @@ -9631,6 +9585,8 @@ tracker_sparql_execute_update (TrackerSparql *sparql, sparql->current_state = &state; sparql->current_state->node = tracker_node_tree_get_root (sparql->tree); + sparql->current_state->blank_node_map = + bnode_map ? g_hash_table_ref (bnode_map) : NULL; tracker_sparql_init_string_builder (sparql); retval = _call_rule_func (sparql, NAMED_RULE_Update, error); sparql->current_state = NULL; diff --git a/src/libtracker-data/tracker-sparql.h b/src/libtracker-data/tracker-sparql.h index 57651de1a..74297bd0a 100644 --- a/src/libtracker-data/tracker-sparql.h +++ b/src/libtracker-data/tracker-sparql.h @@ -40,9 +40,10 @@ TrackerSparqlCursor * tracker_sparql_execute_cursor (TrackerSparql *sparql, GError **error); TrackerSparql * tracker_sparql_new_update (TrackerDataManager *manager, - const gchar *query); + const gchar *query); GVariant * tracker_sparql_execute_update (TrackerSparql *sparql, gboolean blank, + GHashTable *bnode_map, GError **error); GBytes * tracker_sparql_make_langstring (const gchar *str, diff --git a/src/libtracker-sparql/direct/meson.build b/src/libtracker-sparql/direct/meson.build index c1ec244e9..9f5ed7b95 100644 --- a/src/libtracker-sparql/direct/meson.build +++ b/src/libtracker-sparql/direct/meson.build @@ -1,5 +1,6 @@ libtracker_direct = static_library('tracker-direct', 'tracker-direct.c', + 'tracker-direct-batch.c', 'tracker-direct-statement.c', c_args: tracker_c_args + [ '-include', 'libtracker-sparql/tracker-private.h', diff --git a/src/libtracker-sparql/direct/tracker-direct-batch.c b/src/libtracker-sparql/direct/tracker-direct-batch.c new file mode 100644 index 000000000..718a1cdc6 --- /dev/null +++ b/src/libtracker-sparql/direct/tracker-direct-batch.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2020, Red Hat, Inc. + * + * 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: Carlos Garnacho <carlosg@gnome.org> + */ + +#include "config.h" + +#include <libtracker-data/tracker-data-update.h> +#include <libtracker-data/tracker-sparql.h> + +#include "tracker-direct-batch.h" +#include "tracker-direct.h" +#include "tracker-data.h" +#include "tracker-private.h" + +typedef struct _TrackerDirectBatchPrivate TrackerDirectBatchPrivate; +typedef struct _TrackerBatchElem TrackerBatchElem; + +struct _TrackerBatchElem +{ + guint type; + + union { + gchar *sparql; + + struct { + gchar *graph; + TrackerResource *resource; + } resource; + } d; +}; + +struct _TrackerDirectBatchPrivate +{ + GArray *array; +}; + +enum { + TRACKER_DIRECT_BATCH_RESOURCE, + TRACKER_DIRECT_BATCH_SPARQL, +}; + +G_DEFINE_TYPE_WITH_PRIVATE (TrackerDirectBatch, + tracker_direct_batch, + TRACKER_TYPE_BATCH) + +static void +tracker_direct_batch_finalize (GObject *object) +{ + TrackerDirectBatchPrivate *priv; + + priv = tracker_direct_batch_get_instance_private (TRACKER_DIRECT_BATCH (object)); + g_array_unref (priv->array); + + G_OBJECT_CLASS (tracker_direct_batch_parent_class)->finalize (object); +} + +static void +tracker_direct_batch_add_sparql (TrackerBatch *batch, + const gchar *sparql) +{ + TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch); + TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct); + TrackerBatchElem elem; + + elem.type = TRACKER_DIRECT_BATCH_SPARQL; + elem.d.sparql = g_strdup (sparql); + g_array_append_val (priv->array, elem); +} + +static void +tracker_direct_batch_add_resource (TrackerBatch *batch, + const gchar *graph, + TrackerResource *resource) +{ + TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch); + TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct); + TrackerBatchElem elem; + + elem.type = TRACKER_DIRECT_BATCH_RESOURCE; + elem.d.resource.graph = g_strdup (graph); + elem.d.resource.resource = g_object_ref (resource); + g_array_append_val (priv->array, elem); +} + +static gboolean +tracker_direct_batch_execute (TrackerBatch *batch, + GCancellable *cancellable, + GError **error) +{ + TrackerDirectConnection *conn; + + conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch)); + + return tracker_direct_connection_update_batch (conn, batch, error); +} + +static void +tracker_direct_batch_execute_async (TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerDirectConnection *conn; + + conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch)); + + tracker_direct_connection_update_batch_async (conn, batch, + cancellable, + callback, + user_data); +} + +static gboolean +tracker_direct_batch_execute_finish (TrackerBatch *batch, + GAsyncResult *res, + GError **error) +{ + TrackerDirectConnection *conn; + + conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch)); + + return tracker_direct_connection_update_batch_finish (conn, res, error); +} + +static void +tracker_direct_batch_class_init (TrackerDirectBatchClass *klass) +{ + TrackerBatchClass *batch_class = (TrackerBatchClass *) klass; + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = tracker_direct_batch_finalize; + + batch_class->add_sparql = tracker_direct_batch_add_sparql; + batch_class->add_resource = tracker_direct_batch_add_resource; + batch_class->execute = tracker_direct_batch_execute; + batch_class->execute_async = tracker_direct_batch_execute_async; + batch_class->execute_finish = tracker_direct_batch_execute_finish; +} + +static void +tracker_batch_elem_clear (TrackerBatchElem *elem) +{ + if (elem->type == TRACKER_DIRECT_BATCH_RESOURCE) { + g_object_run_dispose (G_OBJECT (elem->d.resource.resource)); + g_object_unref (elem->d.resource.resource); + g_free (elem->d.resource.graph); + } else if (elem->type == TRACKER_DIRECT_BATCH_SPARQL) { + g_free (elem->d.sparql); + } +} + +static void +tracker_direct_batch_init (TrackerDirectBatch *batch) +{ + TrackerDirectBatchPrivate *priv; + + priv = tracker_direct_batch_get_instance_private (batch); + priv->array = g_array_new (FALSE, FALSE, sizeof (TrackerBatchElem)); + g_array_set_clear_func (priv->array, (GDestroyNotify) tracker_batch_elem_clear); +} + +TrackerBatch * +tracker_direct_batch_new (TrackerSparqlConnection *conn) +{ + return g_object_new (TRACKER_TYPE_DIRECT_BATCH, + "connection", conn, + NULL); +} + +/* Executes with the update lock held */ +gboolean +tracker_direct_batch_update (TrackerDirectBatch *batch, + TrackerDataManager *data_manager, + GError **error) +{ + TrackerDirectBatchPrivate *priv; + GError *inner_error = NULL; + GHashTable *bnodes; + TrackerData *data; + guint i; + + priv = tracker_direct_batch_get_instance_private (batch); + data = tracker_data_manager_get_data (data_manager); + bnodes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + + tracker_data_begin_transaction (data, &inner_error); + if (inner_error) + goto error; + + for (i = 0; i < priv->array->len; i++) { + TrackerBatchElem *elem; + + elem = &g_array_index (priv->array, TrackerBatchElem, i); + + if (elem->type == TRACKER_DIRECT_BATCH_RESOURCE) { + tracker_data_update_resource (data, + elem->d.resource.graph, + elem->d.resource.resource, + bnodes, + &inner_error); + } else if (elem->type == TRACKER_DIRECT_BATCH_SPARQL) { + TrackerSparql *query; + + query = tracker_sparql_new_update (data_manager, + elem->d.sparql); + tracker_sparql_execute_update (query, FALSE, + bnodes, + &inner_error); + g_object_unref (query); + } else { + g_assert_not_reached (); + } + + if (inner_error) + break; + } + + if (!inner_error) + tracker_data_update_buffer_flush (data, &inner_error); + + if (inner_error) { + tracker_data_rollback_transaction (data); + goto error; + } + + tracker_data_commit_transaction (data, &inner_error); + if (inner_error) + goto error; + + g_hash_table_unref (bnodes); + + return TRUE; + +error: + g_hash_table_unref (bnodes); + g_propagate_error (error, inner_error); + return FALSE; +} diff --git a/src/libtracker-sparql/direct/tracker-direct-batch.h b/src/libtracker-sparql/direct/tracker-direct-batch.h new file mode 100644 index 000000000..685b94fb7 --- /dev/null +++ b/src/libtracker-sparql/direct/tracker-direct-batch.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018, Red Hat, Inc. + * + * 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. + */ + +#ifndef __TRACKER_DIRECT_BATCH_H__ +#define __TRACKER_DIRECT_BATCH_H__ + +#include "tracker-direct.h" +#include <libtracker-sparql/tracker-sparql.h> +#include <libtracker-sparql/tracker-private.h> + +#define TRACKER_TYPE_DIRECT_BATCH (tracker_direct_batch_get_type ()) + +G_DECLARE_FINAL_TYPE (TrackerDirectBatch, + tracker_direct_batch, + TRACKER, DIRECT_BATCH, + TrackerBatch) + +struct _TrackerDirectBatch +{ + TrackerBatch parent_instance; +}; + +TrackerBatch * tracker_direct_batch_new (TrackerSparqlConnection *conn); + +gboolean tracker_direct_batch_update (TrackerDirectBatch *batch, + TrackerDataManager *data_manager, + GError **error); + +#endif /* __TRACKER_DIRECT_BATCH_H__ */ diff --git a/src/libtracker-sparql/direct/tracker-direct.c b/src/libtracker-sparql/direct/tracker-direct.c index 3d867283c..4908c75b5 100644 --- a/src/libtracker-sparql/direct/tracker-direct.c +++ b/src/libtracker-sparql/direct/tracker-direct.c @@ -21,6 +21,7 @@ #include "config.h" #include "tracker-direct.h" +#include "tracker-direct-batch.h" #include "tracker-direct-statement.h" #include "libtracker-sparql/tracker-private.h" #include <libtracker-data/tracker-data.h> @@ -54,6 +55,11 @@ struct _TrackerDirectConnectionPrivate guint closing : 1; }; +typedef struct { + gchar *graph; + TrackerResource *resource; +} UpdateResource; + enum { PROP_0, PROP_FLAGS, @@ -68,12 +74,15 @@ typedef enum { TASK_TYPE_QUERY, TASK_TYPE_UPDATE, TASK_TYPE_UPDATE_BLANK, + TASK_TYPE_UPDATE_RESOURCE, + TASK_TYPE_UPDATE_BATCH, TASK_TYPE_RELEASE_MEMORY, } TaskType; typedef struct { TaskType type; - gchar *query; + gpointer data; + GDestroyNotify destroy; } TaskData; static void tracker_direct_connection_initable_iface_init (GInitableIface *iface); @@ -90,22 +99,25 @@ G_DEFINE_TYPE_WITH_CODE (TrackerDirectConnection, tracker_direct_connection, tracker_direct_connection_async_initable_iface_init)) static TaskData * -task_data_query_new (TaskType type, - const gchar *sparql) +task_data_query_new (TaskType type, + gpointer data, + GDestroyNotify destroy) { - TaskData *data; + TaskData *task; - data = g_new0 (TaskData, 1); - data->type = type; - data->query = g_strdup (sparql); + task = g_new0 (TaskData, 1); + task->type = type; + task->data = data; + task->destroy = destroy; - return data; + return task; } static void task_data_free (TaskData *task) { - g_free (task->query); + if (task->destroy && task->data) + task->destroy (task->data); g_free (task); } @@ -131,7 +143,7 @@ cleanup_timeout_cb (gpointer user_data) task = g_task_new (conn, NULL, NULL, NULL); g_task_set_task_data (task, - task_data_query_new (TASK_TYPE_RELEASE_MEMORY, NULL), + task_data_query_new (TASK_TYPE_RELEASE_MEMORY, NULL, NULL), (GDestroyNotify) task_data_free); g_thread_pool_push (priv->update_thread, task, NULL); @@ -139,6 +151,39 @@ cleanup_timeout_cb (gpointer user_data) return G_SOURCE_CONTINUE; } +gboolean +update_resource (TrackerData *data, + const gchar *graph, + TrackerResource *resource, + GError **error) +{ + GError *inner_error = NULL; + + tracker_data_begin_transaction (data, &inner_error); + if (inner_error) + goto error; + + tracker_data_update_resource (data, + graph, + resource, + NULL, + &inner_error); + + if (inner_error) { + tracker_data_rollback_transaction (data); + goto error; + } + + tracker_data_commit_transaction (data, &inner_error); + if (inner_error) + goto error; + + return TRUE; + +error: + g_propagate_error (error, inner_error); + return FALSE; +} static void update_thread_func (gpointer data, @@ -165,12 +210,20 @@ update_thread_func (gpointer data, g_warning ("Queries don't go through this thread"); break; case TASK_TYPE_UPDATE: - tracker_data_update_sparql (tracker_data, task_data->query, &error); + tracker_data_update_sparql (tracker_data, task_data->data, &error); break; case TASK_TYPE_UPDATE_BLANK: - retval = tracker_data_update_sparql_blank (tracker_data, task_data->query, &error); + retval = tracker_data_update_sparql_blank (tracker_data, task_data->data, &error); destroy_notify = (GDestroyNotify) g_variant_unref; break; + case TASK_TYPE_UPDATE_RESOURCE: { + UpdateResource *data = task_data->data; + update_resource (tracker_data, data->graph, data->resource, &error); + break; + } + case TASK_TYPE_UPDATE_BATCH: + tracker_direct_batch_update (task_data->data, priv->data_manager, &error); + break; case TASK_TYPE_RELEASE_MEMORY: tracker_data_manager_release_memory (priv->data_manager); update_timestamp = FALSE; @@ -217,7 +270,7 @@ query_thread_pool_func (gpointer data, } cursor = tracker_sparql_connection_query (TRACKER_SPARQL_CONNECTION (g_task_get_source_object (task)), - task_data->query, + task_data->data, g_task_get_cancellable (task), &error); if (cursor) @@ -705,7 +758,9 @@ tracker_direct_connection_query_async (TrackerSparqlConnection *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, - task_data_query_new (TASK_TYPE_QUERY, sparql), + task_data_query_new (TASK_TYPE_QUERY, + g_strdup (sparql), + g_free), (GDestroyNotify) task_data_free); if (!g_thread_pool_push (priv->select_pool, task, &error)) { @@ -771,7 +826,9 @@ tracker_direct_connection_update_async (TrackerSparqlConnection *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, - task_data_query_new (TASK_TYPE_UPDATE, sparql), + task_data_query_new (TASK_TYPE_UPDATE, + g_strdup (sparql), + g_free), (GDestroyNotify) task_data_free); g_thread_pool_push (priv->update_thread, task, NULL); @@ -813,8 +870,7 @@ tracker_direct_connection_update_array_async (TrackerSparqlConnection *self, concatenated = g_strjoinv ("\n", array_copy); g_free (array_copy); - task_data = task_data_query_new (TASK_TYPE_UPDATE, NULL); - task_data->query = concatenated; + task_data = task_data_query_new (TASK_TYPE_UPDATE, concatenated, g_free); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, task_data, @@ -880,7 +936,9 @@ tracker_direct_connection_update_blank_async (TrackerSparqlConnection *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, - task_data_query_new (TASK_TYPE_UPDATE_BLANK, sparql), + task_data_query_new (TASK_TYPE_UPDATE_BLANK, + g_strdup (sparql), + g_free), (GDestroyNotify) task_data_free); g_thread_pool_push (priv->update_thread, task, NULL); @@ -984,7 +1042,7 @@ tracker_direct_connection_close (TrackerSparqlConnection *self) } } -void +static void async_close_thread_func (GTask *task, gpointer source_object, gpointer task_data, @@ -997,7 +1055,7 @@ async_close_thread_func (GTask *task, g_task_return_boolean (task, TRUE); } -void +static void tracker_direct_connection_close_async (TrackerSparqlConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, @@ -1010,7 +1068,7 @@ tracker_direct_connection_close_async (TrackerSparqlConnection *connection, g_object_unref (task); } -gboolean +static gboolean tracker_direct_connection_close_finish (TrackerSparqlConnection *connection, GAsyncResult *res, GError **error) @@ -1018,6 +1076,106 @@ tracker_direct_connection_close_finish (TrackerSparqlConnection *connection, return g_task_propagate_boolean (G_TASK (res), error); } +static UpdateResource * +update_resource_data_new (const gchar *graph, + TrackerResource *resource) +{ + UpdateResource *data; + + data = g_new0 (UpdateResource, 1); + data->graph = g_strdup (graph); + data->resource = g_object_ref (resource); + + return data; +} + +static void +update_resource_data_free (UpdateResource *data) +{ + g_free (data->graph); + g_object_unref (data->resource); + g_free (data); +} + +static gboolean +tracker_direct_connection_update_resource (TrackerSparqlConnection *self, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GError **error) +{ + TrackerDirectConnectionPrivate *priv; + TrackerDirectConnection *conn; + TrackerData *data; + GError *inner_error = NULL; + + conn = TRACKER_DIRECT_CONNECTION (self); + priv = tracker_direct_connection_get_instance_private (conn); + + g_mutex_lock (&priv->mutex); + data = tracker_data_manager_get_data (priv->data_manager); + update_resource (data, graph, resource, &inner_error); + tracker_direct_connection_update_timestamp (conn); + g_mutex_unlock (&priv->mutex); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +static void +tracker_direct_connection_update_resource_async (TrackerSparqlConnection *self, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerDirectConnectionPrivate *priv; + TrackerDirectConnection *conn; + TaskData *task_data; + GTask *task; + + conn = TRACKER_DIRECT_CONNECTION (self); + priv = tracker_direct_connection_get_instance_private (conn); + + task_data = task_data_query_new (TASK_TYPE_UPDATE_RESOURCE, + update_resource_data_new (graph, resource), + (GDestroyNotify) update_resource_data_free); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, task_data, + (GDestroyNotify) task_data_free); + + g_thread_pool_push (priv->update_thread, task, NULL); +} + +static gboolean +tracker_direct_connection_update_resource_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static TrackerBatch * +tracker_direct_connection_create_batch (TrackerSparqlConnection *connection) +{ + TrackerDirectConnectionPrivate *priv; + TrackerDirectConnection *conn; + + conn = TRACKER_DIRECT_CONNECTION (connection); + priv = tracker_direct_connection_get_instance_private (conn); + + if (priv->flags & TRACKER_SPARQL_CONNECTION_FLAGS_READONLY) + return NULL; + + return tracker_direct_batch_new (connection); +} + static void tracker_direct_connection_class_init (TrackerDirectConnectionClass *klass) { @@ -1048,6 +1206,10 @@ tracker_direct_connection_class_init (TrackerDirectConnectionClass *klass) sparql_connection_class->close = tracker_direct_connection_close; sparql_connection_class->close_async = tracker_direct_connection_close_async; sparql_connection_class->close_finish = tracker_direct_connection_close_finish; + sparql_connection_class->update_resource = tracker_direct_connection_update_resource; + sparql_connection_class->update_resource_async = tracker_direct_connection_update_resource_async; + sparql_connection_class->update_resource_finish = tracker_direct_connection_update_resource_finish; + sparql_connection_class->create_batch = tracker_direct_connection_create_batch; props[PROP_FLAGS] = g_param_spec_flags ("flags", @@ -1109,3 +1271,65 @@ tracker_direct_connection_update_timestamp (TrackerDirectConnection *conn) priv = tracker_direct_connection_get_instance_private (conn); priv->timestamp = g_get_monotonic_time (); } + +gboolean +tracker_direct_connection_update_batch (TrackerDirectConnection *conn, + TrackerBatch *batch, + GError **error) +{ + TrackerDirectConnectionPrivate *priv; + GError *inner_error = NULL; + + priv = tracker_direct_connection_get_instance_private (conn); + + g_mutex_lock (&priv->mutex); + tracker_direct_batch_update (TRACKER_DIRECT_BATCH (batch), + priv->data_manager, &inner_error); + tracker_direct_connection_update_timestamp (conn); + g_mutex_unlock (&priv->mutex); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +void +tracker_direct_connection_update_batch_async (TrackerDirectConnection *conn, + TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerDirectConnectionPrivate *priv; + GTask *task; + + priv = tracker_direct_connection_get_instance_private (conn); + + task = g_task_new (batch, cancellable, callback, user_data); + g_task_set_task_data (task, + task_data_query_new (TASK_TYPE_UPDATE_BATCH, + g_object_ref (batch), + g_object_unref), + (GDestroyNotify) task_data_free); + + g_thread_pool_push (priv->update_thread, task, NULL); +} + +gboolean +tracker_direct_connection_update_batch_finish (TrackerDirectConnection *conn, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + + g_task_propagate_boolean (G_TASK (res), &inner_error); + if (inner_error) { + g_propagate_error (error, _translate_internal_error (inner_error)); + return FALSE; + } + + return TRUE; +} diff --git a/src/libtracker-sparql/direct/tracker-direct.h b/src/libtracker-sparql/direct/tracker-direct.h index c353b51ca..d914f0fa1 100644 --- a/src/libtracker-sparql/direct/tracker-direct.h +++ b/src/libtracker-sparql/direct/tracker-direct.h @@ -24,6 +24,8 @@ #include <libtracker-sparql/tracker-sparql.h> #include <libtracker-data/tracker-data-manager.h> +#include "tracker-direct-batch.h" + #define TRACKER_TYPE_DIRECT_CONNECTION (tracker_direct_connection_get_type()) #define TRACKER_DIRECT_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DIRECT_CONNECTION, TrackerDirectConnection)) #define TRACKER_DIRECT_CONNECTION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), TRACKER_TYPE_DIRECT_CONNECTION, TrackerDirectConnectionClass)) @@ -55,7 +57,19 @@ TrackerDataManager *tracker_direct_connection_get_data_manager (TrackerDirectCon void tracker_direct_connection_update_timestamp (TrackerDirectConnection *conn); -/* Internal helper function */ +/* Internal helper functions */ GError *translate_db_interface_error (GError *error); +gboolean tracker_direct_connection_update_batch (TrackerDirectConnection *conn, + TrackerBatch *batch, + GError **error); +void tracker_direct_connection_update_batch_async (TrackerDirectConnection *conn, + TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean tracker_direct_connection_update_batch_finish (TrackerDirectConnection *conn, + GAsyncResult *res, + GError **error); + #endif /* __TRACKER_LOCAL_CONNECTION_H__ */ diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build index 465185753..2bb6ca8a6 100644 --- a/src/libtracker-sparql/meson.build +++ b/src/libtracker-sparql/meson.build @@ -1,3 +1,8 @@ +version_header = configure_file( + input: 'tracker-version-generated.h.meson.in', + output: 'tracker-version-generated.h', + configuration: conf) + enum_types = gnome.mkenums('tracker-sparql-enum-types', sources: ['tracker-notifier.h', 'tracker-connection.h'], c_template: 'tracker-sparql-enum-types.c.template', @@ -10,6 +15,7 @@ tracker_sparql_vapi = files('tracker-sparql.vapi') tracker_sparql_vapi_dep = meson.get_compiler('vala').find_library('tracker-sparql', dirs: meson.current_source_dir()) libtracker_sparql_c_sources = files( + 'tracker-batch.c', 'tracker-connection.c', 'tracker-cursor.c', 'tracker-endpoint.c', @@ -25,6 +31,7 @@ libtracker_sparql_c_sources = files( ) libtracker_sparql_c_public_headers = files( + 'tracker-batch.h', 'tracker-connection.h', 'tracker-cursor.h', 'tracker-endpoint.h', @@ -59,6 +66,7 @@ install_headers( libtracker_sparql_c_public_headers, 'tracker-ontologies.h', 'tracker-sparql.h', + version_header, subdir: 'tracker-@0@/libtracker-sparql'.format(tracker_api_version)) install_data( diff --git a/src/libtracker-sparql/tracker-batch.c b/src/libtracker-sparql/tracker-batch.c new file mode 100644 index 000000000..883d822a7 --- /dev/null +++ b/src/libtracker-sparql/tracker-batch.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2020, Red Hat Ltd. + * + * 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: Carlos Garnacho <carlosg@gnome.org> + */ +/** + * SECTION: tracker-batch + * @short_description: Update batches + * @title: TrackerBatch + * @stability: Stable + * @include: tracker-sparql.h + * + * #TrackerBatch is an object containing a series of SPARQL updates, + * in either SPARQL string or #TrackerResource form. This object has + * a single use, after the batch is executed, it can only be finished + * and freed. + * + * A batch is created with tracker_sparql_connection_create_batch(). + * To add resources use tracker_batch_add_resource() or + * tracker_batch_add_sparql(). + * + * When a batch is ready for execution, use tracker_batch_execute() + * or tracker_batch_execute_async(). The batch is executed as a single + * transaction, it will succeed or fail entirely. + * + * The mapping of blank node labels is global in a #TrackerBatch, + * referencing the same blank node label in different operations in + * a batch will resolve to the same resource. + * + * This object was added in Tracker 3.1. + */ +#include "config.h" + +#include "tracker-batch.h" +#include "tracker-connection.h" +#include "tracker-private.h" + +enum { + PROP_0, + PROP_CONNECTION, + N_PROPS +}; + +static GParamSpec *props[N_PROPS]; + +typedef struct { + TrackerSparqlConnection *connection; + gchar *sparql; + guint already_executed : 1; +} TrackerBatchPrivate; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerBatch, + tracker_batch, + G_TYPE_OBJECT) + +static void +tracker_batch_init (TrackerBatch *batch) +{ +} + +static void +tracker_batch_finalize (GObject *object) +{ + TrackerBatch *batch = TRACKER_BATCH (object); + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_clear_object (&priv->connection); + G_OBJECT_CLASS (tracker_batch_parent_class)->finalize (object); +} + +static void +tracker_batch_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TrackerBatch *batch = TRACKER_BATCH (object); + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + switch (prop_id) { + case PROP_CONNECTION: + priv->connection = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +tracker_batch_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TrackerBatch *batch = TRACKER_BATCH (object); + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +tracker_batch_class_init (TrackerBatchClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = tracker_batch_finalize; + object_class->set_property = tracker_batch_set_property; + object_class->get_property = tracker_batch_get_property; + + /** + * TrackerBatch:connection: + * + * The #TrackerSparqlConnection the batch belongs to. + */ + props[PROP_CONNECTION] = + g_param_spec_object ("connection", + "connection", + "connection", + TRACKER_TYPE_SPARQL_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS | + G_PARAM_READABLE | + G_PARAM_WRITABLE); + + g_object_class_install_properties (object_class, N_PROPS, props); +} + +/** + * tracker_batch_get_connection: + * @batch: a #TrackerBatch + * + * Returns the #TrackerSparqlConnection that this batch was created from. + * + * Returns: (transfer none): The SPARQL connection of this batch. + **/ +TrackerSparqlConnection * +tracker_batch_get_connection (TrackerBatch *batch) +{ + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_return_val_if_fail (TRACKER_IS_BATCH (batch), NULL); + + return priv->connection; +} + +/** + * tracker_batch_add_sparql: + * @batch: a #TrackerBatch + * @sparql: a SPARQL update string + * + * Adds an SPARQL update string to @batch. + * + * Since: 3.1 + **/ +void +tracker_batch_add_sparql (TrackerBatch *batch, + const gchar *sparql) +{ + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_return_if_fail (TRACKER_IS_BATCH (batch)); + g_return_if_fail (sparql != NULL); + g_return_if_fail (!priv->already_executed); + + TRACKER_BATCH_GET_CLASS (batch)->add_sparql (batch, sparql); +} + +/** + * tracker_batch_add_resource: + * @batch: a #TrackerBatch + * @graph: RDF graph to insert the resource to + * @resource: a #TrackerResource + * + * Adds the RDF represented by @resource to @batch. + * + * Since: 3.1 + **/ +void +tracker_batch_add_resource (TrackerBatch *batch, + const gchar *graph, + TrackerResource *resource) +{ + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_return_if_fail (TRACKER_IS_BATCH (batch)); + g_return_if_fail (TRACKER_IS_RESOURCE (resource)); + g_return_if_fail (!priv->already_executed); + + TRACKER_BATCH_GET_CLASS (batch)->add_resource (batch, graph, resource); +} + +/** + * tracker_batch_execute: + * @batch: a #TrackerBatch + * @cancellable: (nullable): a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Executes the batch. This operations happens synchronously. + * + * Returns: %TRUE of there were no errors, %FALSE otherwise + * + * Since: 3.1 + **/ +gboolean +tracker_batch_execute (TrackerBatch *batch, + GCancellable *cancellable, + GError **error) +{ + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_return_val_if_fail (TRACKER_IS_BATCH (batch), FALSE); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (!priv->already_executed, FALSE); + + priv->already_executed = TRUE; + + return TRACKER_BATCH_GET_CLASS (batch)->execute (batch, cancellable, error); +} + +/** + * tracker_batch_execute_async: + * @batch: a #TrackerBatch + * @cancellable: (nullable): a #GCancellable, or %NULL + * @callback: user-defined #GAsyncReadyCallback to be called when + * asynchronous operation is finished. + * @user_data: user-defined data to be passed to @callback + * + * Executes the batch. This operation happens asynchronously, when + * finished @callback will be executed. + * + * Since: 3.1 + **/ +void +tracker_batch_execute_async (TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch); + + g_return_if_fail (TRACKER_IS_BATCH (batch)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (callback != NULL); + g_return_if_fail (!priv->already_executed); + + priv->already_executed = TRUE; + TRACKER_BATCH_GET_CLASS (batch)->execute_async (batch, cancellable, callback, user_data); +} + +/** + * tracker_batch_execute_finish: + * @batch: a #TrackerBatch + * @res: a #GAsyncResult with the result of the operation + * @error: location for a #GError, or %NULL + * + * Finishes the operation started with tracker_batch_execute_async(). + * + * Returns: %TRUE of there were no errors, %FALSE otherwise + * + * Since: 3.1 + **/ +gboolean +tracker_batch_execute_finish (TrackerBatch *batch, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (TRACKER_IS_BATCH (batch), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return TRACKER_BATCH_GET_CLASS (batch)->execute_finish (batch, res, error); +} diff --git a/src/libtracker-sparql/tracker-batch.h b/src/libtracker-sparql/tracker-batch.h new file mode 100644 index 000000000..16e6a1099 --- /dev/null +++ b/src/libtracker-sparql/tracker-batch.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 Red Hat Ltd + * + * 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: Carlos Garnacho + */ +#ifndef __TRACKER_BATCH_H__ +#define __TRACKER_BATCH_H__ + +#if !defined (__LIBTRACKER_SPARQL_INSIDE__) && !defined (TRACKER_COMPILATION) +#error "only <libtracker-sparql/tracker-sparql.h> must be included directly." +#endif + +#include <libtracker-sparql/tracker-version.h> +#include <libtracker-sparql/tracker-resource.h> +#include <gio/gio.h> + +#define TRACKER_TYPE_BATCH tracker_batch_get_type () + +TRACKER_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE (TrackerBatch, + tracker_batch, + TRACKER, BATCH, + GObject) + +#include "tracker-connection.h" + +TRACKER_AVAILABLE_IN_3_1 +TrackerSparqlConnection * tracker_batch_get_connection (TrackerBatch *batch); + +TRACKER_AVAILABLE_IN_3_1 +void tracker_batch_add_sparql (TrackerBatch *batch, + const gchar *sparql); + +TRACKER_AVAILABLE_IN_3_1 +void tracker_batch_add_resource (TrackerBatch *batch, + const gchar *graph, + TrackerResource *resource); + +TRACKER_AVAILABLE_IN_3_1 +gboolean tracker_batch_execute (TrackerBatch *batch, + GCancellable *cancellable, + GError **error); + +TRACKER_AVAILABLE_IN_3_1 +void tracker_batch_execute_async (TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +TRACKER_AVAILABLE_IN_3_1 +gboolean tracker_batch_execute_finish (TrackerBatch *batch, + GAsyncResult *res, + GError **error); + +#endif /* __TRACKER_BATCH_H__ */ diff --git a/src/libtracker-sparql/tracker-connection.c b/src/libtracker-sparql/tracker-connection.c index 8df18b895..5dcc3d226 100644 --- a/src/libtracker-sparql/tracker-connection.c +++ b/src/libtracker-sparql/tracker-connection.c @@ -182,8 +182,6 @@ tracker_sparql_connection_class_init (TrackerSparqlConnectionClass *klass) * * Returns: (transfer full): a new remote #TrackerSparqlConnection. Call * g_object_unref() on the object when no longer used. - * - * Since: 1.12 */ /** @@ -538,6 +536,102 @@ tracker_sparql_connection_update_blank_finish (TrackerSparqlConnection *connect } /** + * tracker_sparql_connection_update_resource: + * @connection: a #TrackerSparqlConnection + * @graph: (nullable): RDF graph where the resource should be inserted/updated, or %NULL for the default graph + * @resource: a #TrackerResource + * @cancellable: (nullable): a #GCancellable, or %NULL + * @error: pointer to a #GError, or %NULL + * + * Inserts a resource as described by @resource, on the graph described by @graph. + * This operation blocks until done. + * + * Returns: #TRUE if there were no errors. + * + * Since: 3.1 + **/ +gboolean +tracker_sparql_connection_update_resource (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (TRACKER_IS_SPARQL_CONNECTION (connection), FALSE); + g_return_val_if_fail (TRACKER_IS_RESOURCE (resource), FALSE); + g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->update_resource (connection, + graph, + resource, + cancellable, + error); +} + +/** + * tracker_sparql_connection_update_resource_async: + * @connection: a #TrackerSparqlConnection + * @graph: (nullable): RDF graph where the resource should be inserted/updated, or %NULL for the default graph + * @resource: a #TrackerResource + * @cancellable: (nullable): a #GCancellable, or %NULL + * @callback: the #GAsyncReadyCallback called when the operation completes + * @user_data: data passed to @callback + * + * Inserts a resource as described by @resource, on the graph described by @graph. + * This operation is executed asynchronously, when finished @callback will be + * executed. + * + * Since: 3.1 + **/ +void +tracker_sparql_connection_update_resource_async (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (TRACKER_IS_SPARQL_CONNECTION (connection)); + g_return_if_fail (TRACKER_IS_RESOURCE (resource)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (callback != NULL); + + TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->update_resource_async (connection, + graph, + resource, + cancellable, + callback, + user_data); +} + +/** + * tracker_sparql_connection_update_resource_finish: + * @connection: a #TrackerSparqlConnection + * @res: a #GAsyncResult with the result of the operation + * @error: pointer to a #GError, or %NULL + * + * Finishes a tracker_sparql_connection_update_resource_async() operation. + * + * Returns: #TRUE if there were no errors. + * + * Since: 3.1 + **/ +gboolean +tracker_sparql_connection_update_resource_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (TRACKER_IS_SPARQL_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + return TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->update_resource_finish (connection, + res, + error); +} + +/** * tracker_sparql_connection_get_namespace_manager: * @connection: a #TrackerSparqlConnection * @@ -673,3 +767,23 @@ tracker_sparql_connection_close_finish (TrackerSparqlConnection *connection, return TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->close_finish (connection, res, error); } + +/** + * tracker_sparql_connection_create_batch: + * @connection: a #TrackerSparqlConnection + * + * Creates a new batch to store and execute update commands. If the connection + * is readonly or cannot issue SPARQL updates, %NULL will be returned. + * + * Returns: (transfer full): (nullable): A new #TrackerBatch + **/ +TrackerBatch * +tracker_sparql_connection_create_batch (TrackerSparqlConnection *connection) +{ + g_return_val_if_fail (TRACKER_IS_SPARQL_CONNECTION (connection), NULL); + + if (!TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->create_batch) + return NULL; + + return TRACKER_SPARQL_CONNECTION_GET_CLASS (connection)->create_batch (connection); +} diff --git a/src/libtracker-sparql/tracker-connection.h b/src/libtracker-sparql/tracker-connection.h index ee92fde22..834f65fb6 100644 --- a/src/libtracker-sparql/tracker-connection.h +++ b/src/libtracker-sparql/tracker-connection.h @@ -25,6 +25,7 @@ #include <libtracker-sparql/tracker-error.h> #include <libtracker-sparql/tracker-notifier.h> +#include <libtracker-sparql/tracker-resource.h> #include <libtracker-sparql/tracker-version.h> #include <gio/gio.h> @@ -63,6 +64,7 @@ G_DECLARE_DERIVABLE_TYPE (TrackerSparqlConnection, TRACKER, SPARQL_CONNECTION, GObject) +#include "tracker-batch.h" #include "tracker-cursor.h" #include "tracker-statement.h" #include "tracker-namespace-manager.h" @@ -136,6 +138,26 @@ TRACKER_AVAILABLE_IN_ALL gboolean tracker_sparql_connection_update_array_finish (TrackerSparqlConnection *connection, GAsyncResult *res, GError **error); +TRACKER_AVAILABLE_IN_3_1 +gboolean tracker_sparql_connection_update_resource (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GError **error); +TRACKER_AVAILABLE_IN_3_1 +void tracker_sparql_connection_update_resource_async (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +TRACKER_AVAILABLE_IN_3_1 +gboolean tracker_sparql_connection_update_resource_finish (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error); +TRACKER_AVAILABLE_IN_3_1 +TrackerBatch * tracker_sparql_connection_create_batch (TrackerSparqlConnection *connection); + TRACKER_AVAILABLE_IN_ALL GVariant * tracker_sparql_connection_update_blank (TrackerSparqlConnection *connection, const gchar *sparql, diff --git a/src/libtracker-sparql/tracker-namespace-manager.c b/src/libtracker-sparql/tracker-namespace-manager.c index c4f4a3003..d8f284ca9 100644 --- a/src/libtracker-sparql/tracker-namespace-manager.c +++ b/src/libtracker-sparql/tracker-namespace-manager.c @@ -99,8 +99,6 @@ finalize (GObject *object) * Creates a new #TrackerNamespaceManager instance. * * Returns: a new #TrackerNamespaceManager instance - * - * Since: 1.10 */ TrackerNamespaceManager * tracker_namespace_manager_new () @@ -123,8 +121,6 @@ tracker_namespace_manager_new () * ontologies, if they have been modified since they were installed. * * Returns: (transfer none): a global, shared #TrackerNamespaceManager instance - * - * Since: 1.10 */ TrackerNamespaceManager * tracker_namespace_manager_get_default () @@ -165,8 +161,6 @@ tracker_namespace_manager_get_default () * Returns whether @prefix is known. * * Returns: %TRUE if the #TrackerNamespaceManager knows about @prefix, %FALSE otherwise - * - * Since: 1.10 */ gboolean tracker_namespace_manager_has_prefix (TrackerNamespaceManager *self, @@ -190,8 +184,6 @@ tracker_namespace_manager_has_prefix (TrackerNamespaceManager *self, * is not known. * * Returns: a string owned by the #TrackerNamespaceManager, or %NULL - * - * Since: 1.10 */ const char * tracker_namespace_manager_lookup_prefix (TrackerNamespaceManager *self, @@ -216,8 +208,6 @@ tracker_namespace_manager_lookup_prefix (TrackerNamespaceManager *self, * * Only one prefix is allowed for a given namespace, and all prefixes must * be unique. - * - * Since: 1.10 */ void tracker_namespace_manager_add_prefix (TrackerNamespaceManager *self, @@ -264,8 +254,6 @@ tracker_namespace_manager_add_prefix (TrackerNamespaceManager *self, * expanded URI. Otherwise, a copy of @compact_uri will be returned. * * Returns: a newly-allocated string - * - * Since: 1.10 */ char * tracker_namespace_manager_expand_uri (TrackerNamespaceManager *self, @@ -307,8 +295,6 @@ tracker_namespace_manager_expand_uri (TrackerNamespaceManager *self, * Writes out all namespaces as Turtle @prefix statements. * * Returns: a newly-allocated string - * - * Since: 1.10 */ char * tracker_namespace_manager_print_turtle (TrackerNamespaceManager *self) @@ -338,8 +324,6 @@ tracker_namespace_manager_print_turtle (TrackerNamespaceManager *self) * @user_data: user data to pass to the function * * Calls @func for each known prefix / URI pair. - * - * Since: 1.10 */ void tracker_namespace_manager_foreach (TrackerNamespaceManager *self, diff --git a/src/libtracker-sparql/tracker-notifier.c b/src/libtracker-sparql/tracker-notifier.c index 38e6897fe..cf1c9a9dd 100644 --- a/src/libtracker-sparql/tracker-notifier.c +++ b/src/libtracker-sparql/tracker-notifier.c @@ -883,8 +883,6 @@ _tracker_notifier_get_connection (TrackerNotifier *notifier) * Returns the event type. * * Returns: The event type - * - * Since: 1.12 **/ TrackerNotifierEventType tracker_notifier_event_get_event_type (TrackerNotifierEvent *event) @@ -901,8 +899,6 @@ tracker_notifier_event_get_event_type (TrackerNotifierEvent *event) * which is used as efficient internal identifier for the resource. * * Returns: the resource ID - * - * Since: 1.12 **/ gint64 tracker_notifier_event_get_id (TrackerNotifierEvent *event) @@ -922,8 +918,6 @@ tracker_notifier_event_get_id (TrackerNotifierEvent *event) * notified upon, typically of the form "urn:uuid:...". * * Returns: The element URN - * - * Since: 1.12 **/ const gchar * tracker_notifier_event_get_urn (TrackerNotifierEvent *event) diff --git a/src/libtracker-sparql/tracker-private.h b/src/libtracker-sparql/tracker-private.h index d91f6e7f4..6c696d4a7 100644 --- a/src/libtracker-sparql/tracker-private.h +++ b/src/libtracker-sparql/tracker-private.h @@ -20,8 +20,9 @@ #ifndef __TRACKER_PRIVATE_H__ #define __TRACKER_PRIVATE_H__ -#include "tracker-cursor.h" -#include "tracker-endpoint-dbus.h" +#include <libtracker-sparql/tracker-version-generated.h> +#include <libtracker-sparql/tracker-cursor.h> +#include <libtracker-sparql/tracker-endpoint-dbus.h> typedef struct _TrackerSparqlConnectionClass TrackerSparqlConnectionClass; @@ -89,6 +90,22 @@ struct _TrackerSparqlConnectionClass gboolean (* close_finish) (TrackerSparqlConnection *connection, GAsyncResult *res, GError **error); + + gboolean (* update_resource) (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GError **error); + void (* update_resource_async) (TrackerSparqlConnection *connection, + const gchar *graph, + TrackerResource *resource, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* update_resource_finish) (TrackerSparqlConnection *connection, + GAsyncResult *res, + GError **error); + TrackerBatch * (* create_batch) (TrackerSparqlConnection *connection); }; typedef struct _TrackerSparqlCursorClass TrackerSparqlCursorClass; @@ -211,6 +228,28 @@ struct _TrackerNotifierClass { const GPtrArray *events); }; +typedef struct _TrackerBatchClass TrackerBatchClass; + +struct _TrackerBatchClass { + GObjectClass parent_class; + + void (* add_sparql) (TrackerBatch *batch, + const gchar *sparql); + void (* add_resource) (TrackerBatch *batch, + const gchar *graph, + TrackerResource *resource); + gboolean (* execute) (TrackerBatch *batch, + GCancellable *cancellable, + GError **error); + void (* execute_async) (TrackerBatch *batch, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* execute_finish) (TrackerBatch *batch, + GAsyncResult *res, + GError **error); +}; + void tracker_sparql_cursor_set_connection (TrackerSparqlCursor *cursor, TrackerSparqlConnection *connection); GError * _translate_internal_error (GError *error); diff --git a/src/libtracker-sparql/tracker-resource.c b/src/libtracker-sparql/tracker-resource.c index 4c4c0fc42..5c64e24c5 100644 --- a/src/libtracker-sparql/tracker-resource.c +++ b/src/libtracker-sparql/tracker-resource.c @@ -227,8 +227,6 @@ set_property (GObject *object, * Creates a TrackerResource instance. * * Returns: a newly created #TrackerResource. Free with g_object_unref() when done - * - * Since: 1.10 */ TrackerResource * tracker_resource_new (const char *identifier) @@ -264,8 +262,6 @@ tracker_resource_new (const char *identifier) * You can pass any kind of GValue for @value, but serialization functions will * normally only be able to serialize URIs/relationships and fundamental value * types (string, int, etc.). - * - * Since: 1.10 */ void tracker_resource_set_gvalue (TrackerResource *self, @@ -363,8 +359,6 @@ validate_pointer (const void *pointer, * @value: the property object * * Sets a single-valued boolean object. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_boolean, gboolean, G_TYPE_BOOLEAN, g_value_set_boolean, validate_boolean); @@ -375,8 +369,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_boolean, gboolean, G_TYPE_BOOLEAN, * @value: the property object * * Sets a single-valued double object. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_double, double, G_TYPE_DOUBLE, g_value_set_double, validate_double); @@ -387,8 +379,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_double, double, G_TYPE_DOUBLE, g_va * @value: the property object * * Sets a single-valued integer object. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_int, int, G_TYPE_INT, g_value_set_int, validate_int); @@ -399,8 +389,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_int, int, G_TYPE_INT, g_value_set_i * @value: the property object * * Sets a single-valued integer object. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_int64, gint64, G_TYPE_INT64, g_value_set_int64, validate_int64); @@ -414,8 +402,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_int64, gint64, G_TYPE_INT64, g_valu * function produces similar RDF to tracker_resource_set_uri(), * although in this function the URI will depend on the identifier * set on @resource. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_relation, TrackerResource *, TRACKER_TYPE_RESOURCE, g_value_set_object, validate_pointer); @@ -429,8 +415,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_relation, TrackerResource *, TRACKE * function produces similar RDF to tracker_resource_set_uri(), * although in this function the URI will depend on the identifier * set on @resource. This function takes ownership of @resource. - * - * Since: 2.0 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_take_relation, TrackerResource *, TRACKER_TYPE_RESOURCE, g_value_take_object, validate_pointer); @@ -441,8 +425,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_take_relation, TrackerResource *, T * @value: the property object * * Sets a single-valued string object. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_string, const char *, G_TYPE_STRING, g_value_set_string, validate_pointer); @@ -455,8 +437,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_string, const char *, G_TYPE_STRING * Sets a single-valued resource object as a string URI. This function * produces similar RDF to tracker_resource_set_relation(), although * it requires that the URI is previously known. - * - * Since: 1.10 */ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_uri, const char *, TRACKER_TYPE_URI, g_value_set_string, validate_pointer); @@ -471,8 +451,6 @@ SET_PROPERTY_FOR_GTYPE (tracker_resource_set_uri, const char *, TRACKER_TYPE_URI * You can pass any kind of GValue for @value, but serialization functions will * normally only be able to serialize URIs/relationships and fundamental value * types (string, int, etc.). - * - * Since: 1.10 */ void tracker_resource_add_gvalue (TrackerResource *self, @@ -583,8 +561,6 @@ tracker_resource_add_gvalue (TrackerResource *self, * @value: the property object * * Adds a boolean object to a multi-valued property. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_boolean, gboolean, G_TYPE_BOOLEAN, g_value_set_boolean, validate_boolean); @@ -595,8 +571,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_boolean, gboolean, G_TYPE_BOOLEAN, * @value: the property object * * Adds a double object to a multi-valued property. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_double, double, G_TYPE_DOUBLE, g_value_set_double, validate_double); @@ -607,8 +581,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_double, double, G_TYPE_DOUBLE, g_va * @value: the property object * * Adds an integer object to a multi-valued property. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_int, int, G_TYPE_INT, g_value_set_int, validate_int); @@ -619,8 +591,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_int, int, G_TYPE_INT, g_value_set_i * @value: the property object * * Adds an integer object to a multi-valued property. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_int64, gint64, G_TYPE_INT64, g_value_set_int64, validate_int64); @@ -634,8 +604,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_int64, gint64, G_TYPE_INT64, g_valu * function produces similar RDF to tracker_resource_add_uri(), * although in this function the URI will depend on the identifier * set on @resource. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_relation, TrackerResource *, TRACKER_TYPE_RESOURCE, g_value_set_object, validate_pointer); @@ -649,8 +617,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_relation, TrackerResource *, TRACKE * function produces similar RDF to tracker_resource_add_uri(), * although in this function the URI will depend on the identifier * set on @resource. This function takes ownership of @resource. - * - * Since: 2.0 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_take_relation, TrackerResource *, TRACKER_TYPE_RESOURCE, g_value_take_object, validate_pointer); @@ -662,8 +628,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_take_relation, TrackerResource *, T * @value: the property object * * Adds a string object to a multi-valued property. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_string, const char *, G_TYPE_STRING, g_value_set_string, validate_pointer); @@ -676,8 +640,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_string, const char *, G_TYPE_STRING * Adds a resource object to a multi-valued property. This function * produces similar RDF to tracker_resource_add_relation(), although * it requires that the URI is previously known. - * - * Since: 1.10 */ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_uri, const char *, TRACKER_TYPE_URI, g_value_set_string, validate_pointer); @@ -691,8 +653,6 @@ ADD_PROPERTY_FOR_GTYPE (tracker_resource_add_uri, const char *, TRACKER_TYPE_URI * * Returns: (transfer container) (element-type GValue): a #GList of #GValue * instances. The list should be freed with g_list_free() - * - * Since: 1.10 */ GList *tracker_resource_get_values (TrackerResource *self, const char *property_uri) @@ -768,8 +728,6 @@ GList *tracker_resource_get_values (TrackerResource *self, * Returns the first boolean object previously assigned to a property. * * Returns: the first boolean object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_boolean, gboolean, G_TYPE_BOOLEAN, g_value_get_boolean, FALSE); @@ -781,8 +739,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_boolean, gboolean, G_TYPE_BOO * Returns the first double object previously assigned to a property. * * Returns: the first double object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_double, double, G_TYPE_DOUBLE, g_value_get_double, 0.0); @@ -794,8 +750,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_double, double, G_TYPE_DOUBLE * Returns the first integer object previously assigned to a property. * * Returns: the first integer object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_int, int, G_TYPE_INT, g_value_get_int, 0); @@ -807,8 +761,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_int, int, G_TYPE_INT, g_value * Returns the first integer object previously assigned to a property. * * Returns: the first integer object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_int64, gint64, G_TYPE_INT64, g_value_get_int64, 0); @@ -820,8 +772,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_int64, gint64, G_TYPE_INT64, * Returns the first resource object previously assigned to a property. * * Returns: (transfer none): the first resource object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_relation, TrackerResource *, TRACKER_TYPE_RESOURCE, g_value_get_object, NULL); @@ -833,8 +783,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_relation, TrackerResource *, * Returns the first string object previously assigned to a property. * * Returns: the first string object - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_string, const char *, G_TYPE_STRING, g_value_get_string, NULL); @@ -846,8 +794,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_string, const char *, G_TYPE_ * Returns the first resource object previously assigned to a property. * * Returns: the first resource object as an URI. - * - * Since: 1.10 */ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_uri, const char *, TRACKER_TYPE_URI, g_value_get_string, NULL); @@ -861,8 +807,6 @@ GET_PROPERTY_FOR_GTYPE (tracker_resource_get_first_uri, const char *, TRACKER_TY * SPARQL blank node identifier, such as "_:123". * * Returns: a string owned by the resource - * - * Since: 1.10 */ const char * tracker_resource_get_identifier (TrackerResource *self) @@ -888,8 +832,6 @@ tracker_resource_get_identifier (TrackerResource *self) * * If the identifier is set to NULL, a SPARQL blank node identifier such as * "_:123" is assigned to the resource. - * - * Since: 1.10 */ void tracker_resource_set_identifier (TrackerResource *self, @@ -953,8 +895,6 @@ tracker_resource_identifier_compare_func (TrackerResource *resource, * thing. * * Returns: 0 if the identifiers are the same, -1 or +1 otherwise - * - * Since: 1.10 */ gint tracker_resource_compare (TrackerResource *a, @@ -1284,8 +1224,6 @@ generate_turtle (TrackerResource *resource, * usual prefixes for all of the ontologies that Tracker ships with by default. * * Returns: a newly-allocated string - * - * Since: 1.10 */ char * tracker_resource_print_turtle (TrackerResource *self, @@ -1541,8 +1479,6 @@ generate_sparql_insert_pattern (TrackerResource *resource, * usual prefixes for all of the ontologies that Tracker ships with by default. * * Returns: a newly-allocated string containing a SPARQL update command. - * - * Since: 1.10 */ char * tracker_resource_print_sparql_update (TrackerResource *resource, @@ -1731,8 +1667,6 @@ generate_jsonld_namespace_mapping_foreach (gpointer key, * usual prefixes for all of the ontologies that Tracker ships with by default. * * Returns: a newly-allocated string containing JSON-LD data. - * - * Since: 2.0.5 */ char * tracker_resource_print_jsonld (TrackerResource *self, @@ -1819,8 +1753,6 @@ tracker_serialize_single_value (TrackerResource *resource, * * Returns: (transfer full): A variant describing the resource, * the reference is floating. - * - * Since: 3.0 **/ GVariant * tracker_resource_serialize (TrackerResource *resource) @@ -1898,8 +1830,6 @@ tracker_resource_serialize (TrackerResource *resource) * * Returns: (transfer full): A TrackerResource, or %NULL if * deserialization fails. - * - * Since: 3.0 **/ TrackerResource * tracker_resource_deserialize (GVariant *variant) @@ -2010,3 +1940,24 @@ tracker_resource_deserialize (GVariant *variant) return resource; } + +/** + * tracker_resource_get_property_overwrite: + * @resource: a #TrackerResource + * @property_uri: a string identifying the property to query + * + * Returns whether the prior values for this property would be deleted + * in the SPARQL issued by @resource. + * + * Returns: #TRUE if the property would be overwritten + * + * Since: 3.1 + **/ +gboolean +tracker_resource_get_property_overwrite (TrackerResource *resource, + const gchar *property_uri) +{ + TrackerResourcePrivate *priv = GET_PRIVATE (resource); + + return g_hash_table_contains (priv->overwrite, property_uri); +} diff --git a/src/libtracker-sparql/tracker-resource.h b/src/libtracker-sparql/tracker-resource.h index 171834449..f975c757c 100644 --- a/src/libtracker-sparql/tracker-resource.h +++ b/src/libtracker-sparql/tracker-resource.h @@ -114,6 +114,9 @@ GVariant * tracker_resource_serialize (TrackerResource *resource); TRACKER_AVAILABLE_IN_ALL TrackerResource * tracker_resource_deserialize (GVariant *variant); +TRACKER_AVAILABLE_IN_3_1 +gboolean tracker_resource_get_property_overwrite (TrackerResource *resource, const gchar *property_uri); + G_END_DECLS #endif /* __LIBTRACKER_RESOURCE_H__ */ diff --git a/src/libtracker-sparql/tracker-sparql.h b/src/libtracker-sparql/tracker-sparql.h index 60432ff6a..cb6309a0d 100644 --- a/src/libtracker-sparql/tracker-sparql.h +++ b/src/libtracker-sparql/tracker-sparql.h @@ -22,9 +22,11 @@ #define __LIBTRACKER_SPARQL_INSIDE__ +#include <libtracker-sparql/tracker-version-generated.h> #include <libtracker-sparql/tracker-version.h> #include <libtracker-sparql/tracker-error.h> #include <libtracker-sparql/tracker-connection.h> +#include <libtracker-sparql/tracker-batch.h> #include <libtracker-sparql/tracker-cursor.h> #include <libtracker-sparql/tracker-endpoint.h> #include <libtracker-sparql/tracker-endpoint-dbus.h> diff --git a/src/libtracker-sparql/tracker-uri.c b/src/libtracker-sparql/tracker-uri.c index b97f84527..3f01256b0 100644 --- a/src/libtracker-sparql/tracker-uri.c +++ b/src/libtracker-sparql/tracker-uri.c @@ -161,8 +161,6 @@ find_conversion (const char *format, * * Returns: (transfer full): a newly-allocated string holding the result. The returned string * should be freed with g_free() when no longer needed. - * - * Since: 0.10 */ gchar * tracker_sparql_escape_uri_vprintf (const gchar *format, @@ -265,8 +263,6 @@ cleanup: * * Returns: (transfer full): a newly-allocated string holding the result.The returned string * should be freed with g_free() when no longer needed. - * - * Since: 0.10 */ gchar * tracker_sparql_escape_uri_printf (const gchar *format, ...) @@ -289,8 +285,6 @@ tracker_sparql_escape_uri_printf (const gchar *format, ...) * * Returns: (transfer full): a newly-allocated string holding the result. The returned string * should be freed with g_free() when no longer needed. - * - * Since: 1.12 */ gchar * tracker_sparql_escape_uri (const gchar *uri) diff --git a/src/libtracker-sparql/tracker-version-generated.h.meson.in b/src/libtracker-sparql/tracker-version-generated.h.meson.in new file mode 100644 index 000000000..6bff961b1 --- /dev/null +++ b/src/libtracker-sparql/tracker-version-generated.h.meson.in @@ -0,0 +1,28 @@ +/* Generated by meson */ + +/** + * TRACKER_MAJOR_VERSION: + * + * The major version of the Tracker library. + * + * Like #tracker_major_version, but intended to be used at application compile time. + */ +#mesondefine TRACKER_MAJOR_VERSION + +/** + * TRACKER_MINOR_VERSION: + * + * The minor version of the Tracker library. + * + * Like #tracker_minor_version, but intended to be used at application compile time. + */ +#mesondefine TRACKER_MINOR_VERSION + +/** + * TRACKER_MICRO_VERSION: + * + * The micro version of the Tracker library. + * + * Like #tracker_micro_version, but intended to be used at application compile time. + */ +#mesondefine TRACKER_MICRO_VERSION diff --git a/src/libtracker-sparql/tracker-version.c b/src/libtracker-sparql/tracker-version.c index 6a71facab..7fd829482 100644 --- a/src/libtracker-sparql/tracker-version.c +++ b/src/libtracker-sparql/tracker-version.c @@ -66,8 +66,6 @@ const guint tracker_binary_age = TRACKER_BINARY_AGE; * given version, or a string describing the version mismatch. * The returned string is owned by Tracker and must not be modified * or freed. - * - * Since: 0.10 **/ const gchar * tracker_check_version (guint required_major, diff --git a/src/libtracker-sparql/tracker-version.h b/src/libtracker-sparql/tracker-version.h index fe18f24ef..cb730fbc9 100644 --- a/src/libtracker-sparql/tracker-version.h +++ b/src/libtracker-sparql/tracker-version.h @@ -33,8 +33,42 @@ G_BEGIN_DECLS #define _TRACKER_EXTERN __attribute__((visibility("default"))) extern #endif +#define _TRACKER_UNAVAILABLE(maj, min) G_UNAVAILABLE(maj, min) _TRACKER_EXTERN +#define _TRACKER_DEPRECATED G_DEPRECATED _TRACKER_EXTERN +#define _TRACKER_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _TRACKER_EXTERN + +#define TRACKER_VERSION_3_0 G_ENCODE_VERSION (3, 0) +#define TRACKER_VERSION_3_1 G_ENCODE_VERSION (3, 1) +#define TRACKER_VERSION_CUR G_ENCODE_VERSION (TRACKER_MAJOR_VERSION, TRACKER_MINOR_VERSION) + +#ifndef TRACKER_VERSION_MIN_REQUIRED +#define TRACKER_VERSION_MIN_REQUIRED TRACKER_VERSION_CUR +#endif + +#ifndef TRACKER_VERSION_MAX_ALLOWED +#define TRACKER_VERSION_MAX_ALLOWED TRACKER_VERSION_CUR +#endif + +#if TRACKER_VERSION_MIN_REQUIRED > TRACKER_VERSION_MAX_ALLOWED +#error "TRACKER_VERSION_MAX_ALLOWED must be >= TRACKER_VERSION_MIN_REQUIRED" +#endif + #define TRACKER_AVAILABLE_IN_ALL _TRACKER_EXTERN +#if TRACKER_VERSION_MIN_REQUIRED >= TRACKER_VERSION_3_1 +#define TRACKER_DEPRECATED_IN_3_1 _TRACKER_DEPRECATED +#define TRACKER_DEPRECATED_IN_3_1_FOR(f) _TRACKER_DEPRECATED_FOR(f) +#else +#define TRACKER_DEPRECATED_IN_3_1 _TRACKER_EXTERN +#define TRACKER_DEPRECATED_IN_3_1_FOR(f) _TRACKER_EXTERN +#endif + +#if TRACKER_VERSION_MAX_ALLOWED < TRACKER_VERSION_3_1 +#define TRACKER_AVAILABLE_IN_3_1 _TRACKER_UNAVAILABLE(3, 1) +#else +#define TRACKER_AVAILABLE_IN_3_1 _TRACKER_EXTERN +#endif + /** * tracker_major_version: * @@ -101,8 +135,6 @@ GLIB_VAR const guint tracker_binary_age; * } * </programlisting> * </example> - * - * Since: 0.10 **/ #define TRACKER_CHECK_VERSION(major,minor,micro) \ (TRACKER_MAJOR_VERSION > (major) || \ |