Coverage Report

Created: 2025-12-14 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/src/libtinysparql/tracker-batch.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2020, Red Hat Ltd.
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
 * TrackerBatch:
23
 *
24
 * `TrackerBatch` executes a series of SPARQL updates and RDF data
25
 * insertions within a transaction.
26
 *
27
 * A batch is created with [method@SparqlConnection.create_batch].
28
 * To add resources use [method@Batch.add_resource],
29
 * [method@Batch.add_sparql] or [method@Batch.add_statement].
30
 *
31
 * When a batch is ready for execution, use [method@Batch.execute]
32
 * or [method@Batch.execute_async]. The batch is executed as a single
33
 * transaction, it will succeed or fail entirely.
34
 *
35
 * This object has a single use, after the batch is executed it can
36
 * only be finished and freed.
37
 *
38
 * The mapping of blank node labels is global in a `TrackerBatch`,
39
 * referencing the same blank node label in different operations in
40
 * a batch will resolve to the same resource.
41
 *
42
 * Since: 3.1
43
 */
44
#include "config.h"
45
46
#include "tracker-batch.h"
47
#include "tracker-connection.h"
48
#include "tracker-private.h"
49
50
#include <gobject/gvaluecollector.h>
51
52
enum {
53
  PROP_0,
54
  PROP_CONNECTION,
55
  N_PROPS
56
};
57
58
static GParamSpec *props[N_PROPS];
59
60
typedef struct {
61
  TrackerSparqlConnection *connection;
62
  gchar *sparql;
63
  guint already_executed : 1;
64
} TrackerBatchPrivate;
65
66
220k
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerBatch,
67
220k
                                     tracker_batch,
68
220k
                                     G_TYPE_OBJECT)
69
220k
70
220k
static void
71
220k
tracker_batch_init (TrackerBatch *batch)
72
220k
{
73
24.4k
}
74
75
static void
76
tracker_batch_finalize (GObject *object)
77
24.4k
{
78
24.4k
  TrackerBatch *batch = TRACKER_BATCH (object);
79
24.4k
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
80
81
24.4k
  g_clear_object (&priv->connection);
82
24.4k
  G_OBJECT_CLASS (tracker_batch_parent_class)->finalize (object);
83
24.4k
}
84
85
static void
86
tracker_batch_set_property (GObject      *object,
87
                            guint         prop_id,
88
                            const GValue *value,
89
                            GParamSpec   *pspec)
90
24.4k
{
91
24.4k
  TrackerBatch *batch = TRACKER_BATCH (object);
92
24.4k
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
93
94
24.4k
  switch (prop_id) {
95
24.4k
  case PROP_CONNECTION:
96
24.4k
    priv->connection = g_value_dup_object (value);
97
24.4k
    break;
98
0
  default:
99
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
100
24.4k
  }
101
24.4k
}
102
103
static void
104
tracker_batch_get_property (GObject    *object,
105
                            guint       prop_id,
106
                            GValue     *value,
107
                            GParamSpec *pspec)
108
0
{
109
0
  TrackerBatch *batch = TRACKER_BATCH (object);
110
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
111
112
0
  switch (prop_id) {
113
0
  case PROP_CONNECTION:
114
0
    g_value_set_object (value, priv->connection);
115
0
    break;
116
0
  default:
117
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118
0
  }
119
0
}
120
121
static void
122
tracker_batch_class_init (TrackerBatchClass *klass)
123
3
{
124
3
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
125
126
3
  object_class->finalize = tracker_batch_finalize;
127
3
  object_class->set_property = tracker_batch_set_property;
128
3
  object_class->get_property = tracker_batch_get_property;
129
130
  /**
131
   * TrackerBatch:connection:
132
   *
133
   * The [class@SparqlConnection] the batch belongs to.
134
   */
135
3
  props[PROP_CONNECTION] =
136
3
    g_param_spec_object ("connection",
137
3
                         "connection",
138
3
                         "connection",
139
3
                         TRACKER_TYPE_SPARQL_CONNECTION,
140
3
                         G_PARAM_CONSTRUCT_ONLY |
141
3
                         G_PARAM_STATIC_STRINGS |
142
3
                         G_PARAM_READABLE |
143
3
                         G_PARAM_WRITABLE);
144
145
3
  g_object_class_install_properties (object_class, N_PROPS, props);
146
3
}
147
148
void
149
tracker_batch_add_dbus_fd (TrackerBatch *batch,
150
                           GInputStream *istream)
