Coverage Report

Created: 2026-02-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/src/libtinysparql/direct/tracker-direct-batch.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2020, Red Hat, Inc.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, write to the
16
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 * Boston, MA  02110-1301, USA.
18
 *
19
 * Author: Carlos Garnacho <carlosg@gnome.org>
20
 */
21
22
#include "config.h"
23
24
#include "core/tracker-data.h"
25
26
#include "direct/tracker-direct-batch.h"
27
#include "direct/tracker-direct-statement.h"
28
#include "direct/tracker-direct.h"
29
30
#include "tracker-private.h"
31
32
typedef struct _TrackerDirectBatchPrivate TrackerDirectBatchPrivate;
33
typedef struct _TrackerBatchElem TrackerBatchElem;
34
35
struct _TrackerBatchElem
36
{
37
  guint type;
38
39
  union {
40
    gchar *sparql;
41
42
    struct {
43
      gchar *graph;
44
      TrackerResource *resource;
45
    } resource;
46
47
    struct {
48
      TrackerSparqlStatement *stmt;
49
      GHashTable *parameters;
50
    } statement;
51
52
    struct {
53
      TrackerDeserializeFlags flags;
54
      TrackerRdfFormat format;
55
      gchar *default_graph;
56
      GInputStream *stream;
57
    } rdf;
58
59
    GInputStream *dbus_fd;
60
  } d;
61
};
62
63
struct _TrackerDirectBatchPrivate
64
{
65
  GArray *array;
66
};
67
68
enum {
69
  TRACKER_DIRECT_BATCH_RESOURCE,
70
  TRACKER_DIRECT_BATCH_SPARQL,
71
  TRACKER_DIRECT_BATCH_STATEMENT,
72
  TRACKER_DIRECT_BATCH_RDF,
73
  TRACKER_DIRECT_BATCH_DBUS_FD,
74
};
75
76
0
G_DEFINE_TYPE_WITH_PRIVATE (TrackerDirectBatch,
77
0
                            tracker_direct_batch,
78
0
                            TRACKER_TYPE_BATCH)
79
0
80
0
static TrackerSerializerFormat
81
0
convert_format (TrackerRdfFormat format)
82
0
{
83
0
  switch (format) {
84
0
  case TRACKER_RDF_FORMAT_TURTLE:
85
0
    return TRACKER_SERIALIZER_FORMAT_TTL;
86
0
  case TRACKER_RDF_FORMAT_TRIG:
87
0
    return TRACKER_SERIALIZER_FORMAT_TRIG;
88
0
  case TRACKER_RDF_FORMAT_JSON_LD:
89
0
    return TRACKER_SERIALIZER_FORMAT_JSON_LD;
90
0
  default:
91
0
    g_assert_not_reached ();
92
0
  }
93
0
}
94
95
static void
96
tracker_direct_batch_finalize (GObject *object)
97
0
{
98
0
  TrackerDirectBatchPrivate *priv;
99
100
0
  priv = tracker_direct_batch_get_instance_private (TRACKER_DIRECT_BATCH (object));
101
0
  g_array_unref (priv->array);
102
103
0
  G_OBJECT_CLASS (tracker_direct_batch_parent_class)->finalize (object);
104
0
}
105
106
static void
107
tracker_direct_batch_add_sparql (TrackerBatch *batch,
108
                                 const gchar  *sparql)
109
0
{
110
0
  TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch);
111
0
  TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct);
112
0
  TrackerBatchElem elem;
113
114
0
  elem.type = TRACKER_DIRECT_BATCH_SPARQL;
115
0
  elem.d.sparql = g_strdup (sparql);
116
0
  g_array_append_val (priv->array, elem);
117
0
}
118
119
static void
120
tracker_direct_batch_add_resource (TrackerBatch    *batch,
121
                                   const gchar     *graph,
122
                                   TrackerResource *resource)
123
0
{
124
0
  TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch);
125
0
  TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct);
126
0
  TrackerBatchElem elem;
