diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2020-11-18 02:12:33 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2020-11-27 00:14:06 +0100 |
commit | cb98b481a0c6ac38c16fc0da716cadeadd777f89 (patch) | |
tree | a9cb22f735530fb633f38e4ff87b0a9661b7acb3 | |
parent | 78649b06021427add9d3cbe0fbdc7648398e2b8f (diff) | |
download | tracker-cb98b481a0c6ac38c16fc0da716cadeadd777f89.tar.gz |
libtracker-sparql: Add TrackerBatch API
This API allows pushing a series of update commands, in either SPARQL or
TrackerResource form. This is committed as a single transaction.
-rw-r--r-- | docs/reference/libtracker-sparql/libtracker-sparql-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/libtracker-sparql/libtracker-sparql-sections.txt | 22 | ||||
-rw-r--r-- | docs/reference/libtracker-sparql/libtracker-sparql.types | 1 | ||||
-rw-r--r-- | src/libtracker-data/tracker-data-update.c | 4 | ||||
-rw-r--r-- | src/libtracker-sparql/direct/meson.build | 1 | ||||
-rw-r--r-- | src/libtracker-sparql/direct/tracker-direct-batch.c | 255 | ||||
-rw-r--r-- | src/libtracker-sparql/direct/tracker-direct-batch.h | 45 | ||||
-rw-r--r-- | src/libtracker-sparql/direct/tracker-direct.c | 83 | ||||
-rw-r--r-- | src/libtracker-sparql/direct/tracker-direct.h | 16 | ||||
-rw-r--r-- | src/libtracker-sparql/meson.build | 2 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-batch.c | 293 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-batch.h | 70 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-connection.c | 20 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-connection.h | 4 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-private.h | 23 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-sparql.h | 1 |
16 files changed, 840 insertions, 1 deletions
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml b/docs/reference/libtracker-sparql/libtracker-sparql-docs.xml index 792f309e7..b22943d68 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"/> diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt index 39c373881..93d73f2b4 100644 --- a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt +++ b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt @@ -123,6 +123,7 @@ 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 @@ -268,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 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-update.c b/src/libtracker-data/tracker-data-update.c index 8883a7f6b..378f434bc 100644 --- a/src/libtracker-data/tracker-data-update.c +++ b/src/libtracker-data/tracker-data-update.c @@ -3065,6 +3065,7 @@ update_resource_single (TrackerData *data, &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", @@ -3078,6 +3079,9 @@ update_resource_single (TrackerData *data, } 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, 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 bd5790895..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> @@ -74,6 +75,7 @@ typedef enum { TASK_TYPE_UPDATE, TASK_TYPE_UPDATE_BLANK, TASK_TYPE_UPDATE_RESOURCE, + TASK_TYPE_UPDATE_BATCH, TASK_TYPE_RELEASE_MEMORY, } TaskType; @@ -219,6 +221,9 @@ update_thread_func (gpointer 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; @@ -1156,6 +1161,21 @@ tracker_direct_connection_update_resource_finish (TrackerSparqlConnection *conn 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) { @@ -1189,6 +1209,7 @@ tracker_direct_connection_class_init (TrackerDirectConnectionClass *klass) 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", @@ -1250,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 073a62162..2bb6ca8a6 100644 --- a/src/libtracker-sparql/meson.build +++ b/src/libtracker-sparql/meson.build @@ -15,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', @@ -30,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', 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 10cb9a425..74cff7fc7 100644 --- a/src/libtracker-sparql/tracker-connection.c +++ b/src/libtracker-sparql/tracker-connection.c @@ -769,3 +769,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 0a08782f3..834f65fb6 100644 --- a/src/libtracker-sparql/tracker-connection.h +++ b/src/libtracker-sparql/tracker-connection.h @@ -64,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" @@ -154,6 +155,9 @@ 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-private.h b/src/libtracker-sparql/tracker-private.h index 87662361b..6c696d4a7 100644 --- a/src/libtracker-sparql/tracker-private.h +++ b/src/libtracker-sparql/tracker-private.h @@ -105,6 +105,7 @@ struct _TrackerSparqlConnectionClass gboolean (* update_resource_finish) (TrackerSparqlConnection *connection, GAsyncResult *res, GError **error); + TrackerBatch * (* create_batch) (TrackerSparqlConnection *connection); }; typedef struct _TrackerSparqlCursorClass TrackerSparqlCursorClass; @@ -227,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-sparql.h b/src/libtracker-sparql/tracker-sparql.h index a9f3badad..cb6309a0d 100644 --- a/src/libtracker-sparql/tracker-sparql.h +++ b/src/libtracker-sparql/tracker-sparql.h @@ -26,6 +26,7 @@ #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> |