151
0
{
152
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
153
154
0
  g_return_if_fail (TRACKER_IS_BATCH (batch));
155
0
  g_return_if_fail (G_IS_INPUT_STREAM (istream));
156
0
  g_return_if_fail (!priv->already_executed);
157
158
0
  TRACKER_BATCH_GET_CLASS (batch)->add_dbus_fd (batch, istream);
159
0
}
160
161
/**
162
 * tracker_batch_get_connection:
163
 * @batch: A `TrackerBatch`
164
 *
165
 * Returns the [class@SparqlConnection] that this batch was created
166
 * from.
167
 *
168
 * Returns: (transfer none): The SPARQL connection of this batch.
169
 **/
170
TrackerSparqlConnection *
171
tracker_batch_get_connection (TrackerBatch *batch)
172
24.4k
{
173
24.4k
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
174
175
24.4k
  g_return_val_if_fail (TRACKER_IS_BATCH (batch), NULL);
176
177
24.4k
  return priv->connection;
178
24.4k
}
179
180
/**
181
 * tracker_batch_add_sparql:
182
 * @batch: A `TrackerBatch`
183
 * @sparql: A SPARQL update string
184
 *
185
 * Adds an SPARQL update string to @batch.
186
 *
187
 * Since: 3.1
188
 **/
189
void
190
tracker_batch_add_sparql (TrackerBatch *batch,
191
                          const gchar  *sparql)
192
0
{
193
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
194
195
0
  g_return_if_fail (TRACKER_IS_BATCH (batch));
196
0
  g_return_if_fail (sparql != NULL);
197
0
  g_return_if_fail (!priv->already_executed);
198
199
0
  TRACKER_BATCH_GET_CLASS (batch)->add_sparql (batch, sparql);
200
0
}
201
202
/**
203
 * tracker_batch_add_resource:
204
 * @batch: A `TrackerBatch`
205
 * @graph: (nullable): RDF graph to insert the resource to
206
 * @resource: A [class@Resource]
207
 *
208
 * Adds the RDF represented by @resource to @batch.
209
 *
210
 * Since: 3.1
211
 **/
212
void
213
tracker_batch_add_resource (TrackerBatch    *batch,
214
                            const gchar     *graph,
215
                            TrackerResource *resource)
216
0
{
217
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
218
219
0
  g_return_if_fail (TRACKER_IS_BATCH (batch));
220
0
  g_return_if_fail (TRACKER_IS_RESOURCE (resource));
221
0
  g_return_if_fail (!priv->already_executed);
222
223
0
  TRACKER_BATCH_GET_CLASS (batch)->add_resource (batch, graph, resource);
224
0
}
225
226
/**
227
 * tracker_batch_add_statement: (skip):
228
 * @batch: a `TrackerBatch`
229
 * @stmt: a [class@SparqlStatement] containing a SPARQL update
230
 * @...: NULL-terminated list of parameters bound to @stmt, in triplets of name, type and value.
231
 *
232
 * Adds a [class@SparqlStatement] containing an SPARQL update. The statement will
233
 * be executed once in the batch, with the parameters bound as specified in the
234
 * variable arguments.
235
 *
236
 * The variable arguments are a NULL terminated set of variable name, type [type@GObject.Type],
237
 * and value. The value C type must correspond to the given [type@GObject.Type]. For example, for
238
 * a statement that has a single `~name` parameter, it could be given a value for execution
239
 * with the following code:
240
 *
241
 * ```c
242
 * tracker_batch_add_statement (batch, stmt,
243
 *                              "name", G_TYPE_STRING, "John Smith",
244
 *                              NULL);
245
 * ```
246
 *
247
 * A [class@SparqlStatement] may be used on multiple [method@Batch.add_statement]
248
 * calls with the same or different values, on the same or different `TrackerBatch`
249
 * objects.
250
 *
251
 * This function should only be called on [class@SparqlStatement] objects
252
 * obtained through [method@SparqlConnection.update_statement] or
253
 * update statements loaded through [method@SparqlConnection.load_statement_from_gresource].
254
 *
255
 * Since: 3.5
256
 **/
257
void
258
tracker_batch_add_statement (TrackerBatch           *batch,
259
                             TrackerSparqlStatement *stmt,
260
                             ...)
