1 /*
2 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
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
20 #include "config.h"
21
22 #include <glib/gstdio.h>
23
24 #include <sqlite3.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <errno.h>
28
29 #include <libtracker-common/tracker-date-time.h>
30 #include <libtracker-common/tracker-debug.h>
31 #include <libtracker-common/tracker-locale.h>
32 #include <libtracker-common/tracker-parser.h>
33
34 #include <libtracker-sparql/tracker-cursor.h>
35 #include <libtracker-sparql/tracker-private.h>
36
37 #include <libtracker-fts/tracker-fts.h>
38
39
40 #ifdef HAVE_LIBUNISTRING
41 /* libunistring versions prior to 9.1.2 need this hack */
42 #define _UNUSED_PARAMETER_
43 #include <unistr.h>
44 #include <unicase.h>
45 #elif defined(HAVE_LIBICU)
46 #include <unicode/utypes.h>
47 #include <unicode/uregex.h>
48 #include <unicode/ustring.h>
49 #include <unicode/ucol.h>
50 #include <unicode/unorm2.h>
51 #endif
52
53 #include "tracker-collation.h"
54
55 #include "tracker-db-interface-sqlite.h"
56 #include "tracker-db-manager.h"
57 #include "tracker-data-enum-types.h"
58 #include "tracker-uuid.h"
59 #include "tracker-vtab-service.h"
60 #include "tracker-vtab-triples.h"
61
62 typedef struct {
63 TrackerDBStatement *head;
64 TrackerDBStatement *tail;
65 guint size;
66 guint max;
67 } TrackerDBStatementLru;
68
69 typedef struct {
70 GRegex *syntax_check;
71 GRegex *replacement;
72 GRegex *unescape;
73 } TrackerDBReplaceFuncChecks;
74
75 struct TrackerDBInterface {
76 GObject parent_instance;
77
78 gchar *filename;
79 gchar *shared_cache_key;
80 sqlite3 *db;
81
82 GHashTable *dynamic_statements;
83
84 /* Compiled regular expressions */
85 TrackerDBReplaceFuncChecks replace_func_checks;
86
87 /* Number of users (e.g. active cursors) */
88 gint n_users;
89
90 guint flags;
91 GCancellable *cancellable;
92
93 TrackerDBStatementLru select_stmt_lru;
94 TrackerDBStatementLru update_stmt_lru;
95
96 gchar *fts_properties;
97
98 /* Used if TRACKER_DB_INTERFACE_USE_MUTEX is set */
99 GMutex mutex;
100
101 /* User data */
102 gpointer user_data;
103 GDestroyNotify user_data_destroy_notify;
104 };
105
106 struct TrackerDBInterfaceClass {
107 GObjectClass parent_class;
108 };
109
110 struct TrackerDBCursor {
111 TrackerSparqlCursor parent_instance;
112 sqlite3_stmt *stmt;
113 TrackerDBStatement *ref_stmt;
114 gboolean finished;
115 TrackerPropertyType *types;
116 gint n_types;
117 gchar **variable_names;
118 gint n_variable_names;
119 };
120
121 struct TrackerDBCursorClass {
122 TrackerSparqlCursorClass parent_class;
123 };
124
125 struct TrackerDBStatement {
126 GInitiallyUnowned parent_instance;
127 TrackerDBInterface *db_interface;
128 sqlite3_stmt *stmt;
129 guint stmt_is_used : 1;
130 guint stmt_is_owned : 1;
131 TrackerDBStatement *next;
132 TrackerDBStatement *prev;
133 };
134
135 struct TrackerDBStatementClass {
136 GObjectClass parent_class;
137 };
138
139 static void tracker_db_interface_initable_iface_init (GInitableIface *iface);
140 static TrackerDBStatement *tracker_db_statement_sqlite_new (TrackerDBInterface *db_interface,
141 sqlite3_stmt *sqlite_stmt);
142 static void tracker_db_statement_sqlite_reset (TrackerDBStatement *stmt);
143 static TrackerDBCursor *tracker_db_cursor_sqlite_new (TrackerDBStatement *ref_stmt,
144 TrackerPropertyType *types,
145 gint n_types,
146 const gchar * const *variable_names,
147 gint n_variable_names);
148 static gboolean tracker_db_cursor_get_boolean (TrackerSparqlCursor *cursor,
149 guint column);
150 static gboolean db_cursor_iter_next (TrackerDBCursor *cursor,
151 GCancellable *cancellable,
152 GError **error);
153
154 enum {
155 PROP_0,
156 PROP_FILENAME,
157 PROP_FLAGS,
158 PROP_SHARED_CACHE_KEY,
159 };
160
161 enum {
162 TRACKER_DB_CURSOR_PROP_0,
163 TRACKER_DB_CURSOR_PROP_N_COLUMNS
164 };
165
166 G_DEFINE_TYPE_WITH_CODE (TrackerDBInterface, tracker_db_interface, G_TYPE_OBJECT,
167 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
168 tracker_db_interface_initable_iface_init));
169
G_DEFINE_TYPE(TrackerDBStatement,tracker_db_statement,G_TYPE_INITIALLY_UNOWNED)170 G_DEFINE_TYPE (TrackerDBStatement, tracker_db_statement, G_TYPE_INITIALLY_UNOWNED)
171
172 G_DEFINE_TYPE (TrackerDBCursor, tracker_db_cursor, TRACKER_SPARQL_TYPE_CURSOR)
173
174 void
175 tracker_db_interface_sqlite_enable_shared_cache (void)
176 {
177 sqlite3_enable_shared_cache (1);
178 }
179
180 static void
result_context_function_error(sqlite3_context * context,const gchar * sparql_function,const gchar * error_message)181 result_context_function_error (sqlite3_context *context,
182 const gchar *sparql_function,
183 const gchar *error_message)
184 {
185 gchar *message;
186
187 message = g_strdup_printf ("%s: %s", sparql_function, error_message);
188 sqlite3_result_error (context, message, -1);
189
190 g_free (message);
191 }
192
193 static void
function_sparql_string_join(sqlite3_context * context,int argc,sqlite3_value * argv[])194 function_sparql_string_join (sqlite3_context *context,
195 int argc,
196 sqlite3_value *argv[])
197 {
198 const gchar *fn = "fn:string-join";
199 GString *str = NULL;
200 const gchar *separator;
201 gint i;
202
203 /* fn:string-join (str1, str2, ..., separator) */
204
205 if (sqlite3_value_type (argv[argc-1]) != SQLITE_TEXT) {
206 result_context_function_error (context, fn, "Invalid separator");
207 return;
208 }
209
210 separator = (gchar *)sqlite3_value_text (argv[argc-1]);
211
212 for (i = 0;i < argc-1; i++) {
213 if (sqlite3_value_type (argv[argc-1]) == SQLITE_TEXT) {
214 const gchar *text = (gchar *)sqlite3_value_text (argv[i]);
215
216 if (text != NULL) {
217 if (!str) {
218 str = g_string_new (text);
219 } else {
220 g_string_append_printf (str, "%s%s", separator, text);
221 }
222 }
223 }
224 }
225
226 if (str) {
227 sqlite3_result_text (context, str->str, str->len, g_free);
228 g_string_free (str, FALSE);
229 } else {
230 sqlite3_result_null (context);
231 }
232
233 return;
234 }
235
236 /* Create a title-type string from the filename for replacing missing ones */
237 static void
function_sparql_string_from_filename(sqlite3_context * context,int argc,sqlite3_value * argv[])238 function_sparql_string_from_filename (sqlite3_context *context,
239 int argc,
240 sqlite3_value *argv[])
241 {
242 const gchar *fn = "fn:string-from-filename";
243 gchar *name = NULL;
244 gchar *suffix = NULL;
245
246 if (argc != 1) {
247 result_context_function_error (context, fn, "Invalid argument count");
248 return;
249 }
250
251 /* "/home/user/path/title_of_the_movie.movie" -> "title of the movie"
252 * Only for local files currently, do we need to change? */
253
254 name = g_filename_display_basename ((gchar *)sqlite3_value_text (argv[0]));
255
256 if (!name) {
257 sqlite3_result_null (context);
258 return;
259 }
260
261 suffix = g_strrstr (name, ".");
262
263 if (suffix) {
264 *suffix = '\0';
265 }
266
267 g_strdelimit (name, "._", ' ');
268
269 sqlite3_result_text (context, name, -1, g_free);
270 }
271
272 static void
function_sparql_uri_is_parent(sqlite3_context * context,int argc,sqlite3_value * argv[])273 function_sparql_uri_is_parent (sqlite3_context *context,
274 int argc,
275 sqlite3_value *argv[])
276 {
277 const gchar *fn = "tracker:uri-is-parent";
278 const gchar *uri, *parent, *remaining;
279 gboolean match = FALSE;
280 guint parent_len;
281
282 if (argc != 2) {
283 result_context_function_error (context, fn, "Invalid argument count");
284 return;
285 }
286
287 parent = (gchar *)sqlite3_value_text (argv[0]);
288 uri = (gchar *)sqlite3_value_text (argv[1]);
289
290 if (!parent || !uri) {
291 sqlite3_result_int (context, FALSE);
292 return;
293 }
294
295 parent_len = sqlite3_value_bytes (argv[0]);
296
297 /* Check only one argument, it's going to
298 * be compared with the other anyway.
299 */
300
301 if (!(parent_len >= 7 && (parent[4] == ':' && parent[5] == '/' && parent[6] == '/'))) {
302 if (strstr (parent, "://") == NULL) {
303 sqlite3_result_int (context, FALSE);
304 return;
305 }
306 }
307
308 /* Remove trailing '/', will
309 * be checked later on uri.
310 */
311 while (parent[parent_len - 1] == '/') {
312 parent_len--;
313 }
314
315 if (strncmp (uri, parent, parent_len) == 0 && uri[parent_len] == '/') {
316 const gchar *slash;
317
318 while (uri[parent_len] == '/') {
319 parent_len++;
320 }
321
322 remaining = &uri[parent_len];
323
324 if (*remaining == '\0') {
325 /* Exact match, not a child */
326 match = FALSE;
327 } else if ((slash = strchr (remaining, '/')) == NULL) {
328 /* Remaining doesn't have uri
329 * separator, it's a direct child.
330 */
331 match = TRUE;
332 } else {
333 /* Check it's not trailing slashes */
334 while (*slash == '/') {
335 slash++;
336 }
337
338 match = (*slash == '\0');
339 }
340 }
341
342 sqlite3_result_int (context, match);
343 }
344
345 static gboolean
check_uri_is_descendant(const gchar * parent,guint parent_len,const gchar * uri)346 check_uri_is_descendant (const gchar *parent,
347 guint parent_len,
348 const gchar *uri)
349 {
350 const gchar *remaining;
351 gboolean match = FALSE;
352
353 /* Check only one argument, it's going to
354 * be compared with the other anyway.
355 */
356
357 if (!(parent_len >= 7 && (parent[4] == ':' && parent[5] == '/' && parent[6] == '/'))) {
358 if (strstr (parent, "://") == NULL) {
359 return FALSE;
360 }
361 }
362
363 /* Remove trailing '/', will
364 * be checked later on uri.
365 */
366 while (parent[parent_len - 1] == '/') {
367 parent_len--;
368 }
369
370 if (strncmp (uri, parent, parent_len) == 0 && uri[parent_len] == '/') {
371 while (uri[parent_len] == '/') {
372 parent_len++;
373 }
374
375 remaining = &uri[parent_len];
376
377 if (remaining && *remaining) {
378 match = TRUE;
379 }
380 }
381
382 return match;
383 }
384
385 static void
function_sparql_uri_is_descendant(sqlite3_context * context,int argc,sqlite3_value * argv[])386 function_sparql_uri_is_descendant (sqlite3_context *context,
387 int argc,
388 sqlite3_value *argv[])
389 {
390 const gchar *fn = "tracker:uri-is-descendant";
391 const gchar *child;
392 gboolean match = FALSE;
393 gint i;
394
395 /* fn:uri-is-descendant (parent1, parent2, ..., parentN, child) */
396
397 if (argc < 2) {
398 result_context_function_error (context, fn, "Invalid argument count");
399 return;
400 }
401
402 for (i = 0; i < argc; i++) {
403 if (sqlite3_value_type (argv[i]) == SQLITE_NULL) {
404 sqlite3_result_int (context, FALSE);
405 return;
406 } else if (sqlite3_value_type (argv[i]) != SQLITE_TEXT) {
407 result_context_function_error (context, fn, "Invalid non-text argument");
408 return;
409 }
410 }
411
412 child = (gchar *)sqlite3_value_text (argv[argc-1]);
413
414 for (i = 0; i < argc - 1 && !match; i++) {
415 if (sqlite3_value_type (argv[i]) == SQLITE_TEXT) {
416 const gchar *parent = (gchar *)sqlite3_value_text (argv[i]);
417 guint parent_len = sqlite3_value_bytes (argv[i]);
418
419 if (!parent)
420 continue;
421
422 match = check_uri_is_descendant (parent, parent_len, child);
423 }
424 }
425
426 sqlite3_result_int (context, match);
427 }
428
429 static void
function_sparql_format_time(sqlite3_context * context,int argc,sqlite3_value * argv[])430 function_sparql_format_time (sqlite3_context *context,
431 int argc,
432 sqlite3_value *argv[])
433 {
434 const gchar *fn = "SparqlFormatTime helper";
435
436 if (argc != 1) {
437 result_context_function_error (context, fn, "Invalid argument count");
438 return;
439 }
440
441 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
442 sqlite3_result_null (context);
443 return;
444 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
445 GDateTime *datetime;
446 gint64 timestamp;
447
448 timestamp = sqlite3_value_int64 (argv[0]);
449 datetime = g_date_time_new_from_unix_utc (timestamp);
450
451 if (datetime) {
452 sqlite3_result_text (context,
453 tracker_date_format_iso8601 (datetime),
454 -1, g_free);
455 g_date_time_unref (datetime);
456 } else {
457 result_context_function_error (context, fn, "Datetime conversion error");
458 }
459 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
460 const gchar *str;
461
462 str = sqlite3_value_text (argv[0]);
463 sqlite3_result_text (context, g_strdup (str), -1, g_free);
464 } else {
465 result_context_function_error (context, fn, "Invalid argument type");
466 }
467 }
468
469 static void
function_sparql_timestamp(sqlite3_context * context,int argc,sqlite3_value * argv[])470 function_sparql_timestamp (sqlite3_context *context,
471 int argc,
472 sqlite3_value *argv[])
473 {
474 const gchar *fn = "SparqlTimestamp helper";
475
476 if (argc != 1) {
477 result_context_function_error (context, fn, "Invalid argument count");
478 return;
479 }
480
481 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
482 sqlite3_result_null (context);
483 return;
484 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
485 gdouble seconds;
486
487 seconds = sqlite3_value_double (argv[0]);
488 sqlite3_result_double (context, seconds);
489 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
490 GError *error = NULL;
491 GDateTime *datetime;
492 const gchar *str;
493
494 str = sqlite3_value_text (argv[0]);
495 datetime = tracker_date_new_from_iso8601 (str, &error);
496 if (error) {
497 result_context_function_error (context, fn, "Failed time string conversion");
498 g_error_free (error);
499 return;
500 }
501
502 sqlite3_result_int64 (context,
503 g_date_time_to_unix (datetime) +
504 (g_date_time_get_utc_offset (datetime) / G_USEC_PER_SEC));
505 g_date_time_unref (datetime);
506 } else {
507 result_context_function_error (context, fn, "Invalid argument type");
508 }
509 }
510
511 static void
function_sparql_time_sort(sqlite3_context * context,int argc,sqlite3_value * argv[])512 function_sparql_time_sort (sqlite3_context *context,
513 int argc,
514 sqlite3_value *argv[])
515 {
516 const gchar *fn = "SparqlTimeSort helper";
517 gint64 sort_key;
518
519 if (argc != 1) {
520 result_context_function_error (context, fn, "Invalid argument count");
521 return;
522 }
523
524 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
525 sqlite3_result_null (context);
526 return;
527 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER ||
528 sqlite3_value_numeric_type (argv[0]) == SQLITE_FLOAT) {
529 gdouble value;
530
531 value = sqlite3_value_double (argv[0]);
532 sort_key = (gint64) (value * G_USEC_PER_SEC);
533 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
534 GDateTime *datetime;
535 const gchar *value;
536 GError *error = NULL;
537
538 value = sqlite3_value_text (argv[0]);
539 datetime = tracker_date_new_from_iso8601 (value, &error);
540 if (error) {
541 result_context_function_error (context, fn, error->message);
542 g_error_free (error);
543 return;
544 }
545
546 sort_key = ((g_date_time_to_unix (datetime) * G_USEC_PER_SEC) +
547 g_date_time_get_microsecond (datetime));
548 g_date_time_unref (datetime);
549 } else {
550 result_context_function_error (context, fn, "Invalid argument type");
551 return;
552 }
553
554 sqlite3_result_int64 (context, sort_key);
555 }
556
557 static void
function_sparql_time_zone_duration(sqlite3_context * context,int argc,sqlite3_value * argv[])558 function_sparql_time_zone_duration (sqlite3_context *context,
559 int argc,
560 sqlite3_value *argv[])
561 {
562 const gchar *fn = "timezone-from-dateTime";
563 if (argc != 1) {
564 result_context_function_error (context, fn, "Invalid argument count");
565 return;
566 }
567
568 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
569 sqlite3_result_null (context);
570 return;
571 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
572 sqlite3_result_int (context, 0);
573 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
574 GError *error = NULL;
575 GDateTime *datetime;
576 const gchar *str;
577
578 str = sqlite3_value_text (argv[0]);
579 datetime = tracker_date_new_from_iso8601 (str, &error);
580 if (error) {
581 result_context_function_error (context, fn, "Invalid date");
582 g_error_free (error);
583 return;
584 }
585
586 sqlite3_result_int64 (context,
587 g_date_time_get_utc_offset (datetime) /
588 G_USEC_PER_SEC);
589 g_date_time_unref (datetime);
590 } else {
591 result_context_function_error (context, fn, "Invalid argument type");
592 }
593 }
594
595 static void
function_sparql_time_zone_substr(sqlite3_context * context,int argc,sqlite3_value * argv[])596 function_sparql_time_zone_substr (sqlite3_context *context,
597 int argc,
598 sqlite3_value *argv[])
599 {
600 if (argc != 1) {
601 sqlite3_result_error (context, "Invalid argument count converting timezone to string", -1);
602 return;
603 }
604
605 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
606 sqlite3_result_null (context);
607 return;
608 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
609 sqlite3_result_text (context, "", -1, NULL);
610 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
611 const gchar *str;
612 gint len;
613
614 str = sqlite3_value_text (argv[0]);
615 len = strlen (str);
616
617 if (g_str_has_suffix (str, "Z")) {
618 sqlite3_result_text (context, "Z", -1, NULL);
619 } else if (len > strlen ("0000-00-00T00:00:00Z")) {
620 const gchar *tz = "";
621
622 /* [+-]HHMM */
623 if (str[len - 5] == '+' || str[len - 5] == '-')
624 tz = &str[len - 5];
625 /* [+-]HH:MM */
626 else if (str[len - 6] == '+' || str[len - 6] == '-')
627 tz = &str[len - 6];
628
629 sqlite3_result_text (context, g_strdup (tz), -1, g_free);
630 } else {
631 sqlite3_result_text (context, "", -1, NULL);
632 }
633 } else {
634 sqlite3_result_error (context, "Invalid argument type converting timezone to string", -1);
635 }
636 }
637
638 static gchar *
offset_to_duration(gint offset)639 offset_to_duration (gint offset)
640 {
641 GString *str = g_string_new (NULL);
642 gint hours, minutes, seconds;
643
644 if (offset > 0)
645 g_string_append (str, "+PT");
646 else
647 g_string_append (str, "-PT");
648
649 offset = ABS (offset);
650 hours = offset / 3600;
651 minutes = offset % 3600 / 60;
652 seconds = offset % 60;
653
654 if (hours > 0)
655 g_string_append_printf (str, "%dH", hours);
656 if (minutes > 0)
657 g_string_append_printf (str, "%dM", minutes);
658 if (seconds > 0)
659 g_string_append_printf (str, "%dS", seconds);
660
661 return g_string_free (str, FALSE);
662 }
663
664 static void
function_sparql_time_zone(sqlite3_context * context,int argc,sqlite3_value * argv[])665 function_sparql_time_zone (sqlite3_context *context,
666 int argc,
667 sqlite3_value *argv[])
668 {
669 const gchar *fn = "SparqlTimezone helper";
670
671 if (argc != 1) {
672 result_context_function_error (context, fn, "Invalid argument count");
673 return;
674 }
675
676 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
677 sqlite3_result_null (context);
678 return;
679 } else if (sqlite3_value_numeric_type (argv[0]) == SQLITE_INTEGER) {
680 sqlite3_result_text (context, "PT0S", -1, NULL);
681 } else if (sqlite3_value_type (argv[0]) == SQLITE_TEXT) {
682 GError *error = NULL;
683 GDateTime *datetime;
684 const gchar *str;
685 gchar *duration;
686
687 str = sqlite3_value_text (argv[0]);
688 datetime = tracker_date_new_from_iso8601 (str, &error);
689 if (error) {
690 result_context_function_error (context, fn, "Invalid date");
691 g_error_free (error);
692 return;
693 }
694
695 duration = offset_to_duration (g_date_time_get_utc_offset (datetime) /
696 G_USEC_PER_SEC);
697 sqlite3_result_text (context, g_strdup (duration), -1, g_free);
698 g_date_time_unref (datetime);
699 } else {
700 result_context_function_error (context, fn, "Invalid argument type");
701 }
702 }
703
704 static void
function_sparql_cartesian_distance(sqlite3_context * context,int argc,sqlite3_value * argv[])705 function_sparql_cartesian_distance (sqlite3_context *context,
706 int argc,
707 sqlite3_value *argv[])
708 {
709 const gchar *fn = "tracker:cartesian-distance";
710
711 gdouble lat1;
712 gdouble lat2;
713 gdouble lon1;
714 gdouble lon2;
715
716 gdouble R;
717 gdouble a;
718 gdouble b;
719 gdouble c;
720 gdouble d;
721
722 if (argc != 4) {
723 result_context_function_error (context, fn, "Invalid argument count");
724 return;
725 }
726
727 lat1 = sqlite3_value_double (argv[0])*M_PI/180;
728 lat2 = sqlite3_value_double (argv[1])*M_PI/180;
729 lon1 = sqlite3_value_double (argv[2])*M_PI/180;
730 lon2 = sqlite3_value_double (argv[3])*M_PI/180;
731
732 R = 6371000;
733 a = M_PI/2 - lat1;
734 b = M_PI/2 - lat2;
735 c = sqrt(a*a + b*b - 2*a*b*cos(lon2 - lon1));
736 d = R*c;
737
738 sqlite3_result_double (context, d);
739 }
740
741 static void
function_sparql_haversine_distance(sqlite3_context * context,int argc,sqlite3_value * argv[])742 function_sparql_haversine_distance (sqlite3_context *context,
743 int argc,
744 sqlite3_value *argv[])
745 {
746 const gchar *fn = "tracker:haversine-distance";
747
748 gdouble lat1;
749 gdouble lat2;
750 gdouble lon1;
751 gdouble lon2;
752
753 gdouble R;
754 gdouble dLat;
755 gdouble dLon;
756 gdouble a;
757 gdouble c;
758 gdouble d;
759
760 if (argc != 4) {
761 result_context_function_error (context, fn, "Invalid argument count");
762 return;
763 }
764
765 lat1 = sqlite3_value_double (argv[0])*M_PI/180;
766 lat2 = sqlite3_value_double (argv[1])*M_PI/180;
767 lon1 = sqlite3_value_double (argv[2])*M_PI/180;
768 lon2 = sqlite3_value_double (argv[3])*M_PI/180;
769
770 R = 6371000;
771 dLat = (lat2-lat1);
772 dLon = (lon2-lon1);
773 a = sin(dLat/2) * sin(dLat/2) + cos(lat1) * cos(lat2) * sin(dLon/2) * sin(dLon/2);
774 c = 2 * atan2(sqrt(a), sqrt(1-a));
775 d = R * c;
776
777 sqlite3_result_double (context, d);
778 }
779
780 static void
function_sparql_regex(sqlite3_context * context,int argc,sqlite3_value * argv[])781 function_sparql_regex (sqlite3_context *context,
782 int argc,
783 sqlite3_value *argv[])
784 {
785 const gchar *fn = "fn:matches";
786 gboolean ret;
787 const gchar *text, *pattern, *flags = "";
788 GRegexCompileFlags regex_flags;
789 GRegex *regex;
790
791 if (argc != 2 && argc != 3) {
792 result_context_function_error (context, fn, "Invalid argument count");
793 return;
794 }
795
796 regex = sqlite3_get_auxdata (context, 1);
797
798 text = (gchar *)sqlite3_value_text (argv[0]);
799
800 if (argc == 3)
801 flags = (gchar *)sqlite3_value_text (argv[2]);
802
803 if (regex == NULL) {
804 gchar *err_str;
805 GError *error = NULL;
806
807 pattern = (gchar *)sqlite3_value_text (argv[1]);
808
809 regex_flags = 0;
810 while (*flags) {
811 switch (*flags) {
812 case 's':
813 regex_flags |= G_REGEX_DOTALL;
814 break;
815 case 'm':
816 regex_flags |= G_REGEX_MULTILINE;
817 break;
818 case 'i':
819 regex_flags |= G_REGEX_CASELESS;
820 break;
821 case 'x':
822 regex_flags |= G_REGEX_EXTENDED;
823 break;
824 default:
825 err_str = g_strdup_printf ("Invalid SPARQL regex flag '%c'", *flags);
826 result_context_function_error (context, fn, err_str);
827 g_free (err_str);
828 return;
829 }
830 flags++;
831 }
832
833 regex = g_regex_new (pattern, regex_flags, 0, &error);
834
835 if (error) {
836 result_context_function_error (context, fn, error->message);
837 g_clear_error (&error);
838 return;
839 }
840
841 sqlite3_set_auxdata (context, 1, regex, (void (*) (void*)) g_regex_unref);
842 }
843
844 if (text != NULL) {
845 ret = g_regex_match (regex, text, 0, NULL);
846 } else {
847 ret = FALSE;
848 }
849
850 sqlite3_result_int (context, ret);
851 }
852
853 static void
ensure_replace_checks(TrackerDBInterface * db_interface)854 ensure_replace_checks (TrackerDBInterface *db_interface)
855 {
856 if (db_interface->replace_func_checks.syntax_check != NULL)
857 return;
858
859 db_interface->replace_func_checks.syntax_check =
860 g_regex_new ("(?<!\\\\)\\$\\D", G_REGEX_OPTIMIZE, 0, NULL);
861 db_interface->replace_func_checks.replacement =
862 g_regex_new("(?<!\\\\)\\$(\\d)", G_REGEX_OPTIMIZE, 0, NULL);
863 db_interface->replace_func_checks.unescape =
864 g_regex_new("\\\\\\$", G_REGEX_OPTIMIZE, 0, NULL);
865 }
866
867 static void
function_sparql_replace(sqlite3_context * context,int argc,sqlite3_value * argv[])868 function_sparql_replace (sqlite3_context *context,
869 int argc,
870 sqlite3_value *argv[])
871 {
872 const gchar *fn = "fn:replace";
873 TrackerDBInterface *db_interface = sqlite3_user_data (context);
874 TrackerDBReplaceFuncChecks *checks = &db_interface->replace_func_checks;
875 gboolean store_regex = FALSE, store_replace_regex = FALSE;
876 const gchar *input, *pattern, *replacement, *flags;
877 gchar *err_str, *output = NULL, *replaced = NULL, *unescaped = NULL;
878 GError *error = NULL;
879 GRegexCompileFlags regex_flags = 0;
880 GRegex *regex, *replace_regex;
881 gint capture_count, i;
882
883 ensure_replace_checks (db_interface);
884
885 if (argc == 3) {
886 flags = "";
887 } else if (argc == 4) {
888 flags = (gchar *)sqlite3_value_text (argv[3]);
889 } else {
890 result_context_function_error (context, fn, "Invalid argument count");
891 return;
892 }
893
894 input = (gchar *)sqlite3_value_text (argv[0]);
895 regex = sqlite3_get_auxdata (context, 1);
896 replacement = (gchar *)sqlite3_value_text (argv[2]);
897
898 if (regex == NULL) {
899 pattern = (gchar *)sqlite3_value_text (argv[1]);
900
901 for (i = 0; flags[i]; i++) {
902 switch (flags[i]) {
903 case 's':
904 regex_flags |= G_REGEX_DOTALL;
905 break;
906 case 'm':
907 regex_flags |= G_REGEX_MULTILINE;
908 break;
909 case 'i':
910 regex_flags |= G_REGEX_CASELESS;
911 break;
912 case 'x':
913 regex_flags |= G_REGEX_EXTENDED;
914 break;
915 default:
916 err_str = g_strdup_printf ("Invalid SPARQL regex flag '%c'", flags[i]);
917 result_context_function_error (context, fn, err_str);
918 g_free (err_str);
919 return;
920 }
921 }
922
923 regex = g_regex_new (pattern, regex_flags, 0, &error);
924
925 if (error) {
926 result_context_function_error (context, fn, error->message);
927 g_clear_error (&error);
928 return;
929 }
930
931 /* According to the XPath 2.0 standard, an error shall be raised, if the given
932 * pattern matches a zero-length string.
933 */
934 if (g_regex_match (regex, "", 0, NULL)) {
935 err_str = g_strdup_printf ("The given pattern '%s' matches a zero-length string.",
936 pattern);
937 result_context_function_error (context, fn, err_str);
938 g_regex_unref (regex);
939 g_free (err_str);
940 return;
941 }
942
943 store_regex = TRUE;
944 }
945
946 /* According to the XPath 2.0 standard, an error shall be raised, if all dollar
947 * signs ($) of the given replacement string are not immediately followed by
948 * a digit 0-9 or not immediately preceded by a \.
949 */
950 if (g_regex_match (checks->syntax_check, replacement, 0, NULL)) {
951 err_str = g_strdup_printf ("The replacement string '%s' contains a \"$\" character "
952 "that is not immediately followed by a digit 0-9 and "
953 "not immediately preceded by a \"\\\".",
954 replacement);
955 result_context_function_error (context, fn, err_str);
956 g_free (err_str);
957 return;
958 }
959
960 /* According to the XPath 2.0 standard, the dollar sign ($) followed by a number
961 * indicates backreferences. GRegex uses the backslash (\) for this purpose.
962 * So the ($) backreferences in the given replacement string are replaced by (\)
963 * backreferences to support the standard.
964 */
965 capture_count = g_regex_get_capture_count (regex);
966 replace_regex = sqlite3_get_auxdata (context, 2);
967
968 if (capture_count > 9 && !replace_regex) {
969 gint i;
970 GString *backref_range;
971 gchar *regex_interpret;
972
973 /* S ... capture_count, N ... the given decimal number.
974 * If N>S and N>9, The last digit of N is taken to be a literal character
975 * to be included "as is" in the replacement string, and the rules are
976 * reapplied using the number N formed by stripping off this last digit.
977 */
978 backref_range = g_string_new ("(");
979 for (i = 10; i <= capture_count; i++) {
980 g_string_append_printf (backref_range, "%d|", i);
981 }
982
983 g_string_append (backref_range, "\\d)");
984 regex_interpret = g_strdup_printf ("(?<!\\\\)\\$%s",
985 backref_range->str);
986
987 replace_regex = g_regex_new (regex_interpret, 0, 0, NULL);
988
989 g_string_free (backref_range, TRUE);
990 g_free (regex_interpret);
991
992 store_replace_regex = TRUE;
993 } else if (capture_count <= 9) {
994 replace_regex = checks->replacement;
995 }
996
997 replaced = g_regex_replace (replace_regex,
998 replacement, -1, 0, "\\\\g<\\1>", 0, &error);
999
1000 if (!error) {
1001 /* All '\$' pairs are replaced by '$' */
1002 unescaped = g_regex_replace (checks->unescape,
1003 replaced, -1, 0, "$", 0, &error);
1004 }
1005
1006 if (!error) {
1007 output = g_regex_replace (regex, input, -1, 0, unescaped, 0, &error);
1008 }
1009
1010 if (error) {
1011 result_context_function_error (context, fn, error->message);
1012 g_clear_error (&error);
1013 return;
1014 }
1015
1016 sqlite3_result_text (context, output, -1, g_free);
1017
1018 if (store_replace_regex)
1019 sqlite3_set_auxdata (context, 2, replace_regex, (GDestroyNotify) g_regex_unref);
1020 if (store_regex)
1021 sqlite3_set_auxdata (context, 1, regex, (GDestroyNotify) g_regex_unref);
1022
1023 g_free (replaced);
1024 g_free (unescaped);
1025 }
1026
1027 #ifdef HAVE_LIBUNISTRING
1028
1029 static void
function_sparql_lower_case(sqlite3_context * context,int argc,sqlite3_value * argv[])1030 function_sparql_lower_case (sqlite3_context *context,
1031 int argc,
1032 sqlite3_value *argv[])
1033 {
1034 const uint16_t *zInput;
1035 uint16_t *zOutput;
1036 size_t written = 0;
1037 int nInput;
1038
1039 g_assert (argc == 1);
1040
1041 zInput = sqlite3_value_text16 (argv[0]);
1042
1043 if (!zInput) {
1044 return;
1045 }
1046
1047 nInput = sqlite3_value_bytes16 (argv[0]);
1048
1049 zOutput = u16_tolower (zInput, nInput/2, NULL, NULL, NULL, &written);
1050
1051 sqlite3_result_text16 (context, zOutput, written * 2, free);
1052 }
1053
1054 static void
function_sparql_upper_case(sqlite3_context * context,int argc,sqlite3_value * argv[])1055 function_sparql_upper_case (sqlite3_context *context,
1056 int argc,
1057 sqlite3_value *argv[])
1058 {
1059 const uint16_t *zInput;
1060 uint16_t *zOutput;
1061 size_t written = 0;
1062 int nInput;
1063
1064 g_assert (argc == 1);
1065
1066 zInput = sqlite3_value_text16 (argv[0]);
1067
1068 if (!zInput) {
1069 return;
1070 }
1071
1072 nInput = sqlite3_value_bytes16 (argv[0]);
1073
1074 zOutput = u16_toupper (zInput, nInput / 2, NULL, NULL, NULL, &written);
1075
1076 sqlite3_result_text16 (context, zOutput, written * 2, free);
1077 }
1078
1079 static void
function_sparql_case_fold(sqlite3_context * context,int argc,sqlite3_value * argv[])1080 function_sparql_case_fold (sqlite3_context *context,
1081 int argc,
1082 sqlite3_value *argv[])
1083 {
1084 const uint16_t *zInput;
1085 uint16_t *zOutput;
1086 size_t written = 0;
1087 int nInput;
1088
1089 g_assert (argc == 1);
1090
1091 zInput = sqlite3_value_text16 (argv[0]);
1092
1093 if (!zInput) {
1094 return;
1095 }
1096
1097 nInput = sqlite3_value_bytes16 (argv[0]);
1098
1099 zOutput = u16_casefold (zInput, nInput/2, NULL, NULL, NULL, &written);
1100
1101 sqlite3_result_text16 (context, zOutput, written * 2, free);
1102 }
1103
1104 static void
function_sparql_normalize(sqlite3_context * context,int argc,sqlite3_value * argv[])1105 function_sparql_normalize (sqlite3_context *context,
1106 int argc,
1107 sqlite3_value *argv[])
1108 {
1109 const gchar *fn = "tracker:normalize";
1110 const gchar *nfstr;
1111 const uint16_t *zInput;
1112 uint16_t *zOutput;
1113 size_t written = 0;
1114 int nInput;
1115 uninorm_t nf;
1116
1117 if (argc != 2) {
1118 result_context_function_error (context, fn, "Invalid argument count");
1119 return;
1120 }
1121
1122 zInput = sqlite3_value_text16 (argv[0]);
1123
1124 if (!zInput) {
1125 return;
1126 }
1127
1128 nfstr = sqlite3_value_text (argv[1]);
1129 if (g_ascii_strcasecmp (nfstr, "nfc") == 0)
1130 nf = UNINORM_NFC;
1131 else if (g_ascii_strcasecmp (nfstr, "nfd") == 0)
1132 nf = UNINORM_NFD;
1133 else if (g_ascii_strcasecmp (nfstr, "nfkc") == 0)
1134 nf = UNINORM_NFKC;
1135 else if (g_ascii_strcasecmp (nfstr, "nfkd") == 0)
1136 nf = UNINORM_NFKD;
1137 else {
1138 result_context_function_error (context, fn, "Invalid normalization specified, options are 'nfc', 'nfd', 'nfkc' or 'nfkd'");
1139 return;
1140 }
1141
1142 nInput = sqlite3_value_bytes16 (argv[0]);
1143
1144 zOutput = u16_normalize (nf, zInput, nInput/2, NULL, &written);
1145
1146 sqlite3_result_text16 (context, zOutput, written * 2, free);
1147 }
1148
1149 static void
function_sparql_unaccent(sqlite3_context * context,int argc,sqlite3_value * argv[])1150 function_sparql_unaccent (sqlite3_context *context,
1151 int argc,
1152 sqlite3_value *argv[])
1153 {
1154 const gchar *zInput;
1155 gchar *zOutput;
1156 gsize written = 0;
1157 int nInput;
1158
1159 g_assert (argc == 1);
1160
1161 zInput = sqlite3_value_text (argv[0]);
1162
1163 if (!zInput) {
1164 return;
1165 }
1166
1167 nInput = sqlite3_value_bytes (argv[0]);
1168
1169 zOutput = u8_normalize (UNINORM_NFKD, zInput, nInput, NULL, &written);
1170
1171 /* Unaccenting is done in place */
1172 tracker_parser_unaccent_nfkd_string (zOutput, &written);
1173
1174 sqlite3_result_text (context, zOutput, written, free);
1175 }
1176
1177 #elif defined(HAVE_LIBICU)
1178
1179 static void
function_sparql_lower_case(sqlite3_context * context,int argc,sqlite3_value * argv[])1180 function_sparql_lower_case (sqlite3_context *context,
1181 int argc,
1182 sqlite3_value *argv[])
1183 {
1184 const gchar *fn = "fn:lower-case";
1185 const UChar *zInput;
1186 UChar *zOutput;
1187 int nInput;
1188 int nOutput;
1189 UErrorCode status = U_ZERO_ERROR;
1190
1191 g_assert (argc == 1);
1192
1193 zInput = sqlite3_value_text16 (argv[0]);
1194
1195 if (!zInput) {
1196 return;
1197 }
1198
1199 nInput = sqlite3_value_bytes16 (argv[0]);
1200
1201 nOutput = nInput * 2 + 2;
1202 zOutput = sqlite3_malloc (nOutput);
1203
1204 if (!zOutput) {
1205 return;
1206 }
1207
1208 u_strToLower (zOutput, nOutput/2, zInput, nInput/2, NULL, &status);
1209
1210 if (!U_SUCCESS (status)){
1211 char zBuf[128];
1212 sqlite3_snprintf (128, zBuf, "ICU error: u_strToLower(): %s", u_errorName (status));
1213 zBuf[127] = '\0';
1214 sqlite3_free (zOutput);
1215 result_context_function_error (context, fn, zBuf);
1216 return;
1217 }
1218
1219 sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
1220 }
1221
1222 static void
function_sparql_upper_case(sqlite3_context * context,int argc,sqlite3_value * argv[])1223 function_sparql_upper_case (sqlite3_context *context,
1224 int argc,
1225 sqlite3_value *argv[])
1226 {
1227 const gchar *fn = "fn:upper-case";
1228 const UChar *zInput;
1229 UChar *zOutput;
1230 int nInput;
1231 int nOutput;
1232 UErrorCode status = U_ZERO_ERROR;
1233
1234 g_assert (argc == 1);
1235
1236 zInput = sqlite3_value_text16 (argv[0]);
1237
1238 if (!zInput) {
1239 return;
1240 }
1241
1242 nInput = sqlite3_value_bytes16 (argv[0]);
1243
1244 nOutput = nInput * 2 + 2;
1245 zOutput = sqlite3_malloc (nOutput);
1246
1247 if (!zOutput) {
1248 return;
1249 }
1250
1251 u_strToUpper (zOutput, nOutput / 2, zInput, nInput / 2, NULL, &status);
1252
1253 if (!U_SUCCESS (status)){
1254 char zBuf[128];
1255 sqlite3_snprintf (128, zBuf, "ICU error: u_strToUpper(): %s", u_errorName (status));
1256 zBuf[127] = '\0';
1257 sqlite3_free (zOutput);
1258 result_context_function_error (context, fn, zBuf);
1259 return;
1260 }
1261
1262 sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
1263 }
1264
1265 static void
function_sparql_case_fold(sqlite3_context * context,int argc,sqlite3_value * argv[])1266 function_sparql_case_fold (sqlite3_context *context,
1267 int argc,
1268 sqlite3_value *argv[])
1269 {
1270 const gchar *fn = "tracker:case-fold";
1271 const UChar *zInput;
1272 UChar *zOutput;
1273 int nInput;
1274 int nOutput;
1275 UErrorCode status = U_ZERO_ERROR;
1276
1277 g_assert (argc == 1);
1278
1279 zInput = sqlite3_value_text16 (argv[0]);
1280
1281 if (!zInput) {
1282 return;
1283 }
1284
1285 nInput = sqlite3_value_bytes16 (argv[0]);
1286
1287 nOutput = nInput * 2 + 2;
1288 zOutput = sqlite3_malloc (nOutput);
1289
1290 if (!zOutput) {
1291 return;
1292 }
1293
1294 u_strFoldCase (zOutput, nOutput/2, zInput, nInput/2, U_FOLD_CASE_DEFAULT, &status);
1295
1296 if (!U_SUCCESS (status)){
1297 char zBuf[128];
1298 sqlite3_snprintf (128, zBuf, "ICU error: u_strFoldCase: %s", u_errorName (status));
1299 zBuf[127] = '\0';
1300 sqlite3_free (zOutput);
1301 result_context_function_error (context, fn, zBuf);
1302 return;
1303 }
1304
1305 sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
1306 }
1307
1308 static void
function_sparql_strip_punctuation(sqlite3_context * context,int argc,sqlite3_value * argv[])1309 function_sparql_strip_punctuation (sqlite3_context *context,
1310 int argc,
1311 sqlite3_value *argv[])
1312 {
1313 const gchar *fn = "tracker:strip-punctuation";
1314 gchar *input, *replacement = "", *output = NULL;
1315 GError *error = NULL;
1316 GRegex *regex;
1317 input = (gchar *)sqlite3_value_text (argv[0]);
1318 const gchar *pattern = "\\p{P}";
1319
1320 regex = g_regex_new (pattern, 0, 0, &error);
1321 if (error)
1322 {
1323 result_context_function_error (context, fn, error->message);
1324 g_clear_error (&error);
1325 return;
1326 }
1327
1328 output = g_regex_replace (regex, input, -1, 0, replacement, 0, &error);
1329
1330 sqlite3_result_text (context, output, -1, g_free);
1331 }
1332
1333 static gunichar2 *
normalize_string(const gunichar2 * string,gsize string_len,const UNormalizer2 * normalizer,gsize * len_out,UErrorCode * status)1334 normalize_string (const gunichar2 *string,
1335 gsize string_len, /* In gunichar2s */
1336 const UNormalizer2 *normalizer,
1337 gsize *len_out, /* In gunichar2s */
1338 UErrorCode *status)
1339 {
1340 int nOutput;
1341 gunichar2 *zOutput;
1342
1343 nOutput = (string_len * 2) + 1;
1344 zOutput = g_new0 (gunichar2, nOutput);
1345
1346 nOutput = unorm2_normalize (normalizer, string, string_len, zOutput, nOutput, status);
1347
1348 if (*status == U_BUFFER_OVERFLOW_ERROR) {
1349 /* Try again after allocating enough space for the normalization */
1350 *status = U_ZERO_ERROR;
1351 zOutput = g_renew (gunichar2, zOutput, nOutput);
1352 memset (zOutput, 0, nOutput * sizeof (gunichar2));
1353 nOutput = unorm2_normalize (normalizer, string, string_len, zOutput, nOutput, status);
1354 }
1355
1356 if (!U_SUCCESS (*status)) {
1357 g_clear_pointer (&zOutput, g_free);
1358 nOutput = 0;
1359 }
1360
1361 if (len_out)
1362 *len_out = nOutput;
1363
1364 return zOutput;
1365 }
1366
1367 static void
function_sparql_normalize(sqlite3_context * context,int argc,sqlite3_value * argv[])1368 function_sparql_normalize (sqlite3_context *context,
1369 int argc,
1370 sqlite3_value *argv[])
1371 {
1372 const gchar *fn = "tracker:normalize";
1373 const gchar *nfstr;
1374 const uint16_t *zInput;
1375 uint16_t *zOutput = NULL;
1376 int nInput;
1377 gsize nOutput;
1378 const UNormalizer2 *normalizer;
1379 UErrorCode status = U_ZERO_ERROR;
1380
1381 if (argc != 2) {
1382 result_context_function_error (context, fn, "Invalid argument count");
1383 return;
1384 }
1385
1386 zInput = sqlite3_value_text16 (argv[0]);
1387
1388 if (!zInput) {
1389 return;
1390 }
1391
1392 nfstr = (gchar *)sqlite3_value_text (argv[1]);
1393 if (g_ascii_strcasecmp (nfstr, "nfc") == 0)
1394 normalizer = unorm2_getNFCInstance (&status);
1395 else if (g_ascii_strcasecmp (nfstr, "nfd") == 0)
1396 normalizer = unorm2_getNFDInstance (&status);
1397 else if (g_ascii_strcasecmp (nfstr, "nfkc") == 0)
1398 normalizer = unorm2_getNFKCInstance (&status);
1399 else if (g_ascii_strcasecmp (nfstr, "nfkd") == 0)
1400 normalizer = unorm2_getNFKDInstance (&status);
1401 else {
1402 result_context_function_error (context, fn, "Invalid normalization specified");
1403 return;
1404 }
1405
1406 if (U_SUCCESS (status)) {
1407 nInput = sqlite3_value_bytes16 (argv[0]);
1408 zOutput = normalize_string (zInput, nInput / 2, normalizer, &nOutput, &status);
1409 }
1410
1411 if (!U_SUCCESS (status)) {
1412 char zBuf[128];
1413 sqlite3_snprintf (128, zBuf, "ICU error: unorm_normalize: %s", u_errorName (status));
1414 zBuf[127] = '\0';
1415 g_free (zOutput);
1416 result_context_function_error (context, fn, zBuf);
1417 return;
1418 }
1419
1420 sqlite3_result_text16 (context, zOutput, nOutput * sizeof (gunichar2), g_free);
1421 }
1422
1423 static void
function_sparql_unaccent(sqlite3_context * context,int argc,sqlite3_value * argv[])1424 function_sparql_unaccent (sqlite3_context *context,
1425 int argc,
1426 sqlite3_value *argv[])
1427 {
1428 const gchar *fn = "tracker:unaccent";
1429 const uint16_t *zInput;
1430 uint16_t *zOutput = NULL;
1431 int nInput;
1432 gsize nOutput;
1433 const UNormalizer2 *normalizer;
1434 UErrorCode status = U_ZERO_ERROR;
1435
1436 g_assert (argc == 1);
1437
1438 zInput = sqlite3_value_text16 (argv[0]);
1439
1440 if (!zInput) {
1441 return;
1442 }
1443
1444 normalizer = unorm2_getNFKDInstance (&status);
1445
1446 if (U_SUCCESS (status)) {
1447 nInput = sqlite3_value_bytes16 (argv[0]);
1448 zOutput = normalize_string (zInput, nInput / 2, normalizer, &nOutput, &status);
1449 }
1450
1451 if (!U_SUCCESS (status)) {
1452 char zBuf[128];
1453 sqlite3_snprintf (128, zBuf, "ICU error: unorm_normalize: %s", u_errorName (status));
1454 zBuf[127] = '\0';
1455 g_free (zOutput);
1456 result_context_function_error (context, fn, zBuf);
1457 return;
1458 }
1459
1460 /* Unaccenting is done in place */
1461 tracker_parser_unaccent_nfkd_string (zOutput, &nOutput);
1462
1463 sqlite3_result_text16 (context, zOutput, nOutput * sizeof (gunichar2), g_free);
1464 }
1465
1466 #endif
1467
1468 static void
function_sparql_encode_for_uri(sqlite3_context * context,int argc,sqlite3_value * argv[])1469 function_sparql_encode_for_uri (sqlite3_context *context,
1470 int argc,
1471 sqlite3_value *argv[])
1472 {
1473 const gchar *fn = "fn:encode-for-uri";
1474 const gchar *str;
1475 gchar *encoded;
1476
1477 if (argc != 1) {
1478 result_context_function_error (context, fn, "Invalid argument count");
1479 return;
1480 }
1481
1482 str = (gchar *)sqlite3_value_text (argv[0]);
1483 encoded = g_uri_escape_string (str, NULL, FALSE);
1484 sqlite3_result_text (context, encoded, -1, g_free);
1485 }
1486
1487 static void
function_sparql_uri(sqlite3_context * context,int argc,sqlite3_value * argv[])1488 function_sparql_uri (sqlite3_context *context,
1489 int argc,
1490 sqlite3_value *argv[])
1491 {
1492 const gchar *fn = "tracker:uri";
1493 const gchar *str;
1494 gchar *encoded;
1495
1496 if (argc != 1) {
1497 result_context_function_error (context, fn, "Invalid argument count");
1498 return;
1499 }
1500
1501 str = (gchar *)sqlite3_value_text (argv[0]);
1502 encoded = g_uri_escape_string (str,
1503 G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
1504 FALSE);
1505 sqlite3_result_text (context, encoded, -1, g_free);
1506 }
1507
1508 static void
function_sparql_string_before(sqlite3_context * context,int argc,sqlite3_value * argv[])1509 function_sparql_string_before (sqlite3_context *context,
1510 int argc,
1511 sqlite3_value *argv[])
1512 {
1513 const gchar *fn = "fn:substring-before";
1514 const gchar *str, *substr, *loc;
1515 gint len;
1516
1517 if (argc != 2) {
1518 result_context_function_error (context, fn, "Invalid argument count");
1519 return;
1520 }
1521
1522 if (sqlite3_value_type (argv[0]) != SQLITE_TEXT ||
1523 sqlite3_value_type (argv[1]) != SQLITE_TEXT) {
1524 result_context_function_error (context, fn, "Invalid argument types");
1525 return;
1526 }
1527
1528 str = (gchar *)sqlite3_value_text (argv[0]);
1529 substr = (gchar *)sqlite3_value_text (argv[1]);
1530 len = strlen (substr);
1531
1532 if (len == 0) {
1533 sqlite3_result_text (context, "", -1, NULL);
1534 return;
1535 }
1536
1537 loc = strstr (str, substr);
1538
1539 if (!loc) {
1540 sqlite3_result_text (context, "", -1, NULL);
1541 return;
1542 }
1543
1544 sqlite3_result_text (context, str, loc - str, NULL);
1545 }
1546
1547 static void
function_sparql_string_after(sqlite3_context * context,int argc,sqlite3_value * argv[])1548 function_sparql_string_after (sqlite3_context *context,
1549 int argc,
1550 sqlite3_value *argv[])
1551 {
1552 const gchar *fn = "fn:substring-after";
1553 const gchar *str, *substr, *loc;
1554 gint len;
1555
1556 if (argc != 2) {
1557 result_context_function_error (context, fn, "Invalid argument count");
1558 return;
1559 }
1560
1561 if (sqlite3_value_type (argv[0]) != SQLITE_TEXT ||
1562 sqlite3_value_type (argv[1]) != SQLITE_TEXT) {
1563 result_context_function_error (context, fn, "Invalid argument types");
1564 return;
1565 }
1566
1567 str = (gchar *)sqlite3_value_text (argv[0]);
1568 substr = (gchar *)sqlite3_value_text (argv[1]);
1569 len = strlen (substr);
1570
1571 if (len == 0) {
1572 sqlite3_result_text (context, g_strdup (str), -1, g_free);
1573 return;
1574 }
1575
1576 loc = strstr (str, substr);
1577
1578 if (!loc) {
1579 sqlite3_result_text (context, "", -1, NULL);
1580 return;
1581 }
1582
1583 sqlite3_result_text (context, loc + len, -1, NULL);
1584 }
1585
1586 static void
function_sparql_ceil(sqlite3_context * context,int argc,sqlite3_value * argv[])1587 function_sparql_ceil (sqlite3_context *context,
1588 int argc,
1589 sqlite3_value *argv[])
1590 {
1591 const gchar *fn = "fn:numeric-ceil";
1592 gdouble value;
1593
1594 if (argc != 1) {
1595 result_context_function_error (context, fn, "Invalid argument count");
1596 return;
1597 }
1598
1599 value = sqlite3_value_double (argv[0]);
1600 sqlite3_result_double (context, ceil (value));
1601 }
1602
1603 static void
function_sparql_floor(sqlite3_context * context,int argc,sqlite3_value * argv[])1604 function_sparql_floor (sqlite3_context *context,
1605 int argc,
1606 sqlite3_value *argv[])
1607 {
1608 const gchar *fn = "fn:numeric-floor";
1609 gdouble value;
1610
1611 if (argc != 1) {
1612 result_context_function_error (context, fn, "Invalid argument count");
1613 return;
1614 }
1615
1616 value = sqlite3_value_double (argv[0]);
1617 sqlite3_result_double (context, floor (value));
1618 }
1619
1620 static void
function_sparql_data_type(sqlite3_context * context,int argc,sqlite3_value * argv[])1621 function_sparql_data_type (sqlite3_context *context,
1622 int argc,
1623 sqlite3_value *argv[])
1624 {
1625 const gchar *fn = "SparqlDateType helper";
1626 TrackerPropertyType prop_type;
1627 const gchar *type = NULL;
1628
1629 if (argc != 1) {
1630 result_context_function_error (context, fn, "Invalid argument count");
1631 return;
1632 }
1633
1634 prop_type = sqlite3_value_int (argv[0]);
1635
1636 switch (prop_type) {
1637 case TRACKER_PROPERTY_TYPE_UNKNOWN:
1638 break;
1639 case TRACKER_PROPERTY_TYPE_STRING:
1640 type = "http://www.w3.org/2001/XMLSchema#string";
1641 break;
1642 case TRACKER_PROPERTY_TYPE_BOOLEAN:
1643 type = "http://www.w3.org/2001/XMLSchema#boolean";
1644 break;
1645 case TRACKER_PROPERTY_TYPE_INTEGER:
1646 type = "http://www.w3.org/2001/XMLSchema#integer";
1647 break;
1648 case TRACKER_PROPERTY_TYPE_DOUBLE:
1649 type = "http://www.w3.org/2001/XMLSchema#double";
1650 break;
1651 case TRACKER_PROPERTY_TYPE_DATE:
1652 type = "http://www.w3.org/2001/XMLSchema#date";
1653 break;
1654 case TRACKER_PROPERTY_TYPE_DATETIME:
1655 type = "http://www.w3.org/2001/XMLSchema#dateType";
1656 break;
1657 case TRACKER_PROPERTY_TYPE_RESOURCE:
1658 type = "http://www.w3.org/2000/01/rdf-schema#Resource";
1659 break;
1660 case TRACKER_PROPERTY_TYPE_LANGSTRING:
1661 type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString";
1662 break;
1663 }
1664
1665 if (type)
1666 sqlite3_result_text (context, type, -1, NULL);
1667 else
1668 sqlite3_result_null (context);
1669 }
1670
1671 static void
function_sparql_rand(sqlite3_context * context,int argc,sqlite3_value * argv[])1672 function_sparql_rand (sqlite3_context *context,
1673 int argc,
1674 sqlite3_value *argv[])
1675 {
1676 const gchar *fn = "rand";
1677
1678 if (argc != 0) {
1679 result_context_function_error (context, fn, "Invalid argument count");
1680 return;
1681 }
1682
1683 sqlite3_result_double (context, g_random_double ());
1684 }
1685
1686 static void
function_sparql_checksum(sqlite3_context * context,int argc,sqlite3_value * argv[])1687 function_sparql_checksum (sqlite3_context *context,
1688 int argc,
1689 sqlite3_value *argv[])
1690 {
1691 const gchar *fn = "SparqlCheckSum helper";
1692 const gchar *str, *checksumstr;
1693 GChecksumType checksum;
1694 gchar *result;
1695
1696 if (argc != 2) {
1697 result_context_function_error (context, fn, "Invalid argument count");
1698 return;
1699 }
1700
1701 str = (gchar *)sqlite3_value_text (argv[0]);
1702 checksumstr = (gchar *)sqlite3_value_text (argv[1]);
1703
1704 if (!str || !checksumstr) {
1705 result_context_function_error (context, fn, "Invalid arguments");
1706 return;
1707 }
1708
1709 if (g_ascii_strcasecmp (checksumstr, "md5") == 0)
1710 checksum = G_CHECKSUM_MD5;
1711 else if (g_ascii_strcasecmp (checksumstr, "sha1") == 0)
1712 checksum = G_CHECKSUM_SHA1;
1713 else if (g_ascii_strcasecmp (checksumstr, "sha256") == 0)
1714 checksum = G_CHECKSUM_SHA256;
1715 #if GLIB_CHECK_VERSION (2, 51, 0)
1716 else if (g_ascii_strcasecmp (checksumstr, "sha384") == 0)
1717 checksum = G_CHECKSUM_SHA384;
1718 #endif
1719 else if (g_ascii_strcasecmp (checksumstr, "sha512") == 0)
1720 checksum = G_CHECKSUM_SHA512;
1721 else {
1722 result_context_function_error (context, fn, "Invalid checksum method specified");
1723 return;
1724 }
1725
1726 result = g_compute_checksum_for_string (checksum, str, -1);
1727 sqlite3_result_text (context, result, -1, g_free);
1728 }
1729
1730 static void
function_sparql_langmatches(sqlite3_context * context,int argc,sqlite3_value * argv[])1731 function_sparql_langmatches (sqlite3_context *context,
1732 int argc,
1733 sqlite3_value *argv[])
1734 {
1735 const gchar *fn = "langMatches";
1736 const gchar *str, *langtag;
1737 gint type;
1738
1739 if (argc != 2) {
1740 result_context_function_error (context, fn, "Invalid argument count");
1741 return;
1742 }
1743
1744 type = sqlite3_value_type (argv[0]);
1745
1746 if (type == SQLITE_TEXT) {
1747 /* text arguments don't contain any language information */
1748 sqlite3_result_int (context, FALSE);
1749 } else if (type == SQLITE_BLOB) {
1750 gint str_len, len;
1751
1752 str = sqlite3_value_blob (argv[0]);
1753 len = sqlite3_value_bytes (argv[0]);
1754 langtag = sqlite3_value_text (argv[1]);
1755 str_len = strlen (str) + 1;
1756
1757 if (str_len + strlen (langtag) != len ||
1758 g_strcmp0 (&str[str_len], langtag) != 0) {
1759 sqlite3_result_int (context, FALSE);
1760 } else {
1761 sqlite3_result_int (context, TRUE);
1762 }
1763 } else {
1764 sqlite3_result_null (context);
1765 }
1766 }
1767
1768 static void
function_sparql_strlang(sqlite3_context * context,int argc,sqlite3_value * argv[])1769 function_sparql_strlang (sqlite3_context *context,
1770 int argc,
1771 sqlite3_value *argv[])
1772 {
1773 const gchar *fn = "strlang";
1774 const gchar *str, *langtag;
1775 GString *langstr;
1776
1777 if (argc != 2) {
1778 result_context_function_error (context, fn, "Invalid argument count");
1779 return;
1780 }
1781
1782 str = sqlite3_value_text (argv[0]);
1783 langtag = sqlite3_value_text (argv[1]);
1784
1785 langstr = g_string_new (str);
1786 g_string_append_c (langstr, '\0');
1787 g_string_append (langstr, langtag);
1788
1789 sqlite3_result_blob64 (context, langstr->str,
1790 langstr->len, g_free);
1791 g_string_free (langstr, FALSE);
1792 }
1793
1794 static inline int
stmt_step(sqlite3_stmt * stmt)1795 stmt_step (sqlite3_stmt *stmt)
1796 {
1797 int result;
1798
1799 result = sqlite3_step (stmt);
1800
1801 /* If the statement expired between preparing it and executing
1802 * sqlite3_step(), we are supposed to get SQLITE_SCHEMA error in
1803 * sqlite3_errcode(), BUT there seems to be a bug in sqlite and
1804 * SQLITE_ABORT is being returned instead for that case. So, the
1805 * only way to see if a given statement was expired is to use
1806 * sqlite3_expired(stmt), which is marked as DEPRECATED in sqlite.
1807 * If found that the statement is expired, we need to reset it
1808 * and retry the sqlite3_step().
1809 * NOTE, that this expiration may only happen between preparing
1810 * the statement and step-ing it, NOT between steps. */
1811 if ((result == SQLITE_ABORT || result == SQLITE_SCHEMA) &&
1812 sqlite3_expired (stmt)) {
1813 sqlite3_reset (stmt);
1814 result = sqlite3_step (stmt);
1815 }
1816
1817 return result;
1818 }
1819
1820 static void
generate_uuid(sqlite3_context * context,const gchar * fn,const gchar * uri_prefix)1821 generate_uuid (sqlite3_context *context,
1822 const gchar *fn,
1823 const gchar *uri_prefix)
1824 {
1825 gchar *uuid = NULL;
1826 sqlite3_stmt *stmt;
1827 gboolean store_auxdata = FALSE;
1828 sqlite3 *db;
1829 gint result;
1830
1831 stmt = sqlite3_get_auxdata (context, 1);
1832
1833 if (stmt == NULL) {
1834 db = sqlite3_context_db_handle (context);
1835
1836 result = sqlite3_prepare_v2 (db, "SELECT ID FROM Resource WHERE Uri=?",
1837 -1, &stmt, NULL);
1838 if (result != SQLITE_OK) {
1839 result_context_function_error (context, fn, sqlite3_errstr (result));
1840 return;
1841 }
1842
1843 store_auxdata = TRUE;
1844 }
1845
1846 do {
1847 g_clear_pointer (&uuid, g_free);
1848 uuid = tracker_generate_uuid (uri_prefix);
1849
1850 sqlite3_reset (stmt);
1851 sqlite3_bind_text (stmt, 1, uuid, -1, SQLITE_TRANSIENT);
1852 result = stmt_step (stmt);
1853 } while (result == SQLITE_ROW);
1854
1855 if (store_auxdata) {
1856 sqlite3_set_auxdata (context, 1, stmt,
1857 (void (*) (void*)) sqlite3_finalize);
1858 }
1859
1860 if (result != SQLITE_DONE) {
1861 result_context_function_error (context, fn, sqlite3_errstr (result));
1862 g_free (uuid);
1863 } else {
1864 sqlite3_result_text (context, uuid, -1, g_free);
1865 }
1866 }
1867
1868 static void
function_sparql_uuid(sqlite3_context * context,int argc,sqlite3_value * argv[])1869 function_sparql_uuid (sqlite3_context *context,
1870 int argc,
1871 sqlite3_value *argv[])
1872 {
1873 const gchar *fn = "SparqlUUID helper";
1874 const gchar *prefix;
1875
1876 if (argc > 1) {
1877 result_context_function_error (context, fn, "Invalid argument count");
1878 return;
1879 }
1880
1881 prefix = sqlite3_value_text (argv[0]);
1882 generate_uuid (context, fn, prefix);
1883 }
1884
1885 static void
function_sparql_bnode(sqlite3_context * context,int argc,sqlite3_value * argv[])1886 function_sparql_bnode (sqlite3_context *context,
1887 int argc,
1888 sqlite3_value *argv[])
1889 {
1890 const gchar *fn = "SparlBNODE helper";
1891
1892 if (argc > 1) {
1893 result_context_function_error (context, fn, "Invalid argument count");
1894 return;
1895 }
1896
1897 generate_uuid (context, fn, "urn:bnode");
1898 }
1899
1900 static void
function_sparql_print_iri(sqlite3_context * context,int argc,sqlite3_value * argv[])1901 function_sparql_print_iri (sqlite3_context *context,
1902 int argc,
1903 sqlite3_value *argv[])
1904 {
1905 const gchar *fn = "PrintIRI helper";
1906
1907 if (argc > 1) {
1908 result_context_function_error (context, fn, "Invalid argument count");
1909 return;
1910 }
1911
1912 if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER) {
1913 sqlite3_stmt *stmt;
1914 gboolean store_auxdata = FALSE;
1915 sqlite3 *db;
1916 gint result;
1917
1918 stmt = sqlite3_get_auxdata (context, 1);
1919
1920 if (stmt == NULL) {
1921 store_auxdata = TRUE;
1922 db = sqlite3_context_db_handle (context);
1923
1924 result = sqlite3_prepare_v2 (db, "SELECT Uri FROM Resource WHERE ID = ?",
1925 -1, &stmt, NULL);
1926 if (result != SQLITE_OK) {
1927 result_context_function_error (context, fn, sqlite3_errstr (result));
1928 return;
1929 }
1930 }
1931
1932 sqlite3_reset (stmt);
1933 sqlite3_bind_value (stmt, 1, argv[0]);
1934 result = stmt_step (stmt);
1935
1936 if (result == SQLITE_DONE) {
1937 sqlite3_result_null (context);
1938 } else if (result == SQLITE_ROW) {
1939 sqlite3_result_value (context, sqlite3_column_value (stmt, 0));
1940 } else {
1941 result_context_function_error (context, fn, sqlite3_errstr (result));
1942 }
1943
1944 if (store_auxdata) {
1945 sqlite3_set_auxdata (context, 1, stmt,
1946 (void (*) (void*)) sqlite3_finalize);
1947 }
1948 } else {
1949 sqlite3_result_value (context, argv[0]);
1950 }
1951 }
1952
1953 static int
check_interrupt(void * user_data)1954 check_interrupt (void *user_data)
1955 {
1956 TrackerDBInterface *db_interface = user_data;
1957
1958 return g_cancellable_is_cancelled (db_interface->cancellable) ? 1 : 0;
1959 }
1960
1961 static void
initialize_functions(TrackerDBInterface * db_interface)1962 initialize_functions (TrackerDBInterface *db_interface)
1963 {
1964 gint i;
1965 struct {
1966 gchar *name;
1967 int n_args;
1968 int mods;
1969 void (*func) (sqlite3_context *, int, sqlite3_value**);
1970 } functions[] = {
1971 /* Geolocation */
1972 { "SparqlHaversineDistance", 4, SQLITE_ANY | SQLITE_DETERMINISTIC,
1973 function_sparql_haversine_distance },
1974 { "SparqlCartesianDistance", 4, SQLITE_ANY | SQLITE_DETERMINISTIC,
1975 function_sparql_cartesian_distance },
1976 /* Date/time */
1977 { "SparqlFormatTime", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1978 function_sparql_format_time },
1979 { "SparqlTimestamp", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1980 function_sparql_timestamp },
1981 { "SparqlTimeSort", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1982 function_sparql_time_sort },
1983 { "SparqlTimezoneDuration", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1984 function_sparql_time_zone_duration },
1985 { "SparqlTimezoneString", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1986 function_sparql_time_zone_substr },
1987 { "SparqlTimezone", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1988 function_sparql_time_zone },
1989 /* Paths and filenames */
1990 { "SparqlStringFromFilename", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1991 function_sparql_string_from_filename },
1992 { "SparqlUriIsParent", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
1993 function_sparql_uri_is_parent },
1994 { "SparqlUriIsDescendant", -1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1995 function_sparql_uri_is_descendant },
1996 { "SparqlEncodeForUri", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1997 function_sparql_encode_for_uri },
1998 { "SparqlUri", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
1999 function_sparql_uri },
2000 /* Strings */
2001 { "SparqlRegex", -1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2002 function_sparql_regex },
2003 { "SparqlStringJoin", -1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2004 function_sparql_string_join },
2005 { "SparqlLowerCase", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2006 function_sparql_lower_case },
2007 { "SparqlUpperCase", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2008 function_sparql_upper_case },
2009 { "SparqlCaseFold", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2010 function_sparql_case_fold },
2011 {"SparqlStripPunctuation", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2012 function_sparql_strip_punctuation },
2013 { "SparqlNormalize", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2014 function_sparql_normalize },
2015 { "SparqlUnaccent", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2016 function_sparql_unaccent },
2017 { "SparqlStringBefore", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2018 function_sparql_string_before },
2019 { "SparqlStringAfter", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2020 function_sparql_string_after },
2021 { "SparqlReplace", -1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2022 function_sparql_replace },
2023 { "SparqlChecksum", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2024 function_sparql_checksum },
2025 { "SparqlLangMatches", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2026 function_sparql_langmatches },
2027 { "SparqlStrLang", 2, SQLITE_ANY | SQLITE_DETERMINISTIC,
2028 function_sparql_strlang },
2029 { "SparqlPrintIRI", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2030 function_sparql_print_iri },
2031 /* Numbers */
2032 { "SparqlCeil", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2033 function_sparql_ceil },
2034 { "SparqlFloor", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2035 function_sparql_floor },
2036 { "SparqlRand", 0, SQLITE_ANY, function_sparql_rand },
2037 /* Types */
2038 { "SparqlDataType", 1, SQLITE_ANY | SQLITE_DETERMINISTIC,
2039 function_sparql_data_type },
2040 /* UUID */
2041 { "SparqlUUID", 1, SQLITE_ANY, function_sparql_uuid },
2042 { "SparqlBNODE", -1, SQLITE_ANY | SQLITE_DETERMINISTIC, function_sparql_bnode },
2043 };
2044
2045 for (i = 0; i < G_N_ELEMENTS (functions); i++) {
2046 sqlite3_create_function (db_interface->db,
2047 functions[i].name, functions[i].n_args,
2048 functions[i].mods, db_interface,
2049 functions[i].func, NULL, NULL);
2050 }
2051 }
2052
2053 static inline void
tracker_db_interface_lock(TrackerDBInterface * iface)2054 tracker_db_interface_lock (TrackerDBInterface *iface)
2055 {
2056 if (iface->flags & TRACKER_DB_INTERFACE_USE_MUTEX)
2057 g_mutex_lock (&iface->mutex);
2058 }
2059
2060 static inline void
tracker_db_interface_unlock(TrackerDBInterface * iface)2061 tracker_db_interface_unlock (TrackerDBInterface *iface)
2062 {
2063 if (iface->flags & TRACKER_DB_INTERFACE_USE_MUTEX)
2064 g_mutex_unlock (&iface->mutex);
2065 }
2066
2067 static void
open_database(TrackerDBInterface * db_interface,GError ** error)2068 open_database (TrackerDBInterface *db_interface,
2069 GError **error)
2070 {
2071 int mode;
2072 int result;
2073 gchar *uri;
2074
2075 g_assert (db_interface->filename != NULL || db_interface->shared_cache_key != NULL);
2076
2077 if ((db_interface->flags & TRACKER_DB_INTERFACE_READONLY) == 0) {
2078 mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
2079 } else {
2080 mode = SQLITE_OPEN_READONLY;
2081 }
2082
2083 if ((db_interface->flags & TRACKER_DB_INTERFACE_IN_MEMORY) != 0) {
2084 mode |= SQLITE_OPEN_MEMORY | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_URI;
2085 uri = g_strdup_printf ("file:%s", db_interface->shared_cache_key);
2086 } else {
2087 uri = g_strdup (db_interface->filename);
2088 }
2089
2090 result = sqlite3_open_v2 (uri, &db_interface->db, mode | SQLITE_OPEN_NOMUTEX, NULL);
2091 g_free (uri);
2092
2093 if (result != SQLITE_OK) {
2094 const gchar *str;
2095
2096 str = sqlite3_errstr (result);
2097 g_set_error (error,
2098 TRACKER_DB_INTERFACE_ERROR,
2099 TRACKER_DB_OPEN_ERROR,
2100 "Could not open sqlite3 database:'%s': %s",
2101 db_interface->filename ? db_interface->filename : "memory",
2102 str);
2103 return;
2104 } else {
2105 TRACKER_NOTE (SQLITE,
2106 g_message ("Opened sqlite3 database:'%s'",
2107 db_interface->filename? db_interface->filename : "memory"));
2108 }
2109
2110 /* Set our unicode collation function */
2111 tracker_db_interface_sqlite_reset_collator (db_interface);
2112
2113 sqlite3_progress_handler (db_interface->db, 100,
2114 check_interrupt, db_interface);
2115
2116 initialize_functions (db_interface);
2117
2118 sqlite3_extended_result_codes (db_interface->db, 0);
2119 sqlite3_busy_timeout (db_interface->db, 100000);
2120
2121 #ifndef SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
2122 #warning Using sqlite3_enable_load_extension instead of SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, this is unsafe
2123 sqlite3_enable_load_extension (db_interface->db, 1);
2124 #else
2125 sqlite3_db_config (db_interface->db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
2126 #endif
2127 }
2128
2129 static gboolean
tracker_db_interface_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)2130 tracker_db_interface_initable_init (GInitable *initable,
2131 GCancellable *cancellable,
2132 GError **error)
2133 {
2134 TrackerDBInterface *db_iface;
2135 GError *internal_error = NULL;
2136
2137 db_iface = TRACKER_DB_INTERFACE (initable);
2138
2139 open_database (db_iface, &internal_error);
2140
2141 if (internal_error) {
2142 g_propagate_error (error, internal_error);
2143 return FALSE;
2144 }
2145
2146 return TRUE;
2147 }
2148
2149 static void
tracker_db_interface_initable_iface_init(GInitableIface * iface)2150 tracker_db_interface_initable_iface_init (GInitableIface *iface)
2151 {
2152 iface->init = tracker_db_interface_initable_init;
2153 }
2154
2155 static void
tracker_db_interface_sqlite_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2156 tracker_db_interface_sqlite_set_property (GObject *object,
2157 guint prop_id,
2158 const GValue *value,
2159 GParamSpec *pspec)
2160 {
2161 TrackerDBInterface *db_iface;
2162
2163 db_iface = TRACKER_DB_INTERFACE (object);
2164
2165 switch (prop_id) {
2166 case PROP_FLAGS:
2167 db_iface->flags = g_value_get_flags (value);
2168 break;
2169 case PROP_FILENAME:
2170 db_iface->filename = g_value_dup_string (value);
2171 break;
2172 case PROP_SHARED_CACHE_KEY:
2173 db_iface->shared_cache_key = g_value_dup_string (value);
2174 break;
2175 default:
2176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2177 }
2178 }
2179
2180 static void
tracker_db_interface_sqlite_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2181 tracker_db_interface_sqlite_get_property (GObject *object,
2182 guint prop_id,
2183 GValue *value,
2184 GParamSpec *pspec)
2185 {
2186 TrackerDBInterface *db_iface;
2187
2188 db_iface = TRACKER_DB_INTERFACE (object);
2189
2190 switch (prop_id) {
2191 case PROP_FLAGS:
2192 g_value_set_flags (value, db_iface->flags);
2193 break;
2194 case PROP_FILENAME:
2195 g_value_set_string (value, db_iface->filename);
2196 break;
2197 case PROP_SHARED_CACHE_KEY:
2198 g_value_set_string (value, db_iface->shared_cache_key);
2199 break;
2200 default:
2201 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2202 }
2203 }
2204
2205 static void
close_database(TrackerDBInterface * db_interface)2206 close_database (TrackerDBInterface *db_interface)
2207 {
2208 gint rc;
2209
2210 if (db_interface->dynamic_statements) {
2211 g_hash_table_unref (db_interface->dynamic_statements);
2212 db_interface->dynamic_statements = NULL;
2213 }
2214
2215 if (db_interface->replace_func_checks.syntax_check)
2216 g_regex_unref (db_interface->replace_func_checks.syntax_check);
2217 if (db_interface->replace_func_checks.replacement)
2218 g_regex_unref (db_interface->replace_func_checks.replacement);
2219 if (db_interface->replace_func_checks.unescape)
2220 g_regex_unref (db_interface->replace_func_checks.unescape);
2221
2222 if (db_interface->db) {
2223 rc = sqlite3_close (db_interface->db);
2224 if (rc != SQLITE_OK) {
2225 g_warning ("Database closed uncleanly: %s",
2226 sqlite3_errstr (rc));
2227 }
2228 }
2229 }
2230
2231 static gchar **
_fts_create_properties(GHashTable * properties)2232 _fts_create_properties (GHashTable *properties)
2233 {
2234 GHashTableIter iter;
2235 GPtrArray *cols;
2236 GList *columns;
2237 gchar *table;
2238
2239 if (g_hash_table_size (properties) == 0) {
2240 return NULL;
2241 }
2242
2243 g_hash_table_iter_init (&iter, properties);
2244 cols = g_ptr_array_new ();
2245
2246 while (g_hash_table_iter_next (&iter, (gpointer *) &table,
2247 (gpointer *) &columns)) {
2248 while (columns) {
2249 g_ptr_array_add (cols, g_strdup (columns->data));
2250 columns = columns->next;
2251 }
2252 }
2253
2254 g_ptr_array_add (cols, NULL);
2255
2256 return (gchar **) g_ptr_array_free (cols, FALSE);
2257 }
2258
2259 gboolean
tracker_db_interface_sqlite_fts_init(TrackerDBInterface * db_interface,const gchar * database,GHashTable * properties,GHashTable * multivalued,gboolean create,GError ** error)2260 tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
2261 const gchar *database,
2262 GHashTable *properties,
2263 GHashTable *multivalued,
2264 gboolean create,
2265 GError **error)
2266 {
2267 GError *inner_error = NULL;
2268 GStrv fts_columns;
2269
2270 if (!tracker_fts_init_db (db_interface->db, db_interface,
2271 db_interface->flags, properties, error))
2272 return FALSE;
2273
2274 if (create &&
2275 !tracker_fts_create_table (db_interface->db, database, "fts5",
2276 properties, multivalued,
2277 &inner_error)) {
2278 g_propagate_prefixed_error (error,
2279 inner_error,
2280 "FTS tables creation failed: ");
2281 return FALSE;
2282 }
2283
2284 fts_columns = _fts_create_properties (properties);
2285
2286 if (fts_columns) {
2287 GString *fts_properties;
2288 gint i;
2289
2290 fts_properties = g_string_new (NULL);
2291
2292 for (i = 0; fts_columns[i] != NULL; i++) {
2293 g_string_append_printf (fts_properties, ", \"%s\"",
2294 fts_columns[i]);
2295 }
2296
2297 g_free (db_interface->fts_properties);
2298 db_interface->fts_properties = g_string_free (fts_properties,
2299 FALSE);
2300 g_strfreev (fts_columns);
2301 }
2302
2303 return TRUE;
2304 }
2305
2306 gboolean
tracker_db_interface_sqlite_fts_delete_table(TrackerDBInterface * db_interface,const gchar * database,GError ** error)2307 tracker_db_interface_sqlite_fts_delete_table (TrackerDBInterface *db_interface,
2308 const gchar *database,
2309 GError **error)
2310 {
2311 return tracker_fts_delete_table (db_interface->db, database, "fts5", error);
2312 }
2313
2314 gboolean
tracker_db_interface_sqlite_fts_alter_table(TrackerDBInterface * db_interface,const gchar * database,GHashTable * properties,GHashTable * multivalued,GError ** error)2315 tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *db_interface,
2316 const gchar *database,
2317 GHashTable *properties,
2318 GHashTable *multivalued,
2319 GError **error)
2320 {
2321 return tracker_fts_alter_table (db_interface->db, database, "fts5",
2322 properties, multivalued, error);
2323 }
2324
2325 static gchar *
tracker_db_interface_sqlite_fts_create_query(TrackerDBInterface * db_interface,const gchar * database,gboolean delete,const gchar ** properties)2326 tracker_db_interface_sqlite_fts_create_query (TrackerDBInterface *db_interface,
2327 const gchar *database,
2328 gboolean delete,
2329 const gchar **properties)
2330 {
2331 GString *insert_str, *values_str;
2332 gint i;
2333
2334 insert_str = g_string_new (NULL);
2335 g_string_append_printf (insert_str, "INSERT INTO \"%s\".fts5 (", database);
2336 values_str = g_string_new (NULL);
2337
2338 if (delete) {
2339 g_string_append (insert_str, "fts5,");
2340 g_string_append (values_str, "'delete',");
2341 }
2342
2343 g_string_append (insert_str, "rowid");
2344 g_string_append (values_str, "?");
2345
2346 for (i = 0; properties[i] != NULL; i++) {
2347 g_string_append_printf (insert_str, ",\"%s\"", properties[i]);
2348 g_string_append (values_str, ",?");
2349 }
2350
2351 g_string_append_printf (insert_str, ") VALUES (%s)", values_str->str);
2352 g_string_free (values_str, TRUE);
2353
2354 return g_string_free (insert_str, FALSE);
2355 }
2356
2357 static gchar *
tracker_db_interface_sqlite_fts_create_delete_all_query(TrackerDBInterface * db_interface,const gchar * database)2358 tracker_db_interface_sqlite_fts_create_delete_all_query (TrackerDBInterface *db_interface,
2359 const gchar *database)
2360 {
2361 GString *insert_str;
2362
2363 insert_str = g_string_new (NULL);
2364 g_string_append_printf (insert_str,
2365 "INSERT INTO \"%s\".fts5 (fts5, rowid %s) "
2366 "SELECT 'delete', rowid %s FROM \"%s\".fts_view "
2367 "WHERE rowid = ? AND COALESCE(NULL %s) IS NOT NULL",
2368 database,
2369 db_interface->fts_properties,
2370 db_interface->fts_properties,
2371 database,
2372 db_interface->fts_properties);
2373 return g_string_free (insert_str, FALSE);
2374 }
2375
2376 gboolean
tracker_db_interface_sqlite_fts_update_text(TrackerDBInterface * db_interface,const gchar * database,int id,const gchar ** properties,const gchar ** text)2377 tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
2378 const gchar *database,
2379 int id,
2380 const gchar **properties,
2381 const gchar **text)
2382 {
2383 TrackerDBStatement *stmt;
2384 GError *error = NULL;
2385 gchar *query;
2386 gint i;
2387
2388 query = tracker_db_interface_sqlite_fts_create_query (db_interface,
2389 database,
2390 FALSE, properties);
2391 stmt = tracker_db_interface_create_statement (db_interface,
2392 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
2393 &error,
2394 query);
2395 g_free (query);
2396
2397 if (!stmt || error) {
2398 if (error) {
2399 g_warning ("Could not create FTS insert statement: %s\n",
2400 error->message);
2401 g_error_free (error);
2402 }
2403 return FALSE;
2404 }
2405
2406 tracker_db_statement_bind_int (stmt, 0, id);
2407 for (i = 0; text[i] != NULL; i++) {
2408 tracker_db_statement_bind_text (stmt, i + 1, text[i]);
2409 }
2410
2411 tracker_db_statement_execute (stmt, &error);
2412 g_object_unref (stmt);
2413
2414 if (error) {
2415 g_warning ("Could not insert FTS text: %s", error->message);
2416 g_error_free (error);
2417 return FALSE;
2418 }
2419
2420 return TRUE;
2421 }
2422
2423 gboolean
tracker_db_interface_sqlite_fts_delete_text(TrackerDBInterface * db_interface,const gchar * database,int rowid,const gchar ** properties,const gchar ** old_text)2424 tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *db_interface,
2425 const gchar *database,
2426 int rowid,
2427 const gchar **properties,
2428 const gchar **old_text)
2429 {
2430 TrackerDBStatement *stmt;
2431 GError *error = NULL;
2432 gchar *query;
2433 gboolean has_text = FALSE;
2434 gint i;
2435
2436 for (i = 0; old_text[i] != NULL; i++)
2437 has_text |= old_text[i] && *old_text[i];
2438
2439 if (!has_text)
2440 return TRUE;
2441
2442 query = tracker_db_interface_sqlite_fts_create_query (db_interface,
2443 database,
2444 TRUE, properties);
2445 stmt = tracker_db_interface_create_statement (db_interface,
2446 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
2447 &error,
2448 query);
2449 g_free (query);
2450
2451 if (!stmt || error) {
2452 g_warning ("Could not create FTS delete statement: %s",
2453 error ? error->message : "No error given");
2454 g_clear_error (&error);
2455 return FALSE;
2456 }
2457
2458 tracker_db_statement_bind_int (stmt, 0, rowid);
2459 for (i = 0; old_text[i] != NULL; i++)
2460 tracker_db_statement_bind_text (stmt, i + 1, old_text[i]);
2461
2462 tracker_db_statement_execute (stmt, &error);
2463 g_object_unref (stmt);
2464
2465 if (error) {
2466 g_warning ("Could not delete FTS text: %s", error->message);
2467 g_error_free (error);
2468 return FALSE;
2469 }
2470
2471 return TRUE;
2472 }
2473
2474 gboolean
tracker_db_interface_sqlite_fts_delete_id(TrackerDBInterface * db_interface,const gchar * database,int id)2475 tracker_db_interface_sqlite_fts_delete_id (TrackerDBInterface *db_interface,
2476 const gchar *database,
2477 int id)
2478 {
2479 TrackerDBStatement *stmt;
2480 GError *error = NULL;
2481 gchar *query;
2482
2483 query = tracker_db_interface_sqlite_fts_create_delete_all_query (db_interface, database);
2484 stmt = tracker_db_interface_create_statement (db_interface,
2485 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
2486 &error,
2487 query);
2488 g_free (query);
2489
2490 if (!stmt || error) {
2491 if (error) {
2492 g_warning ("Could not create FTS delete statement: %s",
2493 error->message);
2494 g_error_free (error);
2495 }
2496 return FALSE;
2497 }
2498
2499 tracker_db_statement_bind_int (stmt, 0, id);
2500 tracker_db_statement_execute (stmt, &error);
2501 g_object_unref (stmt);
2502
2503 if (error) {
2504 g_warning ("Could not delete FTS content: %s", error->message);
2505 g_error_free (error);
2506 return FALSE;
2507 }
2508
2509 return TRUE;
2510 }
2511
2512 gboolean
tracker_db_interface_sqlite_fts_rebuild_tokens(TrackerDBInterface * interface,const gchar * database,GError ** error)2513 tracker_db_interface_sqlite_fts_rebuild_tokens (TrackerDBInterface *interface,
2514 const gchar *database,
2515 GError **error)
2516 {
2517 return tracker_fts_rebuild_tokens (interface->db, database, "fts5", error);
2518 }
2519
2520 void
tracker_db_interface_sqlite_reset_collator(TrackerDBInterface * db_interface)2521 tracker_db_interface_sqlite_reset_collator (TrackerDBInterface *db_interface)
2522 {
2523 TRACKER_NOTE (SQLITE, g_message ("Resetting collator in db interface %p", db_interface));
2524
2525 /* This will overwrite any other collation set before, if any */
2526 if (sqlite3_create_collation_v2 (db_interface->db,
2527 TRACKER_COLLATION_NAME,
2528 SQLITE_UTF8,
2529 tracker_collation_init (),
2530 tracker_collation_utf8,
2531 tracker_collation_shutdown) != SQLITE_OK)
2532 {
2533 g_critical ("Couldn't set collation function: %s",
2534 sqlite3_errmsg (db_interface->db));
2535 }
2536
2537 if (sqlite3_create_collation_v2 (db_interface->db,
2538 TRACKER_TITLE_COLLATION_NAME,
2539 SQLITE_UTF8,
2540 tracker_collation_init (),
2541 tracker_collation_utf8_title,
2542 tracker_collation_shutdown) != SQLITE_OK) {
2543 g_critical ("Couldn't set title collation function: %s",
2544 sqlite3_errmsg (db_interface->db));
2545 }
2546 }
2547
2548 gboolean
tracker_db_interface_sqlite_wal_checkpoint(TrackerDBInterface * interface,gboolean blocking,GError ** error)2549 tracker_db_interface_sqlite_wal_checkpoint (TrackerDBInterface *interface,
2550 gboolean blocking,
2551 GError **error)
2552 {
2553 int return_val;
2554
2555 TRACKER_NOTE (SQLITE, g_message ("Checkpointing database (%s)...", blocking ? "blocking" : "non-blocking"));
2556
2557 return_val = sqlite3_wal_checkpoint_v2 (interface->db, NULL,
2558 blocking ? SQLITE_CHECKPOINT_FULL : SQLITE_CHECKPOINT_PASSIVE,
2559 NULL, NULL);
2560
2561 if (return_val != SQLITE_OK) {
2562 g_set_error (error,
2563 TRACKER_DB_INTERFACE_ERROR,
2564 TRACKER_DB_QUERY_ERROR,
2565 "%s", sqlite3_errstr (return_val));
2566 return FALSE;
2567 }
2568
2569 TRACKER_NOTE (SQLITE, g_message ("Checkpointing complete"));
2570 return TRUE;
2571 }
2572
2573 static void
tracker_db_interface_sqlite_finalize(GObject * object)2574 tracker_db_interface_sqlite_finalize (GObject *object)
2575 {
2576 TrackerDBInterface *db_interface;
2577
2578 db_interface = TRACKER_DB_INTERFACE (object);
2579
2580 close_database (db_interface);
2581 g_free (db_interface->fts_properties);
2582
2583 TRACKER_NOTE (SQLITE, g_message ("Closed sqlite3 database:'%s'", db_interface->filename));
2584
2585 g_free (db_interface->filename);
2586 g_free (db_interface->shared_cache_key);
2587
2588 if (db_interface->user_data && db_interface->user_data_destroy_notify)
2589 db_interface->user_data_destroy_notify (db_interface->user_data);
2590
2591 G_OBJECT_CLASS (tracker_db_interface_parent_class)->finalize (object);
2592 }
2593
2594 static void
tracker_db_interface_class_init(TrackerDBInterfaceClass * class)2595 tracker_db_interface_class_init (TrackerDBInterfaceClass *class)
2596 {
2597 GObjectClass *object_class = G_OBJECT_CLASS (class);
2598
2599 object_class->set_property = tracker_db_interface_sqlite_set_property;
2600 object_class->get_property = tracker_db_interface_sqlite_get_property;
2601 object_class->finalize = tracker_db_interface_sqlite_finalize;
2602
2603 g_object_class_install_property (object_class,
2604 PROP_FILENAME,
2605 g_param_spec_string ("filename",
2606 "DB filename",
2607 "DB filename",
2608 NULL,
2609 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2610
2611 g_object_class_install_property (object_class,
2612 PROP_FLAGS,
2613 g_param_spec_flags ("flags",
2614 "Flags",
2615 "Interface flags",
2616 TRACKER_TYPE_DB_INTERFACE_FLAGS, 0,
2617 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2618
2619 g_object_class_install_property (object_class,
2620 PROP_SHARED_CACHE_KEY,
2621 g_param_spec_string ("shared-cache-key",
2622 "Shared cache key",
2623 "Shared cache key",
2624 NULL,
2625 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2626 }
2627
2628 static void
tracker_db_interface_init(TrackerDBInterface * db_interface)2629 tracker_db_interface_init (TrackerDBInterface *db_interface)
2630 {
2631 db_interface->dynamic_statements = g_hash_table_new_full (g_str_hash, g_str_equal,
2632 NULL,
2633 (GDestroyNotify) g_object_unref);
2634 }
2635
2636 void
tracker_db_interface_set_max_stmt_cache_size(TrackerDBInterface * db_interface,TrackerDBStatementCacheType cache_type,guint max_size)2637 tracker_db_interface_set_max_stmt_cache_size (TrackerDBInterface *db_interface,
2638 TrackerDBStatementCacheType cache_type,
2639 guint max_size)
2640 {
2641 TrackerDBStatementLru *stmt_lru;
2642
2643 if (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE) {
2644 stmt_lru = &db_interface->update_stmt_lru;
2645 } else if (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT) {
2646 stmt_lru = &db_interface->select_stmt_lru;
2647 } else {
2648 return;
2649 }
2650
2651 /* Must be larger than 2 to make sense (to have a tail and head) */
2652 if (max_size > 2) {
2653 stmt_lru->max = max_size;
2654 } else {
2655 stmt_lru->max = 3;
2656 }
2657 }
2658
2659 static sqlite3_stmt *
tracker_db_interface_prepare_stmt(TrackerDBInterface * db_interface,const gchar * full_query,GError ** error)2660 tracker_db_interface_prepare_stmt (TrackerDBInterface *db_interface,
2661 const gchar *full_query,
2662 GError **error)
2663 {
2664 sqlite3_stmt *sqlite_stmt;
2665 int retval;
2666
2667 retval = sqlite3_prepare_v2 (db_interface->db, full_query, -1, &sqlite_stmt, NULL);
2668
2669 if (retval != SQLITE_OK) {
2670 sqlite_stmt = NULL;
2671
2672 if (retval == SQLITE_INTERRUPT) {
2673 g_set_error (error,
2674 TRACKER_DB_INTERFACE_ERROR,
2675 TRACKER_DB_INTERRUPTED,
2676 "Interrupted");
2677 } else {
2678 g_set_error (error,
2679 TRACKER_DB_INTERFACE_ERROR,
2680 TRACKER_DB_QUERY_ERROR,
2681 "%s",
2682 sqlite3_errmsg (db_interface->db));
2683 }
2684 }
2685
2686 return sqlite_stmt;
2687 }
2688
2689 static TrackerDBStatement *
tracker_db_interface_lru_lookup(TrackerDBInterface * db_interface,TrackerDBStatementCacheType * cache_type,const gchar * full_query)2690 tracker_db_interface_lru_lookup (TrackerDBInterface *db_interface,
2691 TrackerDBStatementCacheType *cache_type,
2692 const gchar *full_query)
2693 {
2694 TrackerDBStatement *stmt;
2695
2696 g_return_val_if_fail (*cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE ||
2697 *cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
2698 NULL);
2699
2700 /* There are three kinds of queries:
2701 * a) Cached queries: SELECT and UPDATE ones (cache_type)
2702 * b) Non-Cached queries: NONE ones (cache_type)
2703 * c) Forced Non-Cached: in case of a stmt being already in use, we can't
2704 * reuse it (you can't use two different loops on a sqlite3_stmt, of
2705 * course). This happens with recursive uses of a cursor, for example.
2706 */
2707
2708 stmt = g_hash_table_lookup (db_interface->dynamic_statements,
2709 full_query);
2710 if (!stmt) {
2711 /* Query not in LRU */
2712 return NULL;
2713 }
2714
2715 /* a) Cached */
2716
2717 if (stmt && stmt->stmt_is_owned) {
2718 /* c) Forced non-cached
2719 * prepared statement is owned somewhere else, create new uncached one
2720 */
2721 stmt = NULL;
2722 /* Make sure to set cache_type here, to avoid replacing
2723 * the current statement.
2724 */
2725 *cache_type = TRACKER_DB_STATEMENT_CACHE_TYPE_NONE;
2726 }
2727
2728 return stmt;
2729 }
2730
2731 static void
tracker_db_interface_lru_insert_unchecked(TrackerDBInterface * db_interface,TrackerDBStatementCacheType cache_type,TrackerDBStatement * stmt)2732 tracker_db_interface_lru_insert_unchecked (TrackerDBInterface *db_interface,
2733 TrackerDBStatementCacheType cache_type,
2734 TrackerDBStatement *stmt)
2735 {
2736 TrackerDBStatementLru *stmt_lru;
2737
2738 g_return_if_fail (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE ||
2739 cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT);
2740
2741 /* LRU holds a reference to the stmt */
2742 stmt_lru = cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE ?
2743 &db_interface->update_stmt_lru : &db_interface->select_stmt_lru;
2744
2745 /* use replace instead of insert to make sure we store the string that
2746 * belongs to the right sqlite statement to ensure the lifetime of the string
2747 * matches the statement
2748 */
2749 g_hash_table_replace (db_interface->dynamic_statements,
2750 (gpointer) sqlite3_sql (stmt->stmt),
2751 g_object_ref_sink (stmt));
2752
2753 /* So the ring looks a bit like this: *
2754 * *
2755 * .--tail .--head *
2756 * | | *
2757 * [p-n] -> [p-n] -> [p-n] -> [p-n] *
2758 * ^ | *
2759 * `- [n-p] <- [n-p] <--------' *
2760 * */
2761
2762 if (stmt_lru->size == 0) {
2763 stmt_lru->head = stmt;
2764 stmt_lru->tail = stmt;
2765 } else if (stmt_lru->size >= stmt_lru->max) {
2766 TrackerDBStatement *new_head;
2767
2768 /* We reached max-size of the LRU stmt cache. Destroy current
2769 * least recently used (stmt_lru.head) and fix the ring. For
2770 * that we take out the current head, and close the ring.
2771 * Then we assign head->next as new head.
2772 */
2773 new_head = stmt_lru->head->next;
2774 g_hash_table_remove (db_interface->dynamic_statements,
2775 (gpointer) sqlite3_sql (stmt_lru->head->stmt));
2776 stmt_lru->size--;
2777 stmt_lru->head = new_head;
2778 }
2779
2780 /* Set the current stmt (which is always new here) as the new tail
2781 * (new most recent used). We insert current stmt between head and
2782 * current tail, and we set tail to current stmt.
2783 */
2784 stmt_lru->size++;
2785 stmt->next = stmt_lru->head;
2786 stmt_lru->head->prev = stmt;
2787
2788 stmt_lru->tail->next = stmt;
2789 stmt->prev = stmt_lru->tail;
2790 stmt_lru->tail = stmt;
2791 }
2792
2793 static void
tracker_db_interface_lru_update(TrackerDBInterface * db_interface,TrackerDBStatementCacheType cache_type,TrackerDBStatement * stmt)2794 tracker_db_interface_lru_update (TrackerDBInterface *db_interface,
2795 TrackerDBStatementCacheType cache_type,
2796 TrackerDBStatement *stmt)
2797 {
2798 TrackerDBStatementLru *stmt_lru;
2799
2800 g_return_if_fail (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE ||
2801 cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT);
2802
2803 stmt_lru = cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE ?
2804 &db_interface->update_stmt_lru : &db_interface->select_stmt_lru;
2805
2806 tracker_db_statement_sqlite_reset (stmt);
2807
2808 if (stmt == stmt_lru->head) {
2809 /* Current stmt is least recently used, shift head and tail
2810 * of the ring to efficiently make it most recently used.
2811 */
2812 stmt_lru->head = stmt_lru->head->next;
2813 stmt_lru->tail = stmt_lru->tail->next;
2814 } else if (stmt != stmt_lru->tail) {
2815 /* Current statement isn't most recently used, make it most
2816 * recently used now (less efficient way than above).
2817 */
2818
2819 /* Take stmt out of the list and close the ring */
2820 stmt->prev->next = stmt->next;
2821 stmt->next->prev = stmt->prev;
2822
2823 /* Put stmt as tail (most recent used) */
2824 stmt->next = stmt_lru->head;
2825 stmt_lru->head->prev = stmt;
2826 stmt->prev = stmt_lru->tail;
2827 stmt_lru->tail->next = stmt;
2828 stmt_lru->tail = stmt;
2829 }
2830
2831 /* if (stmt == tail), it's already the most recently used in the
2832 * ring, so in this case we do nothing of course */
2833 }
2834
2835 TrackerDBStatement *
tracker_db_interface_create_statement(TrackerDBInterface * db_interface,TrackerDBStatementCacheType cache_type,GError ** error,const gchar * query)2836 tracker_db_interface_create_statement (TrackerDBInterface *db_interface,
2837 TrackerDBStatementCacheType cache_type,
2838 GError **error,
2839 const gchar *query)
2840 {
2841 TrackerDBStatement *stmt = NULL;
2842
2843 g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (db_interface), NULL);
2844
2845 tracker_db_interface_lock (db_interface);
2846
2847 if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
2848 stmt = tracker_db_interface_lru_lookup (db_interface, &cache_type,
2849 query);
2850 }
2851
2852 if (!stmt) {
2853 sqlite3_stmt *sqlite_stmt;
2854
2855 sqlite_stmt = tracker_db_interface_prepare_stmt (db_interface,
2856 query,
2857 error);
2858 if (!sqlite_stmt) {
2859 tracker_db_interface_unlock (db_interface);
2860 return NULL;
2861 }
2862
2863 stmt = tracker_db_statement_sqlite_new (db_interface,
2864 sqlite_stmt);
2865
2866 if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
2867 tracker_db_interface_lru_insert_unchecked (db_interface,
2868 cache_type,
2869 stmt);
2870 }
2871 } else if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
2872 tracker_db_interface_lru_update (db_interface, cache_type,
2873 stmt);
2874 }
2875
2876 stmt->stmt_is_owned = TRUE;
2877
2878 tracker_db_interface_unlock (db_interface);
2879
2880 return g_object_ref_sink (stmt);
2881 }
2882
2883 TrackerDBStatement *
tracker_db_interface_create_vstatement(TrackerDBInterface * db_interface,TrackerDBStatementCacheType cache_type,GError ** error,const gchar * query,...)2884 tracker_db_interface_create_vstatement (TrackerDBInterface *db_interface,
2885 TrackerDBStatementCacheType cache_type,
2886 GError **error,
2887 const gchar *query,
2888 ...)
2889 {
2890 TrackerDBStatement *stmt;
2891 va_list args;
2892 gchar *full_query;
2893
2894 g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (db_interface), NULL);
2895
2896 va_start (args, query);
2897 full_query = g_strdup_vprintf (query, args);
2898 va_end (args);
2899
2900 stmt = tracker_db_interface_create_statement (db_interface, cache_type,
2901 error, full_query);
2902 g_free (full_query);
2903
2904 return stmt;
2905 }
2906
2907 static gboolean
execute_stmt(TrackerDBInterface * interface,sqlite3_stmt * stmt,GCancellable * cancellable,GError ** error)2908 execute_stmt (TrackerDBInterface *interface,
2909 sqlite3_stmt *stmt,
2910 GCancellable *cancellable,
2911 GError **error)
2912 {
2913 gint result;
2914
2915 result = SQLITE_OK;
2916
2917 tracker_db_interface_ref_use (interface);
2918
2919 #ifdef G_ENABLE_DEBUG
2920 if (TRACKER_DEBUG_CHECK (SQL_STATEMENTS)) {
2921 gchar *full_query;
2922
2923 full_query = sqlite3_expanded_sql (stmt);
2924
2925 if (full_query) {
2926 g_message ("Executing update: '%s'", full_query);
2927 sqlite3_free (full_query);
2928 } else {
2929 g_message ("Executing update: '%s'",
2930 sqlite3_sql (stmt));
2931 }
2932 }
2933 #endif
2934
2935 while (result == SQLITE_OK ||
2936 result == SQLITE_ROW) {
2937
2938 if (g_cancellable_is_cancelled (cancellable)) {
2939 result = SQLITE_INTERRUPT;
2940 sqlite3_reset (stmt);
2941 } else {
2942 /* only one statement can be active at the same time per interface */
2943 interface->cancellable = cancellable;
2944 result = stmt_step (stmt);
2945
2946 interface->cancellable = NULL;
2947 }
2948
2949 switch (result) {
2950 case SQLITE_ERROR:
2951 sqlite3_reset (stmt);
2952 break;
2953 case SQLITE_ROW:
2954 break;
2955 default:
2956 break;
2957 }
2958 }
2959
2960 tracker_db_interface_unref_use (interface);
2961
2962 if (result != SQLITE_DONE) {
2963 /* This is rather fatal */
2964 if (errno != ENOSPC &&
2965 (sqlite3_errcode (interface->db) == SQLITE_IOERR ||
2966 sqlite3_errcode (interface->db) == SQLITE_CORRUPT ||
2967 sqlite3_errcode (interface->db) == SQLITE_NOTADB)) {
2968
2969 g_critical ("SQLite error: %s (errno: %s)",
2970 sqlite3_errmsg (interface->db),
2971 g_strerror (errno));
2972 return FALSE;
2973 }
2974
2975 if (!error) {
2976 g_warning ("Could not perform SQLite operation, error:%d->'%s'",
2977 sqlite3_errcode (interface->db),
2978 sqlite3_errmsg (interface->db));
2979 } else {
2980 if (result == SQLITE_INTERRUPT) {
2981 g_set_error (error,
2982 TRACKER_DB_INTERFACE_ERROR,
2983 TRACKER_DB_INTERRUPTED,
2984 "Interrupted");
2985 } else if (result == SQLITE_CONSTRAINT) {
2986 g_set_error (error,
2987 TRACKER_DB_INTERFACE_ERROR,
2988 TRACKER_DB_CONSTRAINT,
2989 "Constraint would be broken");
2990 } else {
2991 g_set_error (error,
2992 TRACKER_DB_INTERFACE_ERROR,
2993 errno != ENOSPC ? TRACKER_DB_QUERY_ERROR : TRACKER_DB_NO_SPACE,
2994 "%s%s%s%s",
2995 sqlite3_errmsg (interface->db),
2996 errno != 0 ? " (strerror of errno (not necessarily related): " : "",
2997 errno != 0 ? g_strerror (errno) : "",
2998 errno != 0 ? ")" : "");
2999 }
3000 }
3001 }
3002
3003 return result == SQLITE_DONE;
3004 }
3005
3006 void
tracker_db_interface_execute_vquery(TrackerDBInterface * db_interface,GError ** error,const gchar * query,va_list args)3007 tracker_db_interface_execute_vquery (TrackerDBInterface *db_interface,
3008 GError **error,
3009 const gchar *query,
3010 va_list args)
3011 {
3012 gchar *full_query;
3013 sqlite3_stmt *stmt;
3014
3015 tracker_db_interface_lock (db_interface);
3016
3017 full_query = g_strdup_vprintf (query, args);
3018 stmt = tracker_db_interface_prepare_stmt (db_interface,
3019 full_query,
3020 error);
3021 g_free (full_query);
3022 if (stmt) {
3023 execute_stmt (db_interface, stmt, NULL, error);
3024 sqlite3_finalize (stmt);
3025 }
3026
3027 tracker_db_interface_unlock (db_interface);
3028 }
3029
3030 TrackerDBInterface *
tracker_db_interface_sqlite_new(const gchar * filename,const gchar * shared_cache_key,TrackerDBInterfaceFlags flags,GError ** error)3031 tracker_db_interface_sqlite_new (const gchar *filename,
3032 const gchar *shared_cache_key,
3033 TrackerDBInterfaceFlags flags,
3034 GError **error)
3035 {
3036 TrackerDBInterface *object;
3037 GError *internal_error = NULL;
3038
3039 object = g_initable_new (TRACKER_TYPE_DB_INTERFACE,
3040 NULL,
3041 &internal_error,
3042 "filename", filename,
3043 "flags", flags,
3044 "shared-cache-key", shared_cache_key,
3045 NULL);
3046
3047 if (internal_error) {
3048 g_propagate_error (error, internal_error);
3049 return NULL;
3050 }
3051
3052 return object;
3053 }
3054
3055 gint64
tracker_db_interface_sqlite_get_last_insert_id(TrackerDBInterface * interface)3056 tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterface *interface)
3057 {
3058 g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), 0);
3059
3060 return (gint64) sqlite3_last_insert_rowid (interface->db);
3061 }
3062
3063 static void
tracker_db_statement_finalize(GObject * object)3064 tracker_db_statement_finalize (GObject *object)
3065 {
3066 TrackerDBStatement *stmt;
3067
3068 stmt = TRACKER_DB_STATEMENT (object);
3069
3070 /* A cursor was still open while we're being finalized, because a cursor
3071 * holds its own reference, this means that somebody is unreffing a stmt
3072 * too often. We mustn't sqlite3_finalize the priv->stmt in this case,
3073 * though. It would crash&burn the cursor. */
3074
3075 g_assert (!stmt->stmt_is_used);
3076
3077 sqlite3_finalize (stmt->stmt);
3078
3079 G_OBJECT_CLASS (tracker_db_statement_parent_class)->finalize (object);
3080 }
3081
3082 static void
tracker_db_statement_class_init(TrackerDBStatementClass * class)3083 tracker_db_statement_class_init (TrackerDBStatementClass *class)
3084 {
3085 GObjectClass *object_class = G_OBJECT_CLASS (class);
3086
3087 object_class->finalize = tracker_db_statement_finalize;
3088 }
3089
3090 static TrackerDBStatement *
tracker_db_statement_sqlite_new(TrackerDBInterface * db_interface,sqlite3_stmt * sqlite_stmt)3091 tracker_db_statement_sqlite_new (TrackerDBInterface *db_interface,
3092 sqlite3_stmt *sqlite_stmt)
3093 {
3094 TrackerDBStatement *stmt;
3095
3096 stmt = g_object_new (TRACKER_TYPE_DB_STATEMENT, NULL);
3097
3098 stmt->db_interface = db_interface;
3099 stmt->stmt = sqlite_stmt;
3100 stmt->stmt_is_used = FALSE;
3101
3102 return stmt;
3103 }
3104
3105 static TrackerDBStatement *
tracker_db_statement_sqlite_grab(TrackerDBStatement * stmt)3106 tracker_db_statement_sqlite_grab (TrackerDBStatement *stmt)
3107 {
3108 g_assert (!stmt->stmt_is_used);
3109 g_assert (stmt->stmt_is_owned);
3110 stmt->stmt_is_used = TRUE;
3111 g_object_ref (stmt->db_interface);
3112 return g_object_ref (stmt);
3113 }
3114
3115 static void
tracker_db_statement_sqlite_release(TrackerDBStatement * stmt)3116 tracker_db_statement_sqlite_release (TrackerDBStatement *stmt)
3117 {
3118 TrackerDBInterface *iface = stmt->db_interface;
3119
3120 g_assert (stmt->stmt_is_owned);
3121
3122 stmt->stmt_is_owned = FALSE;
3123
3124 if (stmt->stmt_is_used) {
3125 stmt->stmt_is_used = FALSE;
3126 tracker_db_statement_sqlite_reset (stmt);
3127 g_object_unref (stmt);
3128 g_object_unref (iface);
3129 }
3130 }
3131
3132 static void
tracker_db_cursor_close(TrackerDBCursor * cursor)3133 tracker_db_cursor_close (TrackerDBCursor *cursor)
3134 {
3135 TrackerDBInterface *iface;
3136
3137 g_return_if_fail (TRACKER_IS_DB_CURSOR (cursor));
3138
3139 if (cursor->ref_stmt == NULL) {
3140 /* already closed */
3141 return;
3142 }
3143
3144 iface = cursor->ref_stmt->db_interface;
3145
3146 g_object_ref (iface);
3147
3148 tracker_db_interface_lock (iface);
3149 g_clear_pointer (&cursor->ref_stmt, tracker_db_statement_sqlite_release);
3150 tracker_db_interface_unlock (iface);
3151
3152 tracker_db_interface_unref_use (iface);
3153
3154 g_object_unref (iface);
3155 }
3156
3157 static void
tracker_db_cursor_finalize(GObject * object)3158 tracker_db_cursor_finalize (GObject *object)
3159 {
3160 TrackerDBCursor *cursor;
3161 int i;
3162
3163 cursor = TRACKER_DB_CURSOR (object);
3164
3165 tracker_db_cursor_close (cursor);
3166
3167 g_free (cursor->types);
3168
3169 for (i = 0; i < cursor->n_variable_names; i++) {
3170 g_free (cursor->variable_names[i]);
3171 }
3172 g_free (cursor->variable_names);
3173
3174 G_OBJECT_CLASS (tracker_db_cursor_parent_class)->finalize (object);
3175 }
3176
3177 static void
tracker_db_cursor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)3178 tracker_db_cursor_get_property (GObject *object,
3179 guint prop_id,
3180 GValue *value,
3181 GParamSpec *pspec)
3182 {
3183 TrackerDBCursor *cursor = TRACKER_DB_CURSOR (object);
3184 switch (prop_id) {
3185 case TRACKER_DB_CURSOR_PROP_N_COLUMNS:
3186 g_value_set_int (value, tracker_db_cursor_get_n_columns (cursor));
3187 break;
3188 default:
3189 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3190 }
3191 }
3192
3193 static void
tracker_db_cursor_iter_next_thread(GTask * task,gpointer object,gpointer task_data,GCancellable * cancellable)3194 tracker_db_cursor_iter_next_thread (GTask *task,
3195 gpointer object,
3196 gpointer task_data,
3197 GCancellable *cancellable)
3198 {
3199 /* run in thread */
3200
3201 TrackerDBCursor *cursor = object;
3202 GError *error = NULL;
3203 gboolean result;
3204
3205 result = db_cursor_iter_next (cursor, cancellable, &error);
3206 if (error) {
3207 g_task_return_error (task, error);
3208 } else {
3209 g_task_return_boolean (task, result);
3210 }
3211 }
3212
3213 static void
tracker_db_cursor_iter_next_async(TrackerDBCursor * cursor,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)3214 tracker_db_cursor_iter_next_async (TrackerDBCursor *cursor,
3215 GCancellable *cancellable,
3216 GAsyncReadyCallback callback,
3217 gpointer user_data)
3218 {
3219 GTask *task;
3220
3221 task = g_task_new (G_OBJECT (cursor), cancellable, callback, user_data);
3222 g_task_run_in_thread (task, tracker_db_cursor_iter_next_thread);
3223 g_object_unref (task);
3224 }
3225
3226 static gboolean
tracker_db_cursor_iter_next_finish(TrackerDBCursor * cursor,GAsyncResult * res,GError ** error)3227 tracker_db_cursor_iter_next_finish (TrackerDBCursor *cursor,
3228 GAsyncResult *res,
3229 GError **error)
3230 {
3231 return g_task_propagate_boolean (G_TASK (res), error);
3232 }
3233
3234 static void
tracker_db_cursor_class_init(TrackerDBCursorClass * class)3235 tracker_db_cursor_class_init (TrackerDBCursorClass *class)
3236 {
3237 GObjectClass *object_class = G_OBJECT_CLASS (class);
3238 TrackerSparqlCursorClass *sparql_cursor_class = TRACKER_SPARQL_CURSOR_CLASS (class);
3239
3240 object_class->finalize = tracker_db_cursor_finalize;
3241 object_class->get_property = tracker_db_cursor_get_property;
3242
3243 sparql_cursor_class->get_value_type = (TrackerSparqlValueType (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_value_type;
3244 sparql_cursor_class->get_variable_name = (const gchar * (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_variable_name;
3245 sparql_cursor_class->get_n_columns = (gint (*) (TrackerSparqlCursor *)) tracker_db_cursor_get_n_columns;
3246 sparql_cursor_class->get_string = (const gchar * (*) (TrackerSparqlCursor *, gint, glong*)) tracker_db_cursor_get_string;
3247 sparql_cursor_class->next = (gboolean (*) (TrackerSparqlCursor *, GCancellable *, GError **)) tracker_db_cursor_iter_next;
3248 sparql_cursor_class->next_async = (void (*) (TrackerSparqlCursor *, GCancellable *, GAsyncReadyCallback, gpointer)) tracker_db_cursor_iter_next_async;
3249 sparql_cursor_class->next_finish = (gboolean (*) (TrackerSparqlCursor *, GAsyncResult *, GError **)) tracker_db_cursor_iter_next_finish;
3250 sparql_cursor_class->rewind = (void (*) (TrackerSparqlCursor *)) tracker_db_cursor_rewind;
3251 sparql_cursor_class->close = (void (*) (TrackerSparqlCursor *)) tracker_db_cursor_close;
3252
3253 sparql_cursor_class->get_integer = (gint64 (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_int;
3254 sparql_cursor_class->get_double = (gdouble (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_double;
3255 sparql_cursor_class->get_boolean = (gboolean (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_boolean;
3256
3257 g_object_class_override_property (object_class, TRACKER_DB_CURSOR_PROP_N_COLUMNS, "n-columns");
3258 }
3259
3260 static TrackerDBCursor *
tracker_db_cursor_sqlite_new(TrackerDBStatement * ref_stmt,TrackerPropertyType * types,gint n_types,const gchar * const * variable_names,gint n_variable_names)3261 tracker_db_cursor_sqlite_new (TrackerDBStatement *ref_stmt,
3262 TrackerPropertyType *types,
3263 gint n_types,
3264 const gchar * const *variable_names,
3265 gint n_variable_names)
3266 {
3267 TrackerDBCursor *cursor;
3268 TrackerDBInterface *iface;
3269
3270 iface = ref_stmt->db_interface;
3271 tracker_db_interface_ref_use (iface);
3272
3273 #ifdef G_ENABLE_DEBUG
3274 if (TRACKER_DEBUG_CHECK (SQL_STATEMENTS)) {
3275 gchar *full_query;
3276
3277 full_query = sqlite3_expanded_sql (ref_stmt->stmt);
3278
3279 if (full_query) {
3280 g_message ("Executing query: '%s'", full_query);
3281 sqlite3_free (full_query);
3282 } else {
3283 g_message ("Executing query: '%s'",
3284 sqlite3_sql (ref_stmt->stmt));
3285 }
3286 }
3287 #endif
3288
3289 cursor = g_object_new (TRACKER_TYPE_DB_CURSOR, NULL);
3290
3291 cursor->finished = FALSE;
3292
3293 cursor->stmt = ref_stmt->stmt;
3294 cursor->ref_stmt = tracker_db_statement_sqlite_grab (ref_stmt);
3295
3296 if (types) {
3297 gint i;
3298
3299 cursor->types = g_new (TrackerPropertyType, n_types);
3300 cursor->n_types = n_types;
3301 for (i = 0; i < n_types; i++) {
3302 cursor->types[i] = types[i];
3303 }
3304 }
3305
3306 if (variable_names) {
3307 gint i;
3308
3309 cursor->variable_names = g_new (gchar *, n_variable_names);
3310 cursor->n_variable_names = n_variable_names;
3311 for (i = 0; i < n_variable_names; i++) {
3312 cursor->variable_names[i] = g_strdup (variable_names[i]);
3313 }
3314 }
3315
3316 return cursor;
3317 }
3318
3319 void
tracker_db_statement_bind_double(TrackerDBStatement * stmt,int index,double value)3320 tracker_db_statement_bind_double (TrackerDBStatement *stmt,
3321 int index,
3322 double value)
3323 {
3324 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3325
3326 g_assert (!stmt->stmt_is_used);
3327
3328 tracker_db_interface_lock (stmt->db_interface);
3329 sqlite3_bind_double (stmt->stmt, index + 1, value);
3330 tracker_db_interface_unlock (stmt->db_interface);
3331 }
3332
3333 void
tracker_db_statement_bind_int(TrackerDBStatement * stmt,int index,gint64 value)3334 tracker_db_statement_bind_int (TrackerDBStatement *stmt,
3335 int index,
3336 gint64 value)
3337 {
3338 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3339
3340 g_assert (!stmt->stmt_is_used);
3341
3342 tracker_db_interface_lock (stmt->db_interface);
3343 sqlite3_bind_int64 (stmt->stmt, index + 1, value);
3344 tracker_db_interface_unlock (stmt->db_interface);
3345 }
3346
3347 void
tracker_db_statement_bind_null(TrackerDBStatement * stmt,int index)3348 tracker_db_statement_bind_null (TrackerDBStatement *stmt,
3349 int index)
3350 {
3351 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3352
3353 g_assert (!stmt->stmt_is_used);
3354
3355 tracker_db_interface_lock (stmt->db_interface);
3356 sqlite3_bind_null (stmt->stmt, index + 1);
3357 tracker_db_interface_unlock (stmt->db_interface);
3358 }
3359
3360 void
tracker_db_statement_bind_text(TrackerDBStatement * stmt,int index,const gchar * value)3361 tracker_db_statement_bind_text (TrackerDBStatement *stmt,
3362 int index,
3363 const gchar *value)
3364 {
3365 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3366
3367 g_assert (!stmt->stmt_is_used);
3368
3369 tracker_db_interface_lock (stmt->db_interface);
3370 sqlite3_bind_text (stmt->stmt, index + 1, value, -1, SQLITE_TRANSIENT);
3371 tracker_db_interface_unlock (stmt->db_interface);
3372 }
3373
3374 void
tracker_db_statement_bind_bytes(TrackerDBStatement * stmt,int index,GBytes * value)3375 tracker_db_statement_bind_bytes (TrackerDBStatement *stmt,
3376 int index,
3377 GBytes *value)
3378 {
3379 gconstpointer data;
3380 gsize len;
3381
3382 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3383
3384 g_assert (!stmt->stmt_is_used);
3385
3386 data = g_bytes_get_data (value, &len);
3387
3388 tracker_db_interface_lock (stmt->db_interface);
3389 sqlite3_bind_blob (stmt->stmt, index + 1, data, len - 1, SQLITE_TRANSIENT);
3390 tracker_db_interface_unlock (stmt->db_interface);
3391 }
3392
3393 void
tracker_db_statement_bind_value(TrackerDBStatement * stmt,int index,const GValue * value)3394 tracker_db_statement_bind_value (TrackerDBStatement *stmt,
3395 int index,
3396 const GValue *value)
3397 {
3398 GType type;
3399
3400 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3401
3402 g_assert (!stmt->stmt_is_used);
3403
3404 tracker_db_interface_lock (stmt->db_interface);
3405
3406 type = G_VALUE_TYPE (value);
3407
3408 if (type == G_TYPE_INT) {
3409 sqlite3_bind_int64 (stmt->stmt, index + 1, g_value_get_int (value));
3410 } else if (type == G_TYPE_INT64) {
3411 sqlite3_bind_int64 (stmt->stmt, index + 1, g_value_get_int64 (value));
3412 } else if (type == G_TYPE_DOUBLE) {
3413 sqlite3_bind_double (stmt->stmt, index + 1, g_value_get_double (value));
3414 } else if (type == G_TYPE_FLOAT) {
3415 sqlite3_bind_double (stmt->stmt, index + 1, g_value_get_float (value));
3416 } else if (type == G_TYPE_STRING) {
3417 sqlite3_bind_text (stmt->stmt, index + 1,
3418 g_value_get_string (value), -1, SQLITE_TRANSIENT);
3419 } else if (type == G_TYPE_BYTES) {
3420 GBytes *bytes;
3421 gconstpointer data;
3422 gsize len;
3423
3424 bytes = g_value_get_boxed (value);
3425 data = g_bytes_get_data (bytes, &len);
3426 sqlite3_bind_text (stmt->stmt, index + 1,
3427 data, len, SQLITE_TRANSIENT);
3428 } else {
3429 GValue dest = G_VALUE_INIT;
3430
3431 g_value_init (&dest, G_TYPE_STRING);
3432
3433 if (g_value_transform (value, &dest)) {
3434 sqlite3_bind_text (stmt->stmt, index + 1,
3435 g_value_get_string (&dest), -1, SQLITE_TRANSIENT);
3436 g_value_unset (&dest);
3437 } else {
3438 g_assert_not_reached ();
3439 }
3440 }
3441
3442 tracker_db_interface_unlock (stmt->db_interface);
3443 }
3444
3445 void
tracker_db_cursor_rewind(TrackerDBCursor * cursor)3446 tracker_db_cursor_rewind (TrackerDBCursor *cursor)
3447 {
3448 TrackerDBInterface *iface;
3449
3450 g_return_if_fail (TRACKER_IS_DB_CURSOR (cursor));
3451
3452 iface = cursor->ref_stmt->db_interface;
3453
3454 tracker_db_interface_lock (iface);
3455
3456 sqlite3_reset (cursor->stmt);
3457 cursor->finished = FALSE;
3458
3459 tracker_db_interface_unlock (iface);
3460 }
3461
3462 gboolean
tracker_db_cursor_iter_next(TrackerDBCursor * cursor,GCancellable * cancellable,GError ** error)3463 tracker_db_cursor_iter_next (TrackerDBCursor *cursor,
3464 GCancellable *cancellable,
3465 GError **error)
3466 {
3467 if (!cursor) {
3468 return FALSE;
3469 }
3470
3471 return db_cursor_iter_next (cursor, cancellable, error);
3472 }
3473
3474
3475 static gboolean
db_cursor_iter_next(TrackerDBCursor * cursor,GCancellable * cancellable,GError ** error)3476 db_cursor_iter_next (TrackerDBCursor *cursor,
3477 GCancellable *cancellable,
3478 GError **error)
3479 {
3480 TrackerDBStatement *stmt = cursor->ref_stmt;
3481 TrackerDBInterface *iface = stmt->db_interface;
3482
3483 if (!cursor->finished) {
3484 guint result;
3485
3486 tracker_db_interface_lock (iface);
3487
3488 if (g_cancellable_is_cancelled (cancellable)) {
3489 result = SQLITE_INTERRUPT;
3490 sqlite3_reset (cursor->stmt);
3491 } else {
3492 /* only one statement can be active at the same time per interface */
3493 iface->cancellable = cancellable;
3494 result = stmt_step (cursor->stmt);
3495 iface->cancellable = NULL;
3496 }
3497
3498 if (result == SQLITE_INTERRUPT) {
3499 g_set_error (error,
3500 TRACKER_DB_INTERFACE_ERROR,
3501 TRACKER_DB_INTERRUPTED,
3502 "Interrupted");
3503 } else if (result != SQLITE_ROW && result != SQLITE_DONE) {
3504 g_set_error (error,
3505 TRACKER_DB_INTERFACE_ERROR,
3506 TRACKER_DB_QUERY_ERROR,
3507 "%s", sqlite3_errmsg (iface->db));
3508 }
3509
3510 cursor->finished = (result != SQLITE_ROW);
3511
3512 tracker_db_interface_unlock (iface);
3513 }
3514
3515 return (!cursor->finished);
3516 }
3517
3518 guint
tracker_db_cursor_get_n_columns(TrackerDBCursor * cursor)3519 tracker_db_cursor_get_n_columns (TrackerDBCursor *cursor)
3520 {
3521 return sqlite3_column_count (cursor->stmt);
3522 }
3523
3524 void
tracker_db_cursor_get_value(TrackerDBCursor * cursor,guint column,GValue * value)3525 tracker_db_cursor_get_value (TrackerDBCursor *cursor,
3526 guint column,
3527 GValue *value)
3528 {
3529 gint col_type;
3530
3531 col_type = sqlite3_column_type (cursor->stmt, column);
3532
3533 switch (col_type) {
3534 case SQLITE_TEXT:
3535 g_value_init (value, G_TYPE_STRING);
3536 g_value_set_string (value, (gchar *) sqlite3_column_text (cursor->stmt, column));
3537 break;
3538 case SQLITE_INTEGER:
3539 g_value_init (value, G_TYPE_INT64);
3540 g_value_set_int64 (value, sqlite3_column_int64 (cursor->stmt, column));
3541 break;
3542 case SQLITE_FLOAT:
3543 g_value_init (value, G_TYPE_DOUBLE);
3544 g_value_set_double (value, sqlite3_column_double (cursor->stmt, column));
3545 break;
3546 case SQLITE_NULL:
3547 /* just ignore NULLs */
3548 break;
3549 default:
3550 g_critical ("Unknown sqlite3 database column type:%d", col_type);
3551 }
3552
3553 }
3554
3555 gint64
tracker_db_cursor_get_int(TrackerDBCursor * cursor,guint column)3556 tracker_db_cursor_get_int (TrackerDBCursor *cursor,
3557 guint column)
3558 {
3559 TrackerDBInterface *iface;
3560 gint64 result;
3561
3562 iface = cursor->ref_stmt->db_interface;
3563
3564 tracker_db_interface_lock (iface);
3565
3566 result = (gint64) sqlite3_column_int64 (cursor->stmt, column);
3567
3568 tracker_db_interface_unlock (iface);
3569
3570 return result;
3571 }
3572
3573 gdouble
tracker_db_cursor_get_double(TrackerDBCursor * cursor,guint column)3574 tracker_db_cursor_get_double (TrackerDBCursor *cursor,
3575 guint column)
3576 {
3577 TrackerDBInterface *iface;
3578 gdouble result;
3579
3580 iface = cursor->ref_stmt->db_interface;
3581
3582 tracker_db_interface_lock (iface);
3583
3584 result = (gdouble) sqlite3_column_double (cursor->stmt, column);
3585
3586 tracker_db_interface_unlock (iface);
3587
3588 return result;
3589 }
3590
3591 static gboolean
tracker_db_cursor_get_boolean(TrackerSparqlCursor * sparql_cursor,guint column)3592 tracker_db_cursor_get_boolean (TrackerSparqlCursor *sparql_cursor,
3593 guint column)
3594 {
3595 TrackerDBCursor *cursor = (TrackerDBCursor *) sparql_cursor;
3596 return (g_strcmp0 (tracker_db_cursor_get_string (cursor, column, NULL), "true") == 0);
3597 }
3598
3599 TrackerSparqlValueType
tracker_db_cursor_get_value_type(TrackerDBCursor * cursor,guint column)3600 tracker_db_cursor_get_value_type (TrackerDBCursor *cursor,
3601 guint column)
3602 {
3603 TrackerDBInterface *iface;
3604 gint column_type;
3605 gint n_columns = sqlite3_column_count (cursor->stmt);
3606
3607 g_return_val_if_fail (column < n_columns, TRACKER_SPARQL_VALUE_TYPE_UNBOUND);
3608
3609 iface = cursor->ref_stmt->db_interface;
3610
3611 tracker_db_interface_lock (iface);
3612
3613 column_type = sqlite3_column_type (cursor->stmt, column);
3614
3615 tracker_db_interface_unlock (iface);
3616
3617 if (column_type == SQLITE_NULL) {
3618 return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
3619 } else if (column < cursor->n_types) {
3620 switch (cursor->types[column]) {
3621 case TRACKER_PROPERTY_TYPE_RESOURCE:
3622 return TRACKER_SPARQL_VALUE_TYPE_URI;
3623 case TRACKER_PROPERTY_TYPE_INTEGER:
3624 return TRACKER_SPARQL_VALUE_TYPE_INTEGER;
3625 case TRACKER_PROPERTY_TYPE_DOUBLE:
3626 return TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
3627 case TRACKER_PROPERTY_TYPE_DATETIME:
3628 return TRACKER_SPARQL_VALUE_TYPE_DATETIME;
3629 case TRACKER_PROPERTY_TYPE_BOOLEAN:
3630 return TRACKER_SPARQL_VALUE_TYPE_BOOLEAN;
3631 default:
3632 return TRACKER_SPARQL_VALUE_TYPE_STRING;
3633 }
3634 } else {
3635 return TRACKER_SPARQL_VALUE_TYPE_STRING;
3636 }
3637 }
3638
3639 const gchar*
tracker_db_cursor_get_variable_name(TrackerDBCursor * cursor,guint column)3640 tracker_db_cursor_get_variable_name (TrackerDBCursor *cursor,
3641 guint column)
3642 {
3643 TrackerDBInterface *iface;
3644 const gchar *result;
3645
3646 iface = cursor->ref_stmt->db_interface;
3647
3648 tracker_db_interface_lock (iface);
3649
3650 if (column < cursor->n_variable_names) {
3651 result = cursor->variable_names[column];
3652 } else {
3653 result = sqlite3_column_name (cursor->stmt, column);
3654 }
3655
3656 tracker_db_interface_unlock (iface);
3657
3658 return result;
3659 }
3660
3661 const gchar*
tracker_db_cursor_get_string(TrackerDBCursor * cursor,guint column,glong * length)3662 tracker_db_cursor_get_string (TrackerDBCursor *cursor,
3663 guint column,
3664 glong *length)
3665 {
3666 TrackerDBInterface *iface;
3667 const gchar *result;
3668
3669 iface = cursor->ref_stmt->db_interface;
3670
3671 tracker_db_interface_lock (iface);
3672
3673 if (length) {
3674 sqlite3_value *val = sqlite3_column_value (cursor->stmt, column);
3675
3676 *length = sqlite3_value_bytes (val);
3677 result = (const gchar *) sqlite3_value_text (val);
3678 } else {
3679 result = (const gchar *) sqlite3_column_text (cursor->stmt, column);
3680 }
3681
3682 tracker_db_interface_unlock (iface);
3683
3684 return result;
3685 }
3686
3687 void
tracker_db_statement_execute(TrackerDBStatement * stmt,GError ** error)3688 tracker_db_statement_execute (TrackerDBStatement *stmt,
3689 GError **error)
3690 {
3691 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
3692 g_return_if_fail (!stmt->stmt_is_used);
3693
3694 execute_stmt (stmt->db_interface, stmt->stmt, NULL, error);
3695 tracker_db_statement_sqlite_release (stmt);
3696 }
3697
3698 TrackerDBCursor *
tracker_db_statement_start_cursor(TrackerDBStatement * stmt,GError ** error)3699 tracker_db_statement_start_cursor (TrackerDBStatement *stmt,
3700 GError **error)
3701 {
3702 g_return_val_if_fail (TRACKER_IS_DB_STATEMENT (stmt), NULL);
3703 g_return_val_if_fail (!stmt->stmt_is_used, NULL);
3704
3705 return tracker_db_cursor_sqlite_new (stmt, NULL, 0, NULL, 0);
3706 }
3707
3708 TrackerDBCursor *
tracker_db_statement_start_sparql_cursor(TrackerDBStatement * stmt,TrackerPropertyType * types,gint n_types,const gchar * const * variable_names,gint n_variable_names,GError ** error)3709 tracker_db_statement_start_sparql_cursor (TrackerDBStatement *stmt,
3710 TrackerPropertyType *types,
3711 gint n_types,
3712 const gchar * const *variable_names,
3713 gint n_variable_names,
3714 GError **error)
3715 {
3716 g_return_val_if_fail (TRACKER_IS_DB_STATEMENT (stmt), NULL);
3717 g_return_val_if_fail (!stmt->stmt_is_used, NULL);
3718
3719 return tracker_db_cursor_sqlite_new (stmt, types, n_types, variable_names, n_variable_names);
3720 }
3721
3722 static void
tracker_db_statement_init(TrackerDBStatement * stmt)3723 tracker_db_statement_init (TrackerDBStatement *stmt)
3724 {
3725 }
3726
3727 static void
tracker_db_cursor_init(TrackerDBCursor * cursor)3728 tracker_db_cursor_init (TrackerDBCursor *cursor)
3729 {
3730 }
3731
3732 static void
tracker_db_statement_sqlite_reset(TrackerDBStatement * stmt)3733 tracker_db_statement_sqlite_reset (TrackerDBStatement *stmt)
3734 {
3735 g_assert (!stmt->stmt_is_used);
3736
3737 sqlite3_reset (stmt->stmt);
3738 sqlite3_clear_bindings (stmt->stmt);
3739 }
3740
3741
3742 void
tracker_db_interface_set_user_data(TrackerDBInterface * db_interface,gpointer user_data,GDestroyNotify destroy_notify)3743 tracker_db_interface_set_user_data (TrackerDBInterface *db_interface,
3744 gpointer user_data,
3745 GDestroyNotify destroy_notify)
3746 {
3747 if (db_interface->user_data && db_interface->user_data_destroy_notify)
3748 db_interface->user_data_destroy_notify (db_interface->user_data);
3749
3750 db_interface->user_data = user_data;
3751 db_interface->user_data_destroy_notify = destroy_notify;
3752 }
3753
3754 gpointer
tracker_db_interface_get_user_data(TrackerDBInterface * db_interface)3755 tracker_db_interface_get_user_data (TrackerDBInterface *db_interface)
3756 {
3757 return db_interface->user_data;
3758 }
3759
3760 void
tracker_db_interface_ref_use(TrackerDBInterface * db_interface)3761 tracker_db_interface_ref_use (TrackerDBInterface *db_interface)
3762 {
3763 g_atomic_int_inc (&db_interface->n_users);
3764 }
3765
3766 gboolean
tracker_db_interface_unref_use(TrackerDBInterface * db_interface)3767 tracker_db_interface_unref_use (TrackerDBInterface *db_interface)
3768 {
3769 return g_atomic_int_dec_and_test (&db_interface->n_users);
3770 }
3771
3772 gboolean
tracker_db_interface_get_is_used(TrackerDBInterface * db_interface)3773 tracker_db_interface_get_is_used (TrackerDBInterface *db_interface)
3774 {
3775 return g_atomic_int_get (&db_interface->n_users) > 0;
3776 }
3777
3778 gboolean
tracker_db_interface_init_vtabs(TrackerDBInterface * db_interface,gpointer vtab_data)3779 tracker_db_interface_init_vtabs (TrackerDBInterface *db_interface,
3780 gpointer vtab_data)
3781 {
3782 tracker_vtab_triples_init (db_interface->db, vtab_data);
3783 tracker_vtab_service_init (db_interface->db, vtab_data);
3784 return TRUE;
3785 }
3786
3787 gboolean
tracker_db_interface_attach_database(TrackerDBInterface * db_interface,GFile * file,const gchar * name,GError ** error)3788 tracker_db_interface_attach_database (TrackerDBInterface *db_interface,
3789 GFile *file,
3790 const gchar *name,
3791 GError **error)
3792 {
3793 gchar *sql, *uri = NULL;
3794 sqlite3_stmt *stmt;
3795 gboolean retval;
3796
3797 g_return_val_if_fail (file || db_interface->shared_cache_key, FALSE);
3798
3799 if (file) {
3800 uri = g_file_get_path (file);
3801 } else if (db_interface->shared_cache_key &&
3802 (db_interface->flags & TRACKER_DB_INTERFACE_IN_MEMORY) != 0) {
3803 gchar *md5;
3804
3805 md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, name, -1);
3806 uri = g_strdup_printf ("file:%s-%s?mode=memory&cache=shared",
3807 db_interface->shared_cache_key, md5);
3808 g_free (md5);
3809 }
3810
3811 sql = g_strdup_printf ("ATTACH DATABASE \"%s\" AS \"%s\"",
3812 uri, name);
3813 g_free (uri);
3814
3815 stmt = tracker_db_interface_prepare_stmt (db_interface, sql, error);
3816 g_free (sql);
3817 if (!stmt)
3818 return FALSE;
3819
3820 retval = execute_stmt (db_interface, stmt, NULL, error);
3821 sqlite3_finalize (stmt);
3822 return retval;
3823 }
3824
3825 gboolean
tracker_db_interface_detach_database(TrackerDBInterface * db_interface,const gchar * name,GError ** error)3826 tracker_db_interface_detach_database (TrackerDBInterface *db_interface,
3827 const gchar *name,
3828 GError **error)
3829 {
3830 sqlite3_stmt *stmt;
3831 gboolean retval;
3832 gchar *sql;
3833
3834 sql = g_strdup_printf ("DETACH DATABASE \"%s\"", name);
3835 stmt = tracker_db_interface_prepare_stmt (db_interface, sql, error);
3836 g_free (sql);
3837
3838 if (!stmt)
3839 return FALSE;
3840
3841 retval = execute_stmt (db_interface, stmt, NULL, error);
3842 sqlite3_finalize (stmt);
3843 return retval;
3844 }
3845
3846 gssize
tracker_db_interface_sqlite_release_memory(TrackerDBInterface * db_interface)3847 tracker_db_interface_sqlite_release_memory (TrackerDBInterface *db_interface)
3848 {
3849 db_interface->select_stmt_lru.head = db_interface->select_stmt_lru.tail = NULL;
3850 db_interface->select_stmt_lru.size = 0;
3851 db_interface->update_stmt_lru.head = db_interface->update_stmt_lru.tail = NULL;
3852 db_interface->update_stmt_lru.size = 0;
3853 g_hash_table_remove_all (db_interface->dynamic_statements);
3854
3855 return (gssize) sqlite3_db_release_memory (db_interface->db);
3856 }
3857