/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 | } |