261
0
{
262
0
  GPtrArray *variable_names;
263
0
  GArray *values;
264
0
  va_list varargs;
265
0
  const gchar *var_name;
266
267
0
  variable_names = g_ptr_array_new ();
268
0
  g_ptr_array_set_free_func (variable_names, g_free);
269
270
0
  values = g_array_new (FALSE, TRUE, sizeof (GValue));
271
0
  g_array_set_clear_func (values, (GDestroyNotify) g_value_unset);
272
273
0
  va_start (varargs, stmt);
274
275
0
  var_name = va_arg (varargs, const gchar*);
276
277
0
  while (var_name) {
278
0
    GType var_type;
279
0
    GValue var_value = G_VALUE_INIT;
280
0
    gchar *error = NULL;
281
282
0
    var_type = va_arg (varargs, GType);
283
284
0
    G_VALUE_COLLECT_INIT (&var_value, var_type, varargs, 0, &error);
285
286
0
    if (error) {
287
0
      g_warning ("%s: %s", G_STRFUNC, error);
288
0
      g_free (error);
289
0
      goto error;
290
0
    }
291
292
0
    g_ptr_array_add (variable_names, g_strdup (var_name));
293
0
    g_array_append_val (values, var_value);
294
295
0
    var_name = va_arg (varargs, const gchar *);
296
0
  }
297
298
0
  tracker_batch_add_statementv (batch, stmt,
299
0
                                variable_names->len,
300
0
                                (const gchar **) variable_names->pdata,
301
0
                                (const GValue *) values->data);
302
0
 error:
303
0
  va_end (varargs);
304
0
  g_ptr_array_unref (variable_names);
305
0
  g_array_unref (values);
306
0
}
307
308
/**
309
 * tracker_batch_add_statementv: (rename-to tracker_batch_add_statement)
310
 * @batch: A `TrackerBatch`
311
 * @stmt: A [class@SparqlStatement] containing a SPARQL update
312
 * @n_values: The number of bound parameters
313
 * @variable_names: (array length=n_values): The names of each bound parameter
314
 * @values: (array length=n_values): The values of each bound parameter
315
 *
316
 * Adds a [class@SparqlStatement] containing an SPARQL update. The statement will
317
 * be executed once in the batch, with the values bound as specified by @variable_names
318
 * and @values.
319
 *
320
 * For example, for a statement that has a single `~name` parameter,
321
 * it could be given a value for execution with the given code:
322
 *
323
 * ```c
324
 * const char *names = { "name" };
325
 * const GValue values[G_N_ELEMENTS (names)] = { 0, };
326
 *
327
 * g_value_init (&values[0], G_TYPE_STRING);
328
 * g_value_set_string (&values[0], "John Smith");
329
 * tracker_batch_add_statementv (batch, stmt,
330
 *                               G_N_ELEMENTS (names),
331
 *                               names, values);
332
 * ```
333
 * ```python
334
 * batch.add_statement(stmt, ['name'], ['John Smith']);
335
 * ```
336
 * ```js
337
 * batch.add_statement(stmt, ['name'], ['John Smith']);
338
 * ```
339
 *
340
 * A [class@SparqlStatement] may be used on multiple [method@Batch.add_statement]
341
 * calls with the same or different values, on the same or different `TrackerBatch`
342
 * objects.
343
 *
344
 * This function should only be called on [class@SparqlStatement] objects
345
 * obtained through [method@SparqlConnection.update_statement] or
346
 * update statements loaded through [method@SparqlConnection.load_statement_from_gresource].
347
 *
348
 * Since: 3.5
349
 **/
350
void
351
tracker_batch_add_statementv (TrackerBatch           *batch,
352
                              TrackerSparqlStatement *stmt,
353
                              guint                   n_values,
354
                              const gchar            *variable_names[],
355
                              const GValue            values[])
356
0
{
357
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
358
359
0
  g_return_if_fail (TRACKER_IS_BATCH (batch));
360
0
  g_return_if_fail (TRACKER_IS_SPARQL_STATEMENT (stmt));
361
0
  g_return_if_fail (!priv->already_executed);
362
363
0
  TRACKER_BATCH_GET_CLASS (batch)->add_statement (batch, stmt,
364
0
                                                  n_values,
365
0
                                                  variable_names, values);
366
0
}
367
368
/**
369
 * tracker_batch_add_rdf:
370
 * @batch: A `TrackerBatch`
371
 * @flags: Deserialization flags
372
 * @format: RDF format of data in stream
373
 * @default_graph: Default graph that will receive the RDF data
374
 * @stream: Input stream with RDF data
375
 *
376
 * Inserts the RDF data contained in @stream as part of @batch.
377
 *
378
 * The RDF data will be inserted in the given @default_graph if one is provided,
379
 * or the anonymous graph if @default_graph is %NULL. Any RDF data that has a
380
 * graph specified (e.g. using the `GRAPH` clause in the Trig format) will
381
 * be inserted in the specified graph instead of @default_graph.
382
 *
383
 * The @flags argument is reserved for future expansions, currently
384
 * %TRACKER_DESERIALIZE_FLAGS_NONE must be passed.
385
 *
386
 * Since: 3.6
387
 **/