127
128
0
  elem.type = TRACKER_DIRECT_BATCH_RESOURCE;
129
0
  elem.d.resource.graph = g_strdup (graph);
130
0
  elem.d.resource.resource = g_object_ref (resource);
131
0
  g_array_append_val (priv->array, elem);
132
0
}
133
134
static void
135
free_value (gpointer data)
136
0
{
137
0
  GValue *value = data;
138
139
0
  g_value_unset (value);
140
0
  g_free (value);
141
0
}
142
143
static void
144
tracker_direct_batch_add_statement (TrackerBatch           *batch,
145
                                    TrackerSparqlStatement *stmt,
146
                                    guint                   n_values,
147
                                    const gchar            *binding_names[],
148
                                    const GValue            bindings[])
149
0
{
150
0
  TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch);
151
0
  TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct);
152
0
  TrackerBatchElem elem;
153
0
  GHashTable *parameters = NULL;
154
0
  guint i;
155
156
0
  for (i = 0; i < n_values; i++) {
157
0
    GValue *copy;
158
159
0
    if (!parameters) {
160
0
      parameters = g_hash_table_new_full (g_str_hash,
161
0
                                          g_str_equal,
162
0
                                          g_free,
163
0
                                          (GDestroyNotify) free_value);
164
0
    }
165
166
0
    copy = g_new0 (GValue, 1);
167
0
    g_value_init (copy, G_VALUE_TYPE (&bindings[i]));
168
0
    g_value_copy (&bindings[i], copy);
169
0
    g_hash_table_insert (parameters,
170
0
                         g_strdup (binding_names[i]),
171
0
                         copy);
172
0
  }
173
174
0
  elem.type = TRACKER_DIRECT_BATCH_STATEMENT;
175
0
  elem.d.statement.stmt = g_object_ref (stmt);
176
0
  elem.d.statement.parameters = parameters;
177
0
  g_array_append_val (priv->array, elem);
178
0
}
179
180
void
181
tracker_direct_batch_add_rdf (TrackerBatch            *batch,
182
                              TrackerDeserializeFlags  flags,
183
                              TrackerRdfFormat         format,
184
                              const gchar             *default_graph,
185
                              GInputStream            *stream)
186
0
{
187
0
  TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch);
188
0
  TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct);
189
0
  TrackerBatchElem elem;
190
191
0
  elem.type = TRACKER_DIRECT_BATCH_RDF;
192
0
  elem.d.rdf.default_graph = g_strdup (default_graph);
193
0
  elem.d.rdf.format = format;
194
0
  elem.d.rdf.flags = flags;
195
0
  elem.d.rdf.stream = g_object_ref (stream);
196
0
  g_array_append_val (priv->array, elem);
197
0
}
198
199
void
200
tracker_direct_batch_add_dbus_fd (TrackerBatch *batch,
201
                                  GInputStream *istream)
202
0
{
203
0
  TrackerDirectBatch *direct = TRACKER_DIRECT_BATCH (batch);
204
0
  TrackerDirectBatchPrivate *priv = tracker_direct_batch_get_instance_private (direct);
205
0
  TrackerBatchElem elem;
206
207
0
  elem.type = TRACKER_DIRECT_BATCH_DBUS_FD;
208
0
  elem.d.dbus_fd = g_object_ref (istream);
209
0
  g_array_append_val (priv->array, elem);
210
0
}
211
212
static gboolean
213
tracker_direct_batch_execute (TrackerBatch  *batch,
214
                              GCancellable  *cancellable,
215
                              GError       **error)
216
0
{
217
0
  TrackerDirectConnection *conn;
218
219
0
  conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch));
220
221
0
  return tracker_direct_connection_update_batch (conn, batch, error);
222
0
}
223
224
static void
225
tracker_direct_batch_execute_async (TrackerBatch        *batch,
226
                                    GCancellable        *cancellable,
227
                                    GAsyncReadyCallback  callback,
228
                                    gpointer             user_data)
229
0
{
230
0
  TrackerDirectConnection *conn;
231
232
0
  conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch));
