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