388
void
389
tracker_batch_add_rdf (TrackerBatch            *batch,
390
                       TrackerDeserializeFlags  flags,
391
                       TrackerRdfFormat         format,
392
                       const gchar             *default_graph,
393
                       GInputStream            *stream)
394
24.4k
{
395
24.4k
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
396
397
24.4k
  g_return_if_fail (TRACKER_IS_BATCH (batch));
398
24.4k
  g_return_if_fail (G_IS_INPUT_STREAM (stream));
399
24.4k
  g_return_if_fail (!priv->already_executed);
400
401
24.4k
  TRACKER_BATCH_GET_CLASS (batch)->add_rdf (batch,
402
24.4k
                                            flags,
403
24.4k
                                            format,
404
24.4k
                                            default_graph,
405
24.4k
                                            stream);
406
24.4k
}
407
408
/**
409
 * tracker_batch_execute:
410
 * @batch: a `TrackerBatch`
411
 * @cancellable: (nullable): Optional [type@Gio.Cancellable]
412
 * @error: Error location
413
 *
414
 * Executes the batch. This operations happens synchronously.
415
 *
416
 * Returns: %TRUE of there were no errors, %FALSE otherwise
417
 *
418
 * Since: 3.1
419
 **/
420
gboolean
421
tracker_batch_execute (TrackerBatch  *batch,
422
                       GCancellable  *cancellable,
423
                       GError       **error)
424
24.4k
{
425
24.4k
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
426
427
24.4k
  g_return_val_if_fail (TRACKER_IS_BATCH (batch), FALSE);
428
24.4k
  g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
429
24.4k
  g_return_val_if_fail (!priv->already_executed, FALSE);
430
431
24.4k
  priv->already_executed = TRUE;
432
433
24.4k
  if (tracker_sparql_connection_set_error_on_closed (priv->connection, error))
434
0
    return FALSE;
435
436
24.4k
  return TRACKER_BATCH_GET_CLASS (batch)->execute (batch, cancellable, error);
437
24.4k
}
438
439
/**
440
 * tracker_batch_execute_async:
441
 * @batch: A `TrackerBatch`
442
 * @cancellable: (nullable): Optional [type@Gio.Cancellable]
443
 * @callback: User-defined [type@Gio.AsyncReadyCallback] to be called when
444
 *            the asynchronous operation is finished.
445
 * @user_data: User-defined data to be passed to @callback
446
 *
447
 * Executes the batch. This operation happens asynchronously, when
448
 * finished @callback will be executed.
449
 *
450
 * Since: 3.1
451
 **/
452
void
453
tracker_batch_execute_async (TrackerBatch        *batch,
454
                             GCancellable        *cancellable,
455
                             GAsyncReadyCallback  callback,
456
                             gpointer             user_data)
457
0
{
458
0
  TrackerBatchPrivate *priv = tracker_batch_get_instance_private (batch);
459
460
0
  g_return_if_fail (TRACKER_IS_BATCH (batch));
461
0
  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
462
0
  g_return_if_fail (callback != NULL);
463
0
  g_return_if_fail (!priv->already_executed);
464
465
0
  priv->already_executed = TRUE;
466
467
0
  if (tracker_sparql_connection_report_async_error_on_closed (priv->connection,
468
0
                                                              callback,
469
0
                                                              user_data))
470
0
    return;
471
472
0
  TRACKER_BATCH_GET_CLASS (batch)->execute_async (batch, cancellable, callback, user_data);
473
0
}
474
475
/**
476
 * tracker_batch_execute_finish:
477
 * @batch: A `TrackerBatch`
478
 * @res: A [type@Gio.AsyncResult] with the result of the operation
479
 * @error: Error location
480
 *
481
 * Finishes the operation started with [method@Batch.execute_async].
482
 *
483
 * Returns: %TRUE of there were no errors, %FALSE otherwise
484
 *
485
 * Since: 3.1
486
 **/
487
gboolean
488
tracker_batch_execute_finish (TrackerBatch  *batch,
489
                              GAsyncResult  *res,
490
                              GError       **error)
491
0
{
492
0
  g_return_val_if_fail (TRACKER_IS_BATCH (batch), FALSE);
493
0
  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
494
0
  g_return_val_if_fail (!error || !*error, FALSE);
495
496
0
  return TRACKER_BATCH_GET_CLASS (batch)->execute_finish (batch, res, error);
497
0
}