233
234
0
  tracker_direct_connection_update_batch_async (conn, batch,
235
0
                                                cancellable,
236
0
                                                callback,
237
0
                                                user_data);
238
0
}
239
240
static gboolean
241
tracker_direct_batch_execute_finish (TrackerBatch  *batch,
242
                                     GAsyncResult  *res,
243
                                     GError       **error)
244
0
{
245
0
  TrackerDirectConnection *conn;
246
247
0
  conn = TRACKER_DIRECT_CONNECTION (tracker_batch_get_connection (batch));
248
249
0
  return tracker_direct_connection_update_batch_finish (conn, res, error);
250
0
}
251
252
static void
253
tracker_direct_batch_class_init (TrackerDirectBatchClass *klass)
254
0
{
255
0
  TrackerBatchClass *batch_class = (TrackerBatchClass *) klass;
256
0
  GObjectClass *object_class = (GObjectClass *) klass;
257
258
0
  object_class->finalize = tracker_direct_batch_finalize;
259
260
0
  batch_class->add_sparql = tracker_direct_batch_add_sparql;
261
0
  batch_class->add_resource = tracker_direct_batch_add_resource;
262
0
  batch_class->add_statement = tracker_direct_batch_add_statement;
263
0
  batch_class->add_rdf = tracker_direct_batch_add_rdf;
264
0
  batch_class->add_dbus_fd = tracker_direct_batch_add_dbus_fd;
265
0
  batch_class->execute = tracker_direct_batch_execute;
266
0
  batch_class->execute_async = tracker_direct_batch_execute_async;
267
0
  batch_class->execute_finish = tracker_direct_batch_execute_finish;
268
0
}
269
270
static void
271
tracker_batch_elem_clear (TrackerBatchElem *elem)
272
0
{
273
0
  if (elem->type == TRACKER_DIRECT_BATCH_RESOURCE) {
274
0
    g_object_run_dispose (G_OBJECT (elem->d.resource.resource));
275
0
    g_object_unref (elem->d.resource.resource);
276
0
    g_free (elem->d.resource.graph);
277
0
  } else if (elem->type == TRACKER_DIRECT_BATCH_STATEMENT) {
278
0
    g_object_unref (elem->d.statement.stmt);
279
0
    g_clear_pointer (&elem->d.statement.parameters,
280
0
                     g_hash_table_unref);
281
0
  } else if (elem->type == TRACKER_DIRECT_BATCH_SPARQL) {
282
0
    g_free (elem->d.sparql);
283
0
  } else if (elem->type == TRACKER_DIRECT_BATCH_RDF) {
284
0
    g_free (elem->d.rdf.default_graph);
285
0
    g_clear_object (&elem->d.rdf.stream);
286
0
  } else if (elem->type == TRACKER_DIRECT_BATCH_DBUS_FD) {
287
0
    g_clear_object (&elem->d.dbus_fd);
288
0
  }
289
0
}
290
291
static void
292
tracker_direct_batch_init (TrackerDirectBatch *batch)
293
0
{
294
0
  TrackerDirectBatchPrivate *priv;
295
296
0
  priv = tracker_direct_batch_get_instance_private (batch);
297
0
  priv->array = g_array_new (FALSE, FALSE, sizeof (TrackerBatchElem));
298
0
  g_array_set_clear_func (priv->array, (GDestroyNotify) tracker_batch_elem_clear);
299
0
}
300
301
TrackerBatch *
302
tracker_direct_batch_new (TrackerSparqlConnection *conn)
303
0
{
304
0
  return g_object_new (TRACKER_TYPE_DIRECT_BATCH,
305
0
                       "connection", conn,
306
0
                       NULL);
307
0
}
308
309
/* Executes with the update lock held */
310
gboolean
311
tracker_direct_batch_update (TrackerDirectBatch  *batch,
312
                             TrackerDataManager  *data_manager,
313
                             GError             **error)
