[tracker/wip/carlosg/batches-and-resources: 6/7] libtracker-sparql: Add TrackerBatch API




commit cb98b481a0c6ac38c16fc0da716cadeadd777f89
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Nov 18 02:12:33 2020 +0100

    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.

 .../libtracker-sparql/libtracker-sparql-docs.xml   |   1 +
 .../libtracker-sparql-sections.txt                 |  22 ++
 .../libtracker-sparql/libtracker-sparql.types      |   1 +
 src/libtracker-data/tracker-data-update.c          |   4 +
 src/libtracker-sparql/direct/meson.build           |   1 +
 .../direct/tracker-direct-batch.c                  | 255 ++++++++++++++++++
 .../direct/tracker-direct-batch.h                  |  45 ++++
 src/libtracker-sparql/direct/tracker-direct.c      |  83 ++++++
 src/libtracker-sparql/direct/tracker-direct.h      |  16 +-
 src/libtracker-sparql/meson.build                  |   2 +
 src/libtracker-sparql/tracker-batch.c              | 293 +++++++++++++++++++++
 src/libtracker-sparql/tracker-batch.h              |  70 +++++
 src/libtracker-sparql/tracker-connection.c         |  20 ++
 src/libtracker-sparql/tracker-connection.h         |   4 +
 src/libtracker-sparql/tracker-private.h            |  23 ++
 src/libtracker-sparql/tracker-sparql.h             |   1 +
 16 files changed, 840 insertions(+), 1 deletion(-)
---
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
@@ -267,6 +268,27 @@ TrackerEndpointDBusClass
 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>
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>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]