summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2020-11-18 02:12:33 +0100
committerCarlos Garnacho <carlosg@gnome.org>2020-11-27 00:14:06 +0100
commitcb98b481a0c6ac38c16fc0da716cadeadd777f89 (patch)
treea9cb22f735530fb633f38e4ff87b0a9661b7acb3
parent78649b06021427add9d3cbe0fbdc7648398e2b8f (diff)
downloadtracker-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.xml1
-rw-r--r--docs/reference/libtracker-sparql/libtracker-sparql-sections.txt22
-rw-r--r--docs/reference/libtracker-sparql/libtracker-sparql.types1
-rw-r--r--src/libtracker-data/tracker-data-update.c4
-rw-r--r--src/libtracker-sparql/direct/meson.build1
-rw-r--r--src/libtracker-sparql/direct/tracker-direct-batch.c255
-rw-r--r--src/libtracker-sparql/direct/tracker-direct-batch.h45
-rw-r--r--src/libtracker-sparql/direct/tracker-direct.c83
-rw-r--r--src/libtracker-sparql/direct/tracker-direct.h16
-rw-r--r--src/libtracker-sparql/meson.build2
-rw-r--r--src/libtracker-sparql/tracker-batch.c293
-rw-r--r--src/libtracker-sparql/tracker-batch.h70
-rw-r--r--src/libtracker-sparql/tracker-connection.c20
-rw-r--r--src/libtracker-sparql/tracker-connection.h4
-rw-r--r--src/libtracker-sparql/tracker-private.h23
-rw-r--r--src/libtracker-sparql/tracker-sparql.h1
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>