314
0
{
315
0
  TrackerDirectBatchPrivate *priv;
316
0
  GError *inner_error = NULL;
317
0
  GHashTable *bnodes, *visited;
318
0
  TrackerData *data;
319
0
  const gchar *last_graph = NULL;
320
0
  guint i;
321
322
0
  priv = tracker_direct_batch_get_instance_private (batch);
323
0
  data = tracker_data_manager_get_data (data_manager);
324
0
  bnodes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
325
0
                                  (GDestroyNotify) tracker_rowid_free);
326
0
  visited = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) tracker_rowid_free);
327
328
0
  if (!tracker_data_begin_transaction (data, &inner_error))
329
0
    goto error;
330
331
0
  for (i = 0; i < priv->array->len; i++) {
332
0
    TrackerBatchElem *elem;
333
334
0
    elem = &g_array_index (priv->array, TrackerBatchElem, i);
335
336
0
    if (elem->type == TRACKER_DIRECT_BATCH_RESOURCE) {
337
      /* Clear the visited resources set on graph changes, there
338
       * might be resources that are referenced from multiple
339
       * graphs.
340
       */
341
0
      if (g_strcmp0 (last_graph, elem->d.resource.graph) != 0)
342
0
        g_hash_table_remove_all (visited);
343
344
0
      tracker_data_update_resource (data,
345
0
                                    elem->d.resource.graph,
346
0
                                    elem->d.resource.resource,
347
0
                                    bnodes,
348
0
                                    visited,
349
0
                                    &inner_error);
350
0
      last_graph = elem->d.resource.graph;
351
0
    } else if (elem->type == TRACKER_DIRECT_BATCH_SPARQL) {
352
0
      TrackerSparql *query;
353
354
0
      query = tracker_sparql_new_update (data_manager,
355
0
                                         elem->d.sparql,
356
0
                                         &inner_error);
357
0
      if (query) {
358
0
        tracker_sparql_execute_update (query,
359
0
                                       NULL,
360
0
                                       bnodes,
361
0
                                       NULL,
362
0
                                       &inner_error);
363
0
        g_object_unref (query);
364
0
      }
365
0
    } else if (elem->type == TRACKER_DIRECT_BATCH_STATEMENT) {
366
0
      tracker_direct_statement_execute_update (elem->d.statement.stmt,
367
0
                                               elem->d.statement.parameters,
368
0
                                               bnodes,
369
0
                                               &inner_error);
370
0
    } else if (elem->type == TRACKER_DIRECT_BATCH_RDF) {
371
0
      TrackerSparqlCursor *deserializer;
372
373
0
      deserializer = tracker_deserializer_new (elem->d.rdf.stream,
374
0
                                               NULL,
375
0
                                               convert_format (elem->d.rdf.format));
376
377
0
      tracker_data_load_from_deserializer (data,
378
0
                                           TRACKER_DESERIALIZER (deserializer),
379
0
                                           elem->d.rdf.default_graph,
380
0
                                           bnodes,
381
0
                                           &inner_error);
382
0
      g_object_unref (deserializer);
383
0
    } else if (elem->type == TRACKER_DIRECT_BATCH_DBUS_FD) {
384
0
      tracker_data_load_from_dbus_fd (data, elem->d.dbus_fd,
385
0
                                      bnodes, NULL,
386
0
                                      &inner_error);
387
0
    } else {
388
0
      g_assert_not_reached ();
389
0
    }
390
391
0
    if (inner_error)
392
0
      break;
393
0
  }
394
395
0
  if (inner_error) {
396
0
    tracker_data_rollback_transaction (data);
397
0
    goto error;
398
0
  }
399
400
0
  if (!tracker_data_commit_transaction (data, &inner_error))
401
0
    goto error;
402
403
0
  g_array_set_size (priv->array, 0);
404
0
  g_hash_table_unref (bnodes);
405
0
  g_hash_table_unref (visited);
406
407
0
  return TRUE;
408
409
0
error:
410
0
  g_hash_table_unref (bnodes);
411
0
  g_hash_table_unref (visited);
412
0
  g_propagate_error (error, inner_error);
413
0
  return FALSE;
414
0
}