/src/tinysparql/src/libtinysparql/remote/tracker-remote-statement.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2021, 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 "tracker-remote-statement.h" |
25 | | |
26 | | #include <tracker-common.h> |
27 | | |
28 | | #include "core/tracker-sparql-grammar.h" |
29 | | #include "core/tracker-sparql-parser.h" |
30 | | #include "tracker-private.h" |
31 | | |
32 | | struct _TrackerRemoteStatement |
33 | | { |
34 | | TrackerSparqlStatement parent_instance; |
35 | | TrackerNodeTree *parser_tree; |
36 | | GHashTable *bindings; |
37 | | }; |
38 | | |
39 | 0 | G_DEFINE_TYPE (TrackerRemoteStatement, |
40 | 0 | tracker_remote_statement, |
41 | 0 | TRACKER_TYPE_SPARQL_STATEMENT) |
42 | 0 |
|
43 | 0 | static void |
44 | 0 | tracker_remote_statement_finalize (GObject *object) |
45 | 0 | { |
46 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (object); |
47 | |
|
48 | 0 | if (remote_stmt->parser_tree) |
49 | 0 | tracker_node_tree_free (remote_stmt->parser_tree); |
50 | 0 | g_hash_table_unref (remote_stmt->bindings); |
51 | |
|
52 | 0 | G_OBJECT_CLASS (tracker_remote_statement_parent_class)->finalize (object); |
53 | 0 | } |
54 | | |
55 | | static void |
56 | | tracker_remote_statement_bind_int (TrackerSparqlStatement *stmt, |
57 | | const gchar *name, |
58 | | gint64 value) |
59 | 0 | { |
60 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
61 | 0 | GValue *val; |
62 | |
|
63 | 0 | val = g_new0 (GValue, 1); |
64 | 0 | g_value_init (val, G_TYPE_INT64); |
65 | 0 | g_value_set_int64 (val, value); |
66 | |
|
67 | 0 | g_hash_table_insert (remote_stmt->bindings, |
68 | 0 | g_strdup (name), |
69 | 0 | val); |
70 | 0 | } |
71 | | |
72 | | static void |
73 | | tracker_remote_statement_bind_boolean (TrackerSparqlStatement *stmt, |
74 | | const gchar *name, |
75 | | gboolean value) |
76 | 0 | { |
77 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
78 | 0 | GValue *val; |
79 | |
|
80 | 0 | val = g_new0 (GValue, 1); |
81 | 0 | g_value_init (val, G_TYPE_BOOLEAN); |
82 | 0 | g_value_set_boolean (val, value); |
83 | |
|
84 | 0 | g_hash_table_insert (remote_stmt->bindings, |
85 | 0 | g_strdup (name), |
86 | 0 | val); |
87 | 0 | } |
88 | | |
89 | | static void |
90 | | tracker_remote_statement_bind_string (TrackerSparqlStatement *stmt, |
91 | | const gchar *name, |
92 | | const gchar *value) |
93 | 0 | { |
94 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
95 | 0 | GValue *val; |
96 | |
|
97 | 0 | val = g_new0 (GValue, 1); |
98 | 0 | g_value_init (val, G_TYPE_STRING); |
99 | 0 | g_value_set_string (val, value); |
100 | |
|
101 | 0 | g_hash_table_insert (remote_stmt->bindings, |
102 | 0 | g_strdup (name), |
103 | 0 | val); |
104 | 0 | } |
105 | | |
106 | | static void |
107 | | tracker_remote_statement_bind_double (TrackerSparqlStatement *stmt, |
108 | | const gchar *name, |
109 | | gdouble value) |
110 | 0 | { |
111 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
112 | 0 | GValue *val; |
113 | |
|
114 | 0 | val = g_new0 (GValue, 1); |
115 | 0 | g_value_init (val, G_TYPE_DOUBLE); |
116 | 0 | g_value_set_double (val, value); |
117 | |
|
118 | 0 | g_hash_table_insert (remote_stmt->bindings, |
119 | 0 | g_strdup (name), |
120 | 0 | val); |
121 | 0 | } |
122 | | |
123 | | static void |
124 | | tracker_remote_statement_bind_datetime (TrackerSparqlStatement *stmt, |
125 | | const gchar *name, |
126 | | GDateTime *value) |
127 | 0 | { |
128 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
129 | 0 | GValue *val; |
130 | |
|
131 | 0 | val = g_new0 (GValue, 1); |
132 | 0 | g_value_init (val, G_TYPE_DATE_TIME); |
133 | 0 | g_value_set_boxed (val, value); |
134 | |
|
135 | 0 | g_hash_table_insert (remote_stmt->bindings, |
136 | 0 | g_strdup (name), |
137 | 0 | val); |
138 | 0 | } |
139 | | |
140 | | static void |
141 | | tracker_remote_statement_bind_langstring (TrackerSparqlStatement *stmt, |
142 | | const gchar *name, |
143 | | const gchar *value, |
144 | | const gchar *langtag) |
145 | 0 | { |
146 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
147 | 0 | GValue *val; |
148 | |
|
149 | 0 | val = g_new0 (GValue, 1); |
150 | 0 | g_value_init (val, G_TYPE_BYTES); |
151 | 0 | g_value_take_boxed (val, tracker_sparql_make_langstring (value, langtag)); |
152 | |
|
153 | 0 | g_hash_table_insert (remote_stmt->bindings, |
154 | 0 | g_strdup (name), |
155 | 0 | val); |
156 | 0 | } |
157 | | |
158 | | static void |
159 | | append_gvalue (GString *str, |
160 | | const GValue *value) |
161 | 0 | { |
162 | 0 | if (G_VALUE_HOLDS_BOOLEAN (value)) { |
163 | 0 | g_string_append_printf (str, "%s", |
164 | 0 | g_value_get_boolean (value) ? |
165 | 0 | "true" : "false"); |
166 | 0 | } else if (G_VALUE_HOLDS_INT64 (value)) { |
167 | 0 | g_string_append_printf (str, "%" G_GINT64_FORMAT, |
168 | 0 | g_value_get_int64 (value)); |
169 | 0 | } else if (G_VALUE_HOLDS_DOUBLE (value)) { |
170 | 0 | gchar buf[G_ASCII_DTOSTR_BUF_SIZE + 1]; |
171 | |
|
172 | 0 | g_ascii_dtostr (buf, sizeof (buf), g_value_get_double (value)); |
173 | 0 | g_string_append (str, buf); |
174 | 0 | } else if (G_VALUE_TYPE (value) == G_TYPE_DATE_TIME) { |
175 | 0 | GDateTime *datetime; |
176 | 0 | gchar *datetime_str; |
177 | |
|
178 | 0 | datetime = g_value_get_boxed (value); |
179 | 0 | datetime_str = tracker_date_format_iso8601 (datetime); |
180 | 0 | g_string_append_printf (str, "\"%s\"", datetime_str); |
181 | 0 | g_free (datetime_str); |
182 | 0 | } else if (G_VALUE_TYPE (value) == G_TYPE_BYTES) { |
183 | 0 | GBytes *bytes; |
184 | 0 | const gchar *data; |
185 | 0 | gsize len, str_len; |
186 | |
|
187 | 0 | bytes = g_value_get_boxed (value); |
188 | 0 | data = g_bytes_get_data (bytes, &len); |
189 | 0 | str_len = strlen (data); |
190 | 0 | g_string_append_printf (str, "\"%s\"", data); |
191 | 0 | if (str_len < len) { |
192 | 0 | const gchar *langtag; |
193 | 0 | langtag = &data[str_len + 1]; |
194 | 0 | g_string_append_printf (str, "@%s", langtag); |
195 | 0 | } |
196 | 0 | } else if (G_VALUE_HOLDS_STRING (value)) { |
197 | 0 | const gchar *val = g_value_get_string (value); |
198 | 0 | int len = strlen (val); |
199 | 0 | gchar *end; |
200 | 0 | gboolean is_number = FALSE; |
201 | | |
202 | | /* Try to detect numbers anyway, since we use to allow |
203 | | * loose typing in other connection types. |
204 | | */ |
205 | 0 | g_ascii_strtoll (val, &end, 10); |
206 | 0 | is_number = (end == &val[len]); |
207 | |
|
208 | 0 | if (!is_number) { |
209 | 0 | g_ascii_strtod (val, &end); |
210 | 0 | is_number = (end == &val[len]); |
211 | 0 | } |
212 | |
|
213 | 0 | if (is_number) |
214 | 0 | g_string_append (str, val); |
215 | 0 | else |
216 | 0 | g_string_append_printf (str, "\"%s\"", val); |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | static gchar * |
221 | | apply_bindings (TrackerSparqlStatement *stmt, |
222 | | GHashTable *bindings, |
223 | | GError **error) |
224 | 0 | { |
225 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
226 | 0 | const gchar *query = tracker_sparql_statement_get_sparql (stmt); |
227 | 0 | GString *str = g_string_new (NULL); |
228 | 0 | TrackerParserNode *node = tracker_node_tree_get_root (remote_stmt->parser_tree); |
229 | |
|
230 | 0 | for (node = tracker_sparql_parser_tree_find_first (node, TRUE); |
231 | 0 | node; |
232 | 0 | node = tracker_sparql_parser_tree_find_next (node, TRUE)) { |
233 | 0 | const TrackerGrammarRule *rule; |
234 | 0 | gssize start, end; |
235 | |
|
236 | 0 | if (!tracker_parser_node_get_extents (node, &start, &end)) { |
237 | | /* Skip over 0-len nodes */ |
238 | 0 | continue; |
239 | 0 | } |
240 | | |
241 | 0 | rule = tracker_parser_node_get_rule (node); |
242 | |
|
243 | 0 | if (tracker_grammar_rule_is_a (rule, RULE_TYPE_TERMINAL, |
244 | 0 | TERMINAL_TYPE_PARAMETERIZED_VAR)) { |
245 | 0 | gchar *param_name; |
246 | 0 | const GValue *value; |
247 | |
|
248 | 0 | param_name = g_strndup (&query[start], end - start); |
249 | 0 | value = g_hash_table_lookup (bindings, ¶m_name[1]); |
250 | 0 | if (!value) { |
251 | 0 | g_set_error (error, |
252 | 0 | TRACKER_SPARQL_ERROR, |
253 | 0 | TRACKER_SPARQL_ERROR_PARSE, |
254 | 0 | "No binding found for variable %s", |
255 | 0 | param_name); |
256 | 0 | g_string_free (str, TRUE); |
257 | 0 | g_free (param_name); |
258 | 0 | return NULL; |
259 | 0 | } |
260 | | |
261 | 0 | append_gvalue (str, value); |
262 | 0 | g_free (param_name); |
263 | 0 | } else { |
264 | 0 | g_string_append_len (str, |
265 | 0 | &query[start], |
266 | 0 | end - start); |
267 | 0 | } |
268 | | |
269 | 0 | g_string_append_c (str, ' '); |
270 | 0 | } |
271 | | |
272 | 0 | return g_string_free (str, FALSE); |
273 | 0 | } |
274 | | |
275 | | static TrackerSparqlCursor * |
276 | | execute_statement (TrackerSparqlStatement *stmt, |
277 | | GHashTable *bindings, |
278 | | GCancellable *cancellable, |
279 | | GError **error) |
280 | 0 | { |
281 | 0 | TrackerSparqlCursor *cursor; |
282 | 0 | gchar *rewritten_query = NULL; |
283 | |
|
284 | 0 | if (g_hash_table_size (bindings) > 0) { |
285 | 0 | rewritten_query = apply_bindings (stmt, bindings, error); |
286 | 0 | if (!rewritten_query) |
287 | 0 | return NULL; |
288 | 0 | } |
289 | | |
290 | 0 | cursor = tracker_sparql_connection_query (tracker_sparql_statement_get_connection (stmt), |
291 | 0 | rewritten_query ? rewritten_query : |
292 | 0 | tracker_sparql_statement_get_sparql (stmt), |
293 | 0 | cancellable, |
294 | 0 | error); |
295 | 0 | g_free (rewritten_query); |
296 | |
|
297 | 0 | return cursor; |
298 | 0 | } |
299 | | |
300 | | TrackerSparqlCursor * |
301 | | tracker_remote_statement_execute (TrackerSparqlStatement *stmt, |
302 | | GCancellable *cancellable, |
303 | | GError **error) |
304 | 0 | { |
305 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
306 | |
|
307 | 0 | return execute_statement (stmt, remote_stmt->bindings, cancellable, error); |
308 | 0 | } |
309 | | |
310 | | static void |
311 | | execute_in_thread (GTask *task, |
312 | | gpointer object, |
313 | | gpointer task_data, |
314 | | GCancellable *cancellable) |
315 | 0 | { |
316 | 0 | TrackerSparqlCursor *cursor; |
317 | 0 | GHashTable *bindings = task_data; |
318 | 0 | GError *error = NULL; |
319 | |
|
320 | 0 | if (g_task_return_error_if_cancelled (task)) |
321 | 0 | return; |
322 | | |
323 | 0 | cursor = execute_statement (object, |
324 | 0 | bindings, |
325 | 0 | g_task_get_cancellable (task), |
326 | 0 | &error); |
327 | 0 | if (error) |
328 | 0 | g_task_return_error (task, error); |
329 | 0 | else |
330 | 0 | g_task_return_pointer (task, cursor, g_object_unref); |
331 | |
|
332 | 0 | g_object_unref (task); |
333 | 0 | } |
334 | | |
335 | | static void |
336 | | free_gvalue (gpointer data) |
337 | 0 | { |
338 | 0 | g_value_unset (data); |
339 | 0 | g_free (data); |
340 | 0 | } |
341 | | |
342 | | static GHashTable * |
343 | | create_bindings_ht (void) |
344 | 0 | { |
345 | 0 | return g_hash_table_new_full (g_str_hash, g_str_equal, |
346 | 0 | g_free, free_gvalue); |
347 | 0 | } |
348 | | |
349 | | static GHashTable * |
350 | | copy_values_deep (GHashTable *values) |
351 | 0 | { |
352 | 0 | GHashTable *copy; |
353 | 0 | GHashTableIter iter; |
354 | 0 | gpointer key, val; |
355 | |
|
356 | 0 | copy = create_bindings_ht (); |
357 | 0 | g_hash_table_iter_init (&iter, values); |
358 | |
|
359 | 0 | while (g_hash_table_iter_next (&iter, &key, &val)) { |
360 | 0 | GValue *copy_value; |
361 | |
|
362 | 0 | copy_value = g_new0 (GValue, 1); |
363 | 0 | g_value_init (copy_value, G_VALUE_TYPE (val)); |
364 | 0 | g_value_copy (val, copy_value); |
365 | |
|
366 | 0 | g_hash_table_insert (copy, g_strdup (key), copy_value); |
367 | 0 | } |
368 | |
|
369 | 0 | return copy; |
370 | 0 | } |
371 | | |
372 | | static void |
373 | | tracker_remote_statement_execute_async (TrackerSparqlStatement *stmt, |
374 | | GCancellable *cancellable, |
375 | | GAsyncReadyCallback callback, |
376 | | gpointer user_data) |
377 | 0 | { |
378 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
379 | 0 | GHashTable *bindings; |
380 | 0 | GTask *task; |
381 | |
|
382 | 0 | bindings = copy_values_deep (remote_stmt->bindings); |
383 | |
|
384 | 0 | task = g_task_new (stmt, cancellable, callback, user_data); |
385 | 0 | g_task_set_task_data (task, bindings, (GDestroyNotify) g_hash_table_unref); |
386 | 0 | g_task_run_in_thread (task, execute_in_thread); |
387 | 0 | } |
388 | | |
389 | | static TrackerSparqlCursor * |
390 | | tracker_remote_statement_execute_finish (TrackerSparqlStatement *stmt, |
391 | | GAsyncResult *res, |
392 | | GError **error) |
393 | 0 | { |
394 | 0 | return g_task_propagate_pointer (G_TASK (res), error); |
395 | 0 | } |
396 | | |
397 | | static void |
398 | | tracker_remote_statement_clear_bindings (TrackerSparqlStatement *stmt) |
399 | 0 | { |
400 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
401 | |
|
402 | 0 | g_hash_table_remove_all (remote_stmt->bindings); |
403 | 0 | } |
404 | | |
405 | | static void |
406 | | serialize_cb (GObject *source, |
407 | | GAsyncResult *res, |
408 | | gpointer user_data) |
409 | 0 | { |
410 | 0 | GInputStream *istream; |
411 | 0 | GError *error = NULL; |
412 | 0 | GTask *task = user_data; |
413 | |
|
414 | 0 | istream = tracker_sparql_connection_serialize_finish (TRACKER_SPARQL_CONNECTION (source), |
415 | 0 | res, &error); |
416 | 0 | if (error) |
417 | 0 | g_task_return_error (task, error); |
418 | 0 | else |
419 | 0 | g_task_return_pointer (task, istream, g_object_unref); |
420 | |
|
421 | 0 | g_object_unref (task); |
422 | 0 | } |
423 | | |
424 | | static void |
425 | | tracker_remote_statement_serialize_async (TrackerSparqlStatement *stmt, |
426 | | TrackerSerializeFlags flags, |
427 | | TrackerRdfFormat format, |
428 | | GCancellable *cancellable, |
429 | | GAsyncReadyCallback callback, |
430 | | gpointer user_data) |
431 | 0 | { |
432 | 0 | TrackerRemoteStatement *remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
433 | 0 | gchar *rewritten_query = NULL; |
434 | 0 | GError *error = NULL; |
435 | 0 | GTask *task; |
436 | |
|
437 | 0 | task = g_task_new (stmt, cancellable, callback, user_data); |
438 | |
|
439 | 0 | if (g_hash_table_size (remote_stmt->bindings) > 0) { |
440 | 0 | rewritten_query = apply_bindings (stmt, |
441 | 0 | remote_stmt->bindings, |
442 | 0 | &error); |
443 | 0 | if (!rewritten_query) { |
444 | 0 | g_task_return_error (task, error); |
445 | 0 | g_object_unref (task); |
446 | 0 | return; |
447 | 0 | } |
448 | 0 | } |
449 | | |
450 | 0 | tracker_sparql_connection_serialize_async (tracker_sparql_statement_get_connection (stmt), |
451 | 0 | flags, |
452 | 0 | format, |
453 | 0 | rewritten_query ? rewritten_query : |
454 | 0 | tracker_sparql_statement_get_sparql (stmt), |
455 | 0 | cancellable, |
456 | 0 | serialize_cb, |
457 | 0 | task); |
458 | 0 | g_free (rewritten_query); |
459 | 0 | } |
460 | | |
461 | | static GInputStream * |
462 | | tracker_remote_statement_serialize_finish (TrackerSparqlStatement *stmt, |
463 | | GAsyncResult *res, |
464 | | GError **error) |
465 | 0 | { |
466 | 0 | return g_task_propagate_pointer (G_TASK (res), error); |
467 | 0 | } |
468 | | |
469 | | static void |
470 | | tracker_remote_statement_class_init (TrackerRemoteStatementClass *klass) |
471 | 0 | { |
472 | 0 | TrackerSparqlStatementClass *stmt_class = TRACKER_SPARQL_STATEMENT_CLASS (klass); |
473 | 0 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
474 | |
|
475 | 0 | object_class->finalize = tracker_remote_statement_finalize; |
476 | |
|
477 | 0 | stmt_class->bind_int = tracker_remote_statement_bind_int; |
478 | 0 | stmt_class->bind_boolean = tracker_remote_statement_bind_boolean; |
479 | 0 | stmt_class->bind_string = tracker_remote_statement_bind_string; |
480 | 0 | stmt_class->bind_double = tracker_remote_statement_bind_double; |
481 | 0 | stmt_class->bind_datetime = tracker_remote_statement_bind_datetime; |
482 | 0 | stmt_class->bind_langstring = tracker_remote_statement_bind_langstring; |
483 | 0 | stmt_class->execute = tracker_remote_statement_execute; |
484 | 0 | stmt_class->execute_async = tracker_remote_statement_execute_async; |
485 | 0 | stmt_class->execute_finish = tracker_remote_statement_execute_finish; |
486 | 0 | stmt_class->clear_bindings = tracker_remote_statement_clear_bindings; |
487 | 0 | stmt_class->serialize_async = tracker_remote_statement_serialize_async; |
488 | 0 | stmt_class->serialize_finish = tracker_remote_statement_serialize_finish; |
489 | 0 | } |
490 | | |
491 | | static void |
492 | | tracker_remote_statement_init (TrackerRemoteStatement *stmt) |
493 | 0 | { |
494 | 0 | stmt->bindings = create_bindings_ht (); |
495 | 0 | } |
496 | | |
497 | | TrackerSparqlStatement * |
498 | | tracker_remote_statement_new (TrackerSparqlConnection *conn, |
499 | | const gchar *query, |
500 | | GError **error) |
501 | 0 | { |
502 | 0 | TrackerRemoteStatement *remote_stmt; |
503 | 0 | TrackerSparqlStatement *stmt; |
504 | |
|
505 | 0 | stmt = g_object_new (TRACKER_TYPE_REMOTE_STATEMENT, |
506 | 0 | "connection", conn, |
507 | 0 | "sparql", query, |
508 | 0 | NULL); |
509 | 0 | remote_stmt = TRACKER_REMOTE_STATEMENT (stmt); |
510 | 0 | remote_stmt->parser_tree = |
511 | 0 | tracker_sparql_parse_query (TRACKER_SPARQL_PARSE_ALLOW_EXTENSIONS, |
512 | 0 | tracker_sparql_statement_get_sparql (stmt), |
513 | 0 | -1, NULL, error); |
514 | 0 | if (!remote_stmt->parser_tree) { |
515 | 0 | g_object_unref (stmt); |
516 | 0 | return NULL; |
517 | 0 | } |
518 | | |
519 | 0 | return stmt; |
520 | 0 | } |