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-locale.h>
31
32 #include <libtracker-sparql/tracker-sparql.h>
33
34 #if HAVE_TRACKER_FTS
35 #include <libtracker-fts/tracker-fts.h>
36 #endif
37
38 #ifdef HAVE_LIBUNISTRING
39 /* libunistring versions prior to 9.1.2 need this hack */
40 #define _UNUSED_PARAMETER_
41 #include <unistr.h>
42 #include <unicase.h>
43 #elif HAVE_LIBICU
44 #include <unicode/utypes.h>
45 #include <unicode/uregex.h>
46 #include <unicode/ustring.h>
47 #include <unicode/ucol.h>
48 #endif
49
50 #include "tracker-collation.h"
51
52 #include "tracker-db-interface-sqlite.h"
53 #include "tracker-db-manager.h"
54
55 /* Since 2.30, g_atomic_int_add() is fully equivalente to g_atomic_int_exchange_and_add() */
56 #if GLIB_CHECK_VERSION (2,30,0)
57 #define ATOMIC_EXCHANGE_AND_ADD(a,v) g_atomic_int_add (a,v)
58 #else
59 #define ATOMIC_EXCHANGE_AND_ADD(a,v) g_atomic_int_exchange_and_add (a,v)
60 #endif
61
62 #define UNKNOWN_STATUS 0.5
63
64 typedef struct {
65 TrackerDBStatement *head;
66 TrackerDBStatement *tail;
67 guint size;
68 guint max;
69 } TrackerDBStatementLru;
70
71 struct TrackerDBInterface {
72 GObject parent_instance;
73
74 gchar *filename;
75 sqlite3 *db;
76
77 GHashTable *dynamic_statements;
78
79 GSList *function_data;
80
81 /* Collation and locale change */
82 gpointer locale_notification_id;
83 gint collator_reset_requested;
84
85 /* Number of active cursors */
86 gint n_active_cursors;
87
88 guint ro : 1;
89 GCancellable *cancellable;
90
91 TrackerDBStatementLru select_stmt_lru;
92 TrackerDBStatementLru update_stmt_lru;
93
94 TrackerBusyCallback busy_callback;
95 gpointer busy_user_data;
96 gchar *busy_status;
97
98 gchar *fts_insert_str;
99 };
100
101 struct TrackerDBInterfaceClass {
102 GObjectClass parent_class;
103 };
104
105 struct TrackerDBCursor {
106 TrackerSparqlCursor parent_instance;
107 sqlite3_stmt *stmt;
108 TrackerDBStatement *ref_stmt;
109 gboolean finished;
110 TrackerPropertyType *types;
111 gint n_types;
112 gchar **variable_names;
113 gint n_variable_names;
114
115 /* used for direct access as libtracker-sparql is thread-safe and
116 uses a single shared connection with SQLite mutex disabled */
117 gboolean threadsafe;
118 };
119
120 struct TrackerDBCursorClass {
121 TrackerSparqlCursorClass parent_class;
122 };
123
124 struct TrackerDBStatement {
125 GObject parent_instance;
126 TrackerDBInterface *db_interface;
127 sqlite3_stmt *stmt;
128 gboolean stmt_is_sunk;
129 TrackerDBStatement *next;
130 TrackerDBStatement *prev;
131 };
132
133 struct TrackerDBStatementClass {
134 GObjectClass parent_class;
135 };
136
137 static void tracker_db_interface_initable_iface_init (GInitableIface *iface);
138 static TrackerDBStatement *tracker_db_statement_sqlite_new (TrackerDBInterface *db_interface,
139 sqlite3_stmt *sqlite_stmt);
140 static void tracker_db_statement_sqlite_reset (TrackerDBStatement *stmt);
141 static TrackerDBCursor *tracker_db_cursor_sqlite_new (sqlite3_stmt *sqlite_stmt,
142 TrackerDBStatement *ref_stmt,
143 TrackerPropertyType *types,
144 gint n_types,
145 const gchar **variable_names,
146 gint n_variable_names,
147 gboolean threadsafe);
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_RO
158 };
159
160 enum {
161 TRACKER_DB_CURSOR_PROP_0,
162 TRACKER_DB_CURSOR_PROP_N_COLUMNS
163 };
164
165 G_DEFINE_TYPE_WITH_CODE (TrackerDBInterface, tracker_db_interface, G_TYPE_OBJECT,
166 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
167 tracker_db_interface_initable_iface_init));
168
169 G_DEFINE_TYPE (TrackerDBStatement, tracker_db_statement, G_TYPE_OBJECT)
170
171 G_DEFINE_TYPE (TrackerDBCursor, tracker_db_cursor, TRACKER_SPARQL_TYPE_CURSOR)
172
173 void
174 tracker_db_interface_sqlite_enable_shared_cache (void)
175 {
176 sqlite3_enable_shared_cache (1);
177 }
178
179 static void
180 function_sparql_string_join (sqlite3_context *context,
181 int argc,
182 sqlite3_value *argv[])
183 {
184 GString *str = NULL;
185 const gchar *separator;
186 gint i;
187
188 /* fn:string-join (str1, str2, ..., separator) */
189
190 if (sqlite3_value_type (argv[argc-1]) != SQLITE_TEXT) {
191 sqlite3_result_error (context, "Invalid separator", -1);
192 return;
193 }
194
195 separator = sqlite3_value_text (argv[argc-1]);
pointer targets in assignment differ in signedness
(emitted by gcc)
196
197 for (i = 0;i < argc-1; i++) {
198 if (sqlite3_value_type (argv[argc-1]) == SQLITE_TEXT) {
199 const gchar *text = sqlite3_value_text (argv[i]);
200
201 if (text != NULL) {
202 if (!str) {
203 str = g_string_new (text);
204 } else {
205 g_string_append_printf (str, "%s%s", separator, text);
206 }
207 }
208 }
209 }
210
211 if (str) {
212 sqlite3_result_text (context, str->str, str->len, g_free);
213 g_string_free (str, FALSE);
214 } else {
215 sqlite3_result_null (context);
216 }
217
218 return;
219 }
220
221 /* Create a title-type string from the filename for replacing missing ones */
222 static void
223 function_sparql_string_from_filename (sqlite3_context *context,
224 int argc,
225 sqlite3_value *argv[])
226 {
227 gchar *name = NULL;
228 gchar *suffix = NULL;
229
230 if (argc != 1) {
231 sqlite3_result_error (context, "Invalid argument count", -1);
232 return;
233 }
234
235 /* "/home/user/path/title_of_the_movie.movie" -> "title of the movie"
236 * Only for local files currently, do we need to change? */
237
238 name = g_filename_display_basename (sqlite3_value_text (argv[0]));
pointer targets in passing argument 1 of 'g_filename_display_basename' differ in signedness
(emitted by gcc)
239
240 if (!name) {
241 sqlite3_result_null (context);
242 return;
243 }
244
245 suffix = g_strrstr (name, ".");
246
247 if (suffix) {
248 *suffix = '\0';
249 }
250
251 g_strdelimit (name, "._", ' ');
252
253 sqlite3_result_text (context, name, -1, g_free);
254 }
255
256 static void
257 function_sparql_uri_is_parent (sqlite3_context *context,
258 int argc,
259 sqlite3_value *argv[])
260 {
261 const gchar *uri, *parent, *remaining;
262 gboolean match = FALSE;
263 guint parent_len;
264
265 if (argc != 2) {
266 sqlite3_result_error (context, "Invalid argument count", -1);
267 return;
268 }
269
270 parent = sqlite3_value_text (argv[0]);
pointer targets in assignment differ in signedness
(emitted by gcc)
271 uri = sqlite3_value_text (argv[1]);
272
273 if (!parent || !uri) {
274 sqlite3_result_error (context, "Invalid arguments", -1);
275 return;
276 }
277
278 parent_len = sqlite3_value_bytes (argv[0]);
279
280 /* Check only one argument, it's going to
281 * be compared with the other anyway.
282 */
283
284 if (!(parent_len >= 7 && (parent[4] == ':' && parent[5] == '/' && parent[6] == '/'))) {
285 if (strstr (parent, "://") == NULL) {
286 sqlite3_result_int (context, FALSE);
287 return;
288 }
289 }
290
291 /* Remove trailing '/', will
292 * be checked later on uri.
293 */
294 while (parent[parent_len - 1] == '/') {
295 parent_len--;
296 }
297
298 if (strncmp (uri, parent, parent_len) == 0 && uri[parent_len] == '/') {
299 const gchar *slash;
300
301 while (uri[parent_len] == '/') {
302 parent_len++;
303 }
304
305 remaining = &uri[parent_len];
306
307 if (*remaining == '\0') {
308 /* Exact match, not a child */
309 match = FALSE;
310 } else if ((slash = strchr (remaining, '/')) == NULL) {
311 /* Remaining doesn't have uri
312 * separator, it's a direct child.
313 */
314 match = TRUE;
315 } else {
316 /* Check it's not trailing slashes */
317 while (*slash == '/') {
318 slash++;
319 }
320
321 match = (*slash == '\0');
322 }
323 }
324
325 sqlite3_result_int (context, match);
326 }
327
328 static gboolean
329 check_uri_is_descendant (const gchar *parent,
330 guint parent_len,
331 const gchar *uri)
332 {
333 const gchar *remaining;
334 gboolean match = FALSE;
335
336 /* Check only one argument, it's going to
337 * be compared with the other anyway.
338 */
339
340 if (!(parent_len >= 7 && (parent[4] == ':' && parent[5] == '/' && parent[6] == '/'))) {
341 if (strstr (parent, "://") == NULL) {
342 return FALSE;
343 }
344 }
345
346 /* Remove trailing '/', will
347 * be checked later on uri.
348 */
349 while (parent[parent_len - 1] == '/') {
350 parent_len--;
351 }
352
353 if (strncmp (uri, parent, parent_len) == 0 && uri[parent_len] == '/') {
354 while (uri[parent_len] == '/') {
355 parent_len++;
356 }
357
358 remaining = &uri[parent_len];
359
360 if (remaining && *remaining) {
361 match = TRUE;
362 }
363 }
364
365 return match;
366 }
367
368 static void
369 function_sparql_uri_is_descendant (sqlite3_context *context,
370 int argc,
371 sqlite3_value *argv[])
372 {
373 const gchar *child;
374 gboolean match = FALSE;
375 gint i;
376
377 /* fn:uri-is-descendant (parent1, parent2, ..., parentN, child) */
378
379 if (argc < 2) {
380 sqlite3_result_error (context, "Invalid argument count", -1);
381 return;
382 }
383
384 if (sqlite3_value_type (argv[argc-1]) != SQLITE_TEXT) {
385 sqlite3_result_error (context, "Invalid child", -1);
386 return;
387 }
388
389 if (sqlite3_value_type (argv[0]) != SQLITE_TEXT) {
390 sqlite3_result_error (context, "Invalid first parent", -1);
391 return;
392 }
393
394 child = sqlite3_value_text (argv[argc-1]);
pointer targets in assignment differ in signedness
(emitted by gcc)
395
396 for (i = 0; i < argc - 1 && !match; i++) {
397 if (sqlite3_value_type (argv[i]) == SQLITE_TEXT) {
398 const gchar *parent = sqlite3_value_text (argv[i]);
399 guint parent_len = sqlite3_value_bytes (argv[i]);
400
401 if (!parent)
402 continue;
403
404 match = check_uri_is_descendant (parent, parent_len, child);
405 }
406 }
407
408 sqlite3_result_int (context, match);
409 }
410
411 static void
412 function_sparql_format_time (sqlite3_context *context,
413 int argc,
414 sqlite3_value *argv[])
415 {
416 gdouble seconds;
417 gchar *str;
418
419 if (argc != 1) {
420 sqlite3_result_error (context, "Invalid argument count", -1);
421 return;
422 }
423
424 if (sqlite3_value_type (argv[0]) == SQLITE_NULL) {
425 sqlite3_result_null (context);
426 return;
427 }
428
429 seconds = sqlite3_value_double (argv[0]);
430 str = tracker_date_to_string (seconds);
431
432 sqlite3_result_text (context, str, -1, g_free);
433 }
434
435 static void
436 function_sparql_cartesian_distance (sqlite3_context *context,
437 int argc,
438 sqlite3_value *argv[])
439 {
440 gdouble lat1;
441 gdouble lat2;
442 gdouble lon1;
443 gdouble lon2;
444
445 gdouble R;
446 gdouble a;
447 gdouble b;
448 gdouble c;
449 gdouble d;
450
451 if (argc != 4) {
452 sqlite3_result_error (context, "Invalid argument count", -1);
453 return;
454 }
455
456 lat1 = sqlite3_value_double (argv[0])*M_PI/180;
457 lat2 = sqlite3_value_double (argv[1])*M_PI/180;
458 lon1 = sqlite3_value_double (argv[2])*M_PI/180;
459 lon2 = sqlite3_value_double (argv[3])*M_PI/180;
460
461 R = 6371000;
462 a = M_PI/2 - lat1;
463 b = M_PI/2 - lat2;
464 c = sqrt(a*a + b*b - 2*a*b*cos(lon2 - lon1));
465 d = R*c;
466
467 sqlite3_result_double (context, d);
468 }
469
470 static void
471 function_sparql_haversine_distance (sqlite3_context *context,
472 int argc,
473 sqlite3_value *argv[])
474 {
475 gdouble lat1;
476 gdouble lat2;
477 gdouble lon1;
478 gdouble lon2;
479
480 gdouble R;
481 gdouble dLat;
482 gdouble dLon;
483 gdouble a;
484 gdouble c;
485 gdouble d;
486
487 if (argc != 4) {
488 sqlite3_result_error (context, "Invalid argument count", -1);
489 return;
490 }
491
492 lat1 = sqlite3_value_double (argv[0])*M_PI/180;
493 lat2 = sqlite3_value_double (argv[1])*M_PI/180;
494 lon1 = sqlite3_value_double (argv[2])*M_PI/180;
495 lon2 = sqlite3_value_double (argv[3])*M_PI/180;
496
497 R = 6371000;
498 dLat = (lat2-lat1);
499 dLon = (lon2-lon1);
500 a = sin(dLat/2) * sin(dLat/2) + cos(lat1) * cos(lat2) * sin(dLon/2) * sin(dLon/2);
501 c = 2 * atan2(sqrt(a), sqrt(1-a));
502 d = R * c;
503
504 sqlite3_result_double (context, d);
505 }
506
507 static void
508 function_sparql_regex (sqlite3_context *context,
509 int argc,
510 sqlite3_value *argv[])
511 {
512 gboolean ret;
513 const gchar *text, *pattern, *flags;
514 GRegexCompileFlags regex_flags;
515 GRegex *regex;
516
517 if (argc != 3) {
518 sqlite3_result_error (context, "Invalid argument count", -1);
519 return;
520 }
521
522 regex = sqlite3_get_auxdata (context, 1);
523
524 text = sqlite3_value_text (argv[0]);
pointer targets in assignment differ in signedness
(emitted by gcc)
525 flags = sqlite3_value_text (argv[2]);
526
527 if (regex == NULL) {
528 gchar *err_str;
529 GError *error = NULL;
530
531 pattern = sqlite3_value_text (argv[1]);
532
533 regex_flags = 0;
534 while (*flags) {
535 switch (*flags) {
536 case 's':
537 regex_flags |= G_REGEX_DOTALL;
538 break;
539 case 'm':
540 regex_flags |= G_REGEX_MULTILINE;
541 break;
542 case 'i':
543 regex_flags |= G_REGEX_CASELESS;
544 break;
545 case 'x':
546 regex_flags |= G_REGEX_EXTENDED;
547 break;
548 default:
549 err_str = g_strdup_printf ("Invalid SPARQL regex flag '%c'", *flags);
550 sqlite3_result_error (context, err_str, -1);
551 g_free (err_str);
552 return;
553 }
554 flags++;
555 }
556
557 regex = g_regex_new (pattern, regex_flags, 0, &error);
558
559 if (error) {
560 sqlite3_result_error (context, error->message, -1);
561 g_clear_error (&error);
562 return;
563 }
564
565 sqlite3_set_auxdata (context, 1, regex, (void (*) (void*)) g_regex_unref);
566 }
567
568 ret = g_regex_match (regex, text, 0, NULL);
569
570 sqlite3_result_int (context, ret);
571 }
572
573 #ifdef HAVE_LIBUNISTRING
574
575 static void
576 function_sparql_lower_case (sqlite3_context *context,
577 int argc,
578 sqlite3_value *argv[])
579 {
580 const uint16_t *zInput;
581 uint16_t *zOutput;
582 size_t written = 0;
583 int nInput;
584
585 g_assert (argc == 1);
586
587 zInput = sqlite3_value_text16 (argv[0]);
588
589 if (!zInput) {
590 return;
591 }
592
593 nInput = sqlite3_value_bytes16 (argv[0]);
594
595 zOutput = u16_tolower (zInput, nInput/2, NULL, NULL, NULL, &written);
596
597 sqlite3_result_text16 (context, zOutput, written * 2, free);
598 }
599
600 static void
601 function_sparql_case_fold (sqlite3_context *context,
602 int argc,
603 sqlite3_value *argv[])
604 {
605 const uint16_t *zInput;
606 uint16_t *zOutput;
607 size_t written = 0;
608 int nInput;
609
610 g_assert (argc == 1);
611
612 zInput = sqlite3_value_text16 (argv[0]);
613
614 if (!zInput) {
615 return;
616 }
617
618 nInput = sqlite3_value_bytes16 (argv[0]);
619
620 zOutput = u16_casefold (zInput, nInput/2, NULL, NULL, NULL, &written);
621
622 sqlite3_result_text16 (context, zOutput, written * 2, free);
623 }
624
625 #elif HAVE_LIBICU
626
627 static void
628 function_sparql_lower_case (sqlite3_context *context,
629 int argc,
630 sqlite3_value *argv[])
631 {
632 const UChar *zInput;
633 UChar *zOutput;
634 int nInput;
635 int nOutput;
636 UErrorCode status = U_ZERO_ERROR;
637
638 g_assert (argc == 1);
639
640 zInput = sqlite3_value_text16 (argv[0]);
641
642 if (!zInput) {
643 return;
644 }
645
646 nInput = sqlite3_value_bytes16 (argv[0]);
647
648 nOutput = nInput * 2 + 2;
649 zOutput = sqlite3_malloc (nOutput);
650
651 if (!zOutput) {
652 return;
653 }
654
655 u_strToLower (zOutput, nOutput/2, zInput, nInput/2, NULL, &status);
656
657 if (!U_SUCCESS (status)){
658 char zBuf[128];
659 sqlite3_snprintf (128, zBuf, "ICU error: u_strToLower(): %s", u_errorName (status));
660 zBuf[127] = '\0';
661 sqlite3_free (zOutput);
662 sqlite3_result_error (context, zBuf, -1);
663 return;
664 }
665
666 sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
667 }
668
669 static void
670 function_sparql_case_fold (sqlite3_context *context,
671 int argc,
672 sqlite3_value *argv[])
673 {
674 const UChar *zInput;
675 UChar *zOutput;
676 int nInput;
677 int nOutput;
678 UErrorCode status = U_ZERO_ERROR;
679
680 g_assert (argc == 1);
681
682 zInput = sqlite3_value_text16 (argv[0]);
683
684 if (!zInput) {
685 return;
686 }
687
688 nInput = sqlite3_value_bytes16 (argv[0]);
689
690 nOutput = nInput * 2 + 2;
691 zOutput = sqlite3_malloc (nOutput);
692
693 if (!zOutput) {
694 return;
695 }
696
697 u_strFoldCase (zOutput, nOutput/2, zInput, nInput/2, U_FOLD_CASE_DEFAULT, &status);
698
699 if (!U_SUCCESS (status)){
700 char zBuf[128];
701 sqlite3_snprintf (128, zBuf, "ICU error: u_strFoldCase: %s", u_errorName (status));
702 zBuf[127] = '\0';
703 sqlite3_free (zOutput);
704 sqlite3_result_error (context, zBuf, -1);
705 return;
706 }
707
708 sqlite3_result_text16 (context, zOutput, -1, sqlite3_free);
709 }
710 #else /* GLib based */
711
712 static void
713 function_sparql_lower_case (sqlite3_context *context,
714 int argc,
715 sqlite3_value *argv[])
716 {
717 const gchar *zInput;
718 gchar *zOutput;
719 int nInput;
720
721 g_assert (argc == 1);
722
723 /* GLib API works with UTF-8, so use the UTF-8 functions of SQLite too */
724
725 zInput = (const gchar*) sqlite3_value_text (argv[0]);
726
727 if (!zInput) {
728 return;
729 }
730
731 nInput = sqlite3_value_bytes (argv[0]);
732
733 if (!zOutput) {
734 return;
735 }
736
737 zOutput = g_utf8_strdown (zInput, nInput);
738
739 sqlite3_result_text (context, zOutput, -1, g_free);
740 }
741
742 static void
743 function_sparql_case_fold (sqlite3_context *context,
744 int argc,
745 sqlite3_value *argv[])
746 {
747 const gchar *zInput;
748 gchar *zOutput;
749 int nInput;
750
751 g_assert (argc == 1);
752
753 /* GLib API works with UTF-8, so use the UTF-8 functions of SQLite too */
754
755 zInput = (const gchar*) sqlite3_value_text (argv[0]);
756
757 if (!zInput) {
758 return;
759 }
760
761 nInput = sqlite3_value_bytes (argv[0]);
762
763 if (!zOutput) {
764 return;
765 }
766
767 zOutput = g_utf8_casefold (zInput, nInput);
768
769 sqlite3_result_text (context, zOutput, -1, g_free);
770 }
771
772 #endif
773
774 static inline int
775 stmt_step (sqlite3_stmt *stmt)
776 {
777 int result;
778
779 result = sqlite3_step (stmt);
780
781 /* If the statement expired between preparing it and executing
782 * sqlite3_step(), we are supposed to get SQLITE_SCHEMA error in
783 * sqlite3_errcode(), BUT there seems to be a bug in sqlite and
784 * SQLITE_ABORT is being returned instead for that case. So, the
785 * only way to see if a given statement was expired is to use
786 * sqlite3_expired(stmt), which is marked as DEPRECATED in sqlite.
787 * If found that the statement is expired, we need to reset it
788 * and retry the sqlite3_step().
789 * NOTE, that this expiration may only happen between preparing
790 * the statement and step-ing it, NOT between steps. */
791 if ((result == SQLITE_ABORT || result == SQLITE_SCHEMA) &&
792 sqlite3_expired (stmt)) {
793 sqlite3_reset (stmt);
794 result = sqlite3_step (stmt);
795 }
796
797 return result;
798 }
799
800 static int
801 check_interrupt (void *user_data)
802 {
803 TrackerDBInterface *db_interface = user_data;
804
805 if (db_interface->busy_callback) {
806 db_interface->busy_callback (db_interface->busy_status,
807 UNKNOWN_STATUS, /* No idea to get the status from SQLite */
808 db_interface->busy_user_data);
809 }
810
811 return g_cancellable_is_cancelled (db_interface->cancellable) ? 1 : 0;
812 }
813
814 static void
815 tracker_locale_notify_cb (TrackerLocaleID id,
816 gpointer user_data)
817 {
818 TrackerDBInterface *db_interface = user_data;
819
820 /* Request a collator reset. Use thread-safe methods as this function will get
821 * called from the main thread */
822 g_atomic_int_compare_and_exchange (&(db_interface->collator_reset_requested), FALSE, TRUE);
823 }
824
825 static void
826 open_database (TrackerDBInterface *db_interface,
827 GError **error)
828 {
829 int mode;
830
831 g_assert (db_interface->filename != NULL);
832
833 if (!db_interface->ro) {
834 mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
835 } else {
836 mode = SQLITE_OPEN_READONLY;
837 }
838
839 if (sqlite3_open_v2 (db_interface->filename, &db_interface->db, mode | SQLITE_OPEN_NOMUTEX, NULL) != SQLITE_OK) {
840 g_set_error (error,
841 TRACKER_DB_INTERFACE_ERROR,
842 TRACKER_DB_OPEN_ERROR,
843 "Could not open sqlite3 database:'%s'", db_interface->filename);
844 return;
845 } else {
846 g_message ("Opened sqlite3 database:'%s'", db_interface->filename);
847 }
848
849 /* Set our unicode collation function */
850 tracker_db_interface_sqlite_reset_collator (db_interface);
851 /* And register for updates on locale changes */
852 db_interface->locale_notification_id = tracker_locale_notify_add (TRACKER_LOCALE_COLLATE,
853 tracker_locale_notify_cb,
854 db_interface,
855 NULL);
856
857 sqlite3_progress_handler (db_interface->db, 100,
858 check_interrupt, db_interface);
859
860 sqlite3_create_function (db_interface->db, "SparqlRegex", 3, SQLITE_ANY,
861 db_interface, &function_sparql_regex,
862 NULL, NULL);
863
864 sqlite3_create_function (db_interface->db, "SparqlHaversineDistance", 4, SQLITE_ANY,
865 db_interface, &function_sparql_haversine_distance,
866 NULL, NULL);
867
868 sqlite3_create_function (db_interface->db, "SparqlCartesianDistance", 4, SQLITE_ANY,
869 db_interface, &function_sparql_cartesian_distance,
870 NULL, NULL);
871
872 sqlite3_create_function (db_interface->db, "SparqlStringFromFilename", 1, SQLITE_ANY,
873 db_interface, &function_sparql_string_from_filename,
874 NULL, NULL);
875
876 sqlite3_create_function (db_interface->db, "SparqlStringJoin", -1, SQLITE_ANY,
877 db_interface, &function_sparql_string_join,
878 NULL, NULL);
879
880 sqlite3_create_function (db_interface->db, "SparqlUriIsParent", 2, SQLITE_ANY,
881 db_interface, &function_sparql_uri_is_parent,
882 NULL, NULL);
883
884 sqlite3_create_function (db_interface->db, "SparqlUriIsDescendant", -1, SQLITE_ANY,
885 db_interface, &function_sparql_uri_is_descendant,
886 NULL, NULL);
887
888 sqlite3_create_function (db_interface->db, "SparqlLowerCase", 1, SQLITE_ANY,
889 db_interface, &function_sparql_lower_case,
890 NULL, NULL);
891
892 sqlite3_create_function (db_interface->db, "SparqlCaseFold", 1, SQLITE_ANY,
893 db_interface, &function_sparql_case_fold,
894 NULL, NULL);
895
896 sqlite3_create_function (db_interface->db, "SparqlFormatTime", 1, SQLITE_ANY,
897 db_interface, &function_sparql_format_time,
898 NULL, NULL);
899
900 sqlite3_extended_result_codes (db_interface->db, 0);
901 sqlite3_busy_timeout (db_interface->db, 100000);
902 }
903
904 static gboolean
905 tracker_db_interface_initable_init (GInitable *initable,
906 GCancellable *cancellable,
907 GError **error)
908 {
909 TrackerDBInterface *db_iface;
910 GError *internal_error = NULL;
911
912 db_iface = TRACKER_DB_INTERFACE (initable);
913
914 open_database (db_iface, &internal_error);
915
916 if (internal_error) {
917 g_propagate_error (error, internal_error);
918 return FALSE;
919 }
920
921 return TRUE;
922 }
923
924 static void
925 tracker_db_interface_initable_iface_init (GInitableIface *iface)
926 {
927 iface->init = tracker_db_interface_initable_init;
928 }
929
930 static void
931 tracker_db_interface_sqlite_set_property (GObject *object,
932 guint prop_id,
933 const GValue *value,
934 GParamSpec *pspec)
935 {
936 TrackerDBInterface *db_iface;
937
938 db_iface = TRACKER_DB_INTERFACE (object);
939
940 switch (prop_id) {
941 case PROP_RO:
942 db_iface->ro = g_value_get_boolean (value);
943 break;
944 case PROP_FILENAME:
945 db_iface->filename = g_value_dup_string (value);
946 break;
947 default:
948 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
949 }
950 }
951
952 static void
953 tracker_db_interface_sqlite_get_property (GObject *object,
954 guint prop_id,
955 GValue *value,
956 GParamSpec *pspec)
957 {
958 TrackerDBInterface *db_iface;
959
960 db_iface = TRACKER_DB_INTERFACE (object);
961
962 switch (prop_id) {
963 case PROP_RO:
964 g_value_set_boolean (value, db_iface->ro);
965 break;
966 case PROP_FILENAME:
967 g_value_set_string (value, db_iface->filename);
968 break;
969 default:
970 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
971 }
972 }
973
974 static void
975 close_database (TrackerDBInterface *db_interface)
976 {
977 gint rc;
978
979 if (db_interface->dynamic_statements) {
980 g_hash_table_unref (db_interface->dynamic_statements);
981 db_interface->dynamic_statements = NULL;
982 }
983
984 if (db_interface->function_data) {
985 g_slist_foreach (db_interface->function_data, (GFunc) g_free, NULL);
986 g_slist_free (db_interface->function_data);
987 db_interface->function_data = NULL;
988 }
989
990 if (db_interface->db) {
991 rc = sqlite3_close (db_interface->db);
992 g_warn_if_fail (rc == SQLITE_OK);
993 }
994 }
995
996 static gchar **
997 _fts_create_properties (GHashTable *properties)
998 {
999 GHashTableIter iter;
1000 GPtrArray *cols;
1001 GList *columns;
1002 gchar *table;
1003
1004 if (g_hash_table_size (properties) == 0) {
1005 return NULL;
1006 }
1007
1008 g_hash_table_iter_init (&iter, properties);
1009 cols = g_ptr_array_new ();
1010
1011 while (g_hash_table_iter_next (&iter, (gpointer *) &table,
1012 (gpointer *) &columns)) {
1013 while (columns) {
1014 g_ptr_array_add (cols, g_strdup (columns->data));
1015 columns = columns->next;
1016 }
1017 }
1018
1019 g_ptr_array_add (cols, NULL);
1020
1021 return (gchar **) g_ptr_array_free (cols, FALSE);
1022 }
1023
1024 void
1025 tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
1026 GHashTable *properties,
1027 GHashTable *multivalued,
1028 gboolean create)
1029 {
1030 #if HAVE_TRACKER_FTS
1031 GStrv fts_columns = NULL;
1032
1033 tracker_fts_init_db (db_interface->db);
1034
1035 if (properties) {
1036 fts_columns = _fts_create_properties (properties);
1037 }
1038
1039 if (create &&
1040 !tracker_fts_create_table (db_interface->db, "fts",
1041 properties, multivalued)) {
1042 g_warning ("FTS tables creation failed");
1043 }
1044
1045 if (fts_columns) {
1046 GString *insert, *select;
1047 gint i = 0;
1048
1049 insert = g_string_new ("INSERT INTO fts (docid");
1050 select = g_string_new ("SELECT rowid");
1051
1052 while (fts_columns[i]) {
1053 g_string_append_printf (insert, ", \"%s\"",
1054 fts_columns[i]);
1055 g_string_append_printf (select, ", \"%s\"",
1056 fts_columns[i]);
1057 i++;
1058 }
1059
1060 g_string_append (select, " FROM fts_view WHERE rowid=?");
1061 g_string_append (insert, ") ");
1062 g_string_append (insert, select->str);
1063
1064 g_string_free (select, TRUE);
1065 db_interface->fts_insert_str = g_string_free (insert, FALSE);
1066
1067 g_strfreev (fts_columns);
1068 }
1069 #endif
1070 }
1071
1072 #if HAVE_TRACKER_FTS
1073 void
1074 tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *db_interface,
1075 GHashTable *properties,
1076 GHashTable *multivalued)
1077 {
1078 if (!tracker_fts_alter_table (db_interface->db, "fts", properties, multivalued)) {
1079 g_critical ("Failed to update FTS columns");
1080 }
1081 }
1082
1083 gboolean
1084 tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
1085 int id,
1086 const gchar **properties,
1087 const char **text,
1088 gboolean create)
1089 {
1090 TrackerDBStatement *stmt;
1091 GError *error = NULL;
1092
1093 if (!create) {
1094 stmt = tracker_db_interface_create_statement (db_interface,
1095 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
1096 &error,
1097 "DELETE FROM fts WHERE docid=?");
1098 tracker_db_statement_bind_int (stmt, 0, id);
1099
1100 tracker_db_statement_execute (stmt, &error);
1101 g_object_unref (stmt);
1102
1103 if (error) {
1104 g_warning ("Could not update FTS text: %s", error->message);
1105 g_error_free (error);
1106 return FALSE;
1107 }
1108 }
1109
1110 stmt = tracker_db_interface_create_statement (db_interface,
1111 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
1112 &error,
1113 "%s",
1114 db_interface->fts_insert_str);
1115
1116 if (!stmt || error) {
1117 if (error) {
1118 g_warning ("Could not create FTS insert statement: %s\n",
1119 error->message);
1120 g_error_free (error);
1121 }
1122 return FALSE;
1123 }
1124
1125 tracker_db_statement_bind_int (stmt, 0, id);
1126 tracker_db_statement_execute (stmt, &error);
1127 g_object_unref (stmt);
1128
1129 if (error) {
1130 g_warning ("Could not insert FTS text: %s", error->message);
1131 g_error_free (error);
1132 return FALSE;
1133 }
1134
1135 return TRUE;
1136 }
1137
1138 gboolean
1139 tracker_db_interface_sqlite_fts_delete_text (TrackerDBInterface *db_interface,
1140 int id,
1141 const gchar *property)
1142 {
1143 TrackerDBStatement *stmt;
1144 GError *error = NULL;
1145
1146 stmt = tracker_db_interface_create_statement (db_interface,
1147 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
1148 &error,
1149 "UPDATE fts SET \"%s\" = '' WHERE docid = ?",
1150 property);
1151
1152 if (!stmt || error) {
1153 if (error) {
1154 g_warning ("Could not create FTS update statement: %s\n",
1155 error->message);
1156 g_error_free (error);
1157 }
1158 return FALSE;
1159 }
1160
1161 tracker_db_statement_bind_int (stmt, 0, id);
1162 tracker_db_statement_execute (stmt, &error);
1163 g_object_unref (stmt);
1164
1165 if (error) {
1166 g_warning ("Could not execute FTS update: %s", error->message);
1167 g_error_free (error);
1168 return FALSE;
1169 }
1170
1171 return TRUE;
1172 }
1173
1174 #endif
1175
1176 void
1177 tracker_db_interface_sqlite_reset_collator (TrackerDBInterface *db_interface)
1178 {
1179 g_debug ("Resetting collator in db interface %p", db_interface);
1180
1181 /* This will overwrite any other collation set before, if any */
1182 if (sqlite3_create_collation_v2 (db_interface->db,
1183 TRACKER_COLLATION_NAME,
1184 SQLITE_UTF8,
1185 tracker_collation_init (),
1186 tracker_collation_utf8,
1187 tracker_collation_shutdown) != SQLITE_OK)
1188 {
1189 g_critical ("Couldn't set collation function: %s",
1190 sqlite3_errmsg (db_interface->db));
1191 }
1192 }
1193
1194 static gint
1195 wal_hook (gpointer user_data,
1196 sqlite3 *db,
1197 const gchar *db_name,
1198 gint n_pages)
1199 {
1200 ((TrackerDBWalCallback) user_data) (n_pages);
1201
1202 return SQLITE_OK;
1203 }
1204
1205 void
1206 tracker_db_interface_sqlite_wal_hook (TrackerDBInterface *interface,
1207 TrackerDBWalCallback callback)
1208 {
1209 sqlite3_wal_hook (interface->db, wal_hook, callback);
1210 }
1211
1212
1213 static void
1214 tracker_db_interface_sqlite_finalize (GObject *object)
1215 {
1216 TrackerDBInterface *db_interface;
1217
1218 db_interface = TRACKER_DB_INTERFACE (object);
1219
1220 close_database (db_interface);
1221 g_free (db_interface->fts_insert_str);
1222
1223 g_message ("Closed sqlite3 database:'%s'", db_interface->filename);
1224
1225 g_free (db_interface->filename);
1226 g_free (db_interface->busy_status);
1227
1228 if (db_interface->locale_notification_id) {
1229 tracker_locale_notify_remove (db_interface->locale_notification_id);
1230 }
1231
1232 G_OBJECT_CLASS (tracker_db_interface_parent_class)->finalize (object);
1233 }
1234
1235 static void
1236 tracker_db_interface_class_init (TrackerDBInterfaceClass *class)
1237 {
1238 GObjectClass *object_class = G_OBJECT_CLASS (class);
1239
1240 object_class->set_property = tracker_db_interface_sqlite_set_property;
1241 object_class->get_property = tracker_db_interface_sqlite_get_property;
1242 object_class->finalize = tracker_db_interface_sqlite_finalize;
1243
1244 g_object_class_install_property (object_class,
1245 PROP_FILENAME,
1246 g_param_spec_string ("filename",
1247 "DB filename",
1248 "DB filename",
1249 NULL,
1250 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1251
1252 g_object_class_install_property (object_class,
1253 PROP_RO,
1254 g_param_spec_boolean ("read-only",
1255 "Read only",
1256 "Whether the connection is read only",
1257 FALSE,
1258 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1259 }
1260
1261 static void
1262 prepare_database (TrackerDBInterface *db_interface)
1263 {
1264 db_interface->dynamic_statements = g_hash_table_new_full (g_str_hash, g_str_equal,
1265 NULL,
1266 (GDestroyNotify) g_object_unref);
1267 }
1268
1269 static void
1270 tracker_db_interface_init (TrackerDBInterface *db_interface)
1271 {
1272 db_interface->ro = FALSE;
1273
1274 prepare_database (db_interface);
1275 }
1276
1277 void
1278 tracker_db_interface_set_max_stmt_cache_size (TrackerDBInterface *db_interface,
1279 TrackerDBStatementCacheType cache_type,
1280 guint max_size)
1281 {
1282 TrackerDBStatementLru *stmt_lru;
1283
1284 if (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE) {
1285 stmt_lru = &db_interface->update_stmt_lru;
1286 } else if (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT) {
1287 stmt_lru = &db_interface->select_stmt_lru;
1288 } else {
1289 return;
1290 }
1291
1292 /* Must be larger than 2 to make sense (to have a tail and head) */
1293 if (max_size > 2) {
1294 stmt_lru->max = max_size;
1295 } else {
1296 stmt_lru->max = 3;
1297 }
1298 }
1299
1300 void
1301 tracker_db_interface_set_busy_handler (TrackerDBInterface *db_interface,
1302 TrackerBusyCallback busy_callback,
1303 const gchar *busy_status,
1304 gpointer busy_user_data)
1305 {
1306 g_return_if_fail (TRACKER_IS_DB_INTERFACE (db_interface));
1307 db_interface->busy_callback = busy_callback;
1308 db_interface->busy_user_data = busy_user_data;
1309 g_free (db_interface->busy_status);
1310
1311 if (busy_status) {
1312 db_interface->busy_status = g_strdup (busy_status);
1313 } else {
1314 db_interface->busy_status = NULL;
1315 }
1316 }
1317
1318 TrackerDBStatement *
1319 tracker_db_interface_create_statement (TrackerDBInterface *db_interface,
1320 TrackerDBStatementCacheType cache_type,
1321 GError **error,
1322 const gchar *query,
1323 ...)
1324 {
1325 TrackerDBStatementLru *stmt_lru = NULL;
1326 TrackerDBStatement *stmt;
1327 va_list args;
1328 gchar *full_query;
1329
1330 g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (db_interface), NULL);
1331
1332 va_start (args, query);
1333 full_query = g_strdup_vprintf (query, args);
1334 va_end (args);
1335
1336 /* There are three kinds of queries:
1337 * a) Cached queries: SELECT and UPDATE ones (cache_type)
1338 * b) Non-Cached queries: NONE ones (cache_type)
1339 * c) Forced Non-Cached: in case of a stmt being already in use, we can't
1340 * reuse it (you can't use two different loops on a sqlite3_stmt, of
1341 * course). This happens with recursive uses of a cursor, for example */
1342
1343 if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
1344 stmt = g_hash_table_lookup (db_interface->dynamic_statements, full_query);
1345
1346 if (stmt && stmt->stmt_is_sunk) {
1347 /* c) Forced non-cached
1348 * prepared statement is still in use, create new uncached one */
1349 stmt = NULL;
1350 /* Make sure to set cache_type here, to ensure the right flow in the
1351 * LRU-cache below */
1352 cache_type = TRACKER_DB_STATEMENT_CACHE_TYPE_NONE;
1353 }
1354
1355 if (cache_type == TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE) {
1356 /* a) Cached */
1357 stmt_lru = &db_interface->update_stmt_lru;
1358 } else {
1359 /* a) Cached */
1360 stmt_lru = &db_interface->select_stmt_lru;
1361 }
1362
1363 } else {
1364 /* b) Non-Cached */
1365 stmt = NULL;
1366 }
1367
1368 if (!stmt) {
1369 sqlite3_stmt *sqlite_stmt;
1370 int retval;
1371
1372 g_debug ("Preparing query: '%s'", full_query);
1373
1374 retval = sqlite3_prepare_v2 (db_interface->db, full_query, -1, &sqlite_stmt, NULL);
1375
1376 if (retval != SQLITE_OK) {
1377
1378 if (retval == SQLITE_INTERRUPT) {
1379 g_set_error (error,
1380 TRACKER_DB_INTERFACE_ERROR,
1381 TRACKER_DB_INTERRUPTED,
1382 "Interrupted");
1383 } else {
1384 g_set_error (error,
1385 TRACKER_DB_INTERFACE_ERROR,
1386 TRACKER_DB_QUERY_ERROR,
1387 "%s",
1388 sqlite3_errmsg (db_interface->db));
1389 }
1390
1391 g_free (full_query);
1392
1393 return NULL;
1394 }
1395
1396 stmt = tracker_db_statement_sqlite_new (db_interface, sqlite_stmt);
1397
1398 if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
1399 /* use replace instead of insert to make sure we store the string that
1400 belongs to the right sqlite statement to ensure the lifetime of the string
1401 matches the statement */
1402 g_hash_table_replace (db_interface->dynamic_statements,
1403 (gpointer) sqlite3_sql (sqlite_stmt),
1404 stmt);
1405
1406 /* So the ring looks a bit like this: *
1407 * *
1408 * .--tail .--head *
1409 * | | *
1410 * [p-n] -> [p-n] -> [p-n] -> [p-n] *
1411 * ^ | *
1412 * `- [n-p] <- [n-p] <--------' *
1413 * */
1414
1415 if (stmt_lru->size >= stmt_lru->max) {
1416 TrackerDBStatement *new_head;
1417
1418 /* We reached max-size of the LRU stmt cache. Destroy current
1419 * least recently used (stmt_lru.head) and fix the ring. For
1420 * that we take out the current head, and close the ring.
1421 * Then we assign head->next as new head. */
1422
1423 new_head = stmt_lru->head->next;
1424 g_hash_table_remove (db_interface->dynamic_statements,
1425 (gpointer) sqlite3_sql (stmt_lru->head->stmt));
1426 stmt_lru->size--;
1427 stmt_lru->head = new_head;
1428 } else {
1429 if (stmt_lru->size == 0) {
1430 stmt_lru->head = stmt;
1431 stmt_lru->tail = stmt;
1432 }
1433 }
1434
1435 /* Set the current stmt (which is always new here) as the new tail
1436 * (new most recent used). We insert current stmt between head and
1437 * current tail, and we set tail to current stmt. */
1438
1439 stmt_lru->size++;
1440 stmt->next = stmt_lru->head;
1441 stmt_lru->head->prev = stmt;
1442
1443 stmt_lru->tail->next = stmt;
1444 stmt->prev = stmt_lru->tail;
1445 stmt_lru->tail = stmt;
1446 }
1447 } else {
1448 tracker_db_statement_sqlite_reset (stmt);
1449
1450 if (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) {
1451 if (stmt == stmt_lru->head) {
1452
1453 /* Current stmt is least recently used, shift head and tail
1454 * of the ring to efficiently make it most recently used. */
1455
1456 stmt_lru->head = stmt_lru->head->next;
1457 stmt_lru->tail = stmt_lru->tail->next;
1458 } else if (stmt != stmt_lru->tail) {
1459
1460 /* Current statement isn't most recently used, make it most
1461 * recently used now (less efficient way than above). */
1462
1463 /* Take stmt out of the list and close the ring */
1464 stmt->prev->next = stmt->next;
1465 stmt->next->prev = stmt->prev;
1466
1467 /* Put stmt as tail (most recent used) */
1468 stmt->next = stmt_lru->head;
1469 stmt_lru->head->prev = stmt;
1470 stmt->prev = stmt_lru->tail;
1471 stmt_lru->tail->next = stmt;
1472 stmt_lru->tail = stmt;
1473 }
1474
1475 /* if (stmt == tail), it's already the most recently used in the
1476 * ring, so in this case we do nothing of course */
1477 }
1478 }
1479
1480 g_free (full_query);
1481
1482 return (cache_type != TRACKER_DB_STATEMENT_CACHE_TYPE_NONE) ? g_object_ref (stmt) : stmt;
1483 }
1484
1485 static void
1486 execute_stmt (TrackerDBInterface *interface,
1487 sqlite3_stmt *stmt,
1488 GCancellable *cancellable,
1489 GError **error)
1490 {
1491 gint result;
1492
1493 result = SQLITE_OK;
1494
1495 /* Statement is going to start, check if we got a request to reset the
1496 * collator, and if so, do it. */
1497 if (ATOMIC_EXCHANGE_AND_ADD (&interface->n_active_cursors, 1) == 0 &&
1498 g_atomic_int_compare_and_exchange (&(interface->collator_reset_requested), TRUE, FALSE)) {
1499 tracker_db_interface_sqlite_reset_collator (interface);
1500 }
1501
1502 while (result == SQLITE_OK ||
1503 result == SQLITE_ROW) {
1504
1505 if (g_cancellable_is_cancelled (cancellable)) {
1506 result = SQLITE_INTERRUPT;
1507 sqlite3_reset (stmt);
1508 } else {
1509 /* only one statement can be active at the same time per interface */
1510 interface->cancellable = cancellable;
1511 result = stmt_step (stmt);
1512
1513 interface->cancellable = NULL;
1514 }
1515
1516 switch (result) {
1517 case SQLITE_ERROR:
1518 sqlite3_reset (stmt);
1519 break;
1520 case SQLITE_ROW:
1521 break;
1522 default:
1523 break;
1524 }
1525 }
1526
1527
1528 if (result == SQLITE_DONE) {
1529 /* Statement finished, check if we got a request to reset the
1530 * collator, and if so, do it.
1531 */
1532 if (g_atomic_int_dec_and_test (&interface->n_active_cursors) &&
1533 g_atomic_int_compare_and_exchange (&(interface->collator_reset_requested), TRUE, FALSE)) {
1534 tracker_db_interface_sqlite_reset_collator (interface);
1535 }
1536 } else {
1537 g_atomic_int_add (&interface->n_active_cursors, -1);
1538
1539 /* This is rather fatal */
1540 if (errno != ENOSPC &&
1541 (sqlite3_errcode (interface->db) == SQLITE_IOERR ||
1542 sqlite3_errcode (interface->db) == SQLITE_CORRUPT ||
1543 sqlite3_errcode (interface->db) == SQLITE_NOTADB)) {
1544
1545 g_critical ("SQLite error: %s (errno: %s)",
1546 sqlite3_errmsg (interface->db),
1547 g_strerror (errno));
1548
1549 #ifndef DISABLE_JOURNAL
1550 g_unlink (interface->filename);
1551
1552 g_error ("SQLite experienced an error with file:'%s'. "
1553 "It is either NOT a SQLite database or it is "
1554 "corrupt or there was an IO error accessing the data. "
1555 "This file has now been removed and will be recreated on the next start. "
1556 "Shutting down now.",
1557 interface->filename);
1558
1559 return;
1560 #endif /* DISABLE_JOURNAL */
1561 }
1562
1563 if (!error) {
1564 g_warning ("Could not perform SQLite operation, error:%d->'%s'",
1565 sqlite3_errcode (interface->db),
1566 sqlite3_errmsg (interface->db));
1567 } else {
1568 if (result == SQLITE_INTERRUPT) {
1569 g_set_error (error,
1570 TRACKER_DB_INTERFACE_ERROR,
1571 TRACKER_DB_INTERRUPTED,
1572 "Interrupted");
1573 } else {
1574 g_set_error (error,
1575 TRACKER_DB_INTERFACE_ERROR,
1576 errno != ENOSPC ? TRACKER_DB_QUERY_ERROR : TRACKER_DB_NO_SPACE,
1577 "%s%s%s%s",
1578 sqlite3_errmsg (interface->db),
1579 errno != 0 ? " (strerror of errno (not necessarily related): " : "",
1580 errno != 0 ? g_strerror (errno) : "",
1581 errno != 0 ? ")" : "");
1582 }
1583 }
1584 }
1585 }
1586
1587 void
1588 tracker_db_interface_execute_vquery (TrackerDBInterface *db_interface,
1589 GError **error,
1590 const gchar *query,
1591 va_list args)
1592 {
1593 gchar *full_query;
1594 sqlite3_stmt *stmt;
1595 int retval;
1596
1597 full_query = g_strdup_vprintf (query, args);
1598
1599 /* g_debug ("Running query: '%s'", full_query); */
1600 retval = sqlite3_prepare_v2 (db_interface->db, full_query, -1, &stmt, NULL);
1601
1602 if (retval != SQLITE_OK) {
1603 g_set_error (error,
1604 TRACKER_DB_INTERFACE_ERROR,
1605 TRACKER_DB_QUERY_ERROR,
1606 "%s",
1607 sqlite3_errmsg (db_interface->db));
1608 g_free (full_query);
1609 return;
1610 } else if (stmt == NULL) {
1611 g_set_error (error,
1612 TRACKER_DB_INTERFACE_ERROR,
1613 TRACKER_DB_QUERY_ERROR,
1614 "Could not prepare SQL statement:'%s'",
1615 full_query);
1616
1617 g_free (full_query);
1618 return;
1619 }
1620
1621 execute_stmt (db_interface, stmt, NULL, error);
1622 sqlite3_finalize (stmt);
1623
1624 g_free (full_query);
1625 }
1626
1627 TrackerDBInterface *
1628 tracker_db_interface_sqlite_new (const gchar *filename,
1629 GError **error)
1630 {
1631 TrackerDBInterface *object;
1632 GError *internal_error = NULL;
1633
1634 object = g_initable_new (TRACKER_TYPE_DB_INTERFACE,
1635 NULL,
1636 &internal_error,
1637 "filename", filename,
1638 NULL);
1639
1640 if (internal_error) {
1641 g_propagate_error (error, internal_error);
1642 return NULL;
1643 }
1644
1645 return object;
1646 }
1647
1648 TrackerDBInterface *
1649 tracker_db_interface_sqlite_new_ro (const gchar *filename,
1650 GError **error)
1651 {
1652 TrackerDBInterface *object;
1653 GError *internal_error = NULL;
1654
1655 object = g_initable_new (TRACKER_TYPE_DB_INTERFACE,
1656 NULL,
1657 &internal_error,
1658 "filename", filename,
1659 "read-only", TRUE,
1660 NULL);
1661
1662 if (internal_error) {
1663 g_propagate_error (error, internal_error);
1664 return NULL;
1665 }
1666
1667 return object;
1668 }
1669
1670 gint64
1671 tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterface *interface)
1672 {
1673 g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), 0);
1674
1675 return (gint64) sqlite3_last_insert_rowid (interface->db);
1676 }
1677
1678 static void
1679 tracker_db_statement_finalize (GObject *object)
1680 {
1681 TrackerDBStatement *stmt;
1682
1683 stmt = TRACKER_DB_STATEMENT (object);
1684
1685 /* A cursor was still open while we're being finalized, because a cursor
1686 * holds its own reference, this means that somebody is unreffing a stmt
1687 * too often. We mustn't sqlite3_finalize the priv->stmt in this case,
1688 * though. It would crash&burn the cursor. */
1689
1690 g_assert (!stmt->stmt_is_sunk);
1691
1692 sqlite3_finalize (stmt->stmt);
1693
1694 G_OBJECT_CLASS (tracker_db_statement_parent_class)->finalize (object);
1695 }
1696
1697 static void
1698 tracker_db_statement_class_init (TrackerDBStatementClass *class)
1699 {
1700 GObjectClass *object_class = G_OBJECT_CLASS (class);
1701
1702 object_class->finalize = tracker_db_statement_finalize;
1703 }
1704
1705 static TrackerDBStatement *
1706 tracker_db_statement_sqlite_new (TrackerDBInterface *db_interface,
1707 sqlite3_stmt *sqlite_stmt)
1708 {
1709 TrackerDBStatement *stmt;
1710
1711 stmt = g_object_new (TRACKER_TYPE_DB_STATEMENT, NULL);
1712
1713 stmt->db_interface = db_interface;
1714 stmt->stmt = sqlite_stmt;
1715 stmt->stmt_is_sunk = FALSE;
1716
1717 return stmt;
1718 }
1719
1720 static void
1721 tracker_db_cursor_close (TrackerDBCursor *cursor)
1722 {
1723 TrackerDBInterface *iface;
1724
1725 g_return_if_fail (TRACKER_IS_DB_CURSOR (cursor));
1726
1727 if (cursor->ref_stmt == NULL) {
1728 /* already closed */
1729 return;
1730 }
1731
1732 /* As soon as we finalize the cursor, check if we need a collator reset
1733 * and notify the iface about the removed cursor */
1734 iface = cursor->ref_stmt->db_interface;
1735 if (g_atomic_int_dec_and_test (&iface->n_active_cursors) &&
1736 g_atomic_int_compare_and_exchange (&(iface->collator_reset_requested), TRUE, FALSE)) {
1737 tracker_db_interface_sqlite_reset_collator (iface);
1738 }
1739
1740 if (cursor->threadsafe) {
1741 tracker_db_manager_lock ();
1742 }
1743
1744 cursor->ref_stmt->stmt_is_sunk = FALSE;
1745 tracker_db_statement_sqlite_reset (cursor->ref_stmt);
1746 g_object_unref (cursor->ref_stmt);
1747 cursor->ref_stmt = NULL;
1748
1749 if (cursor->threadsafe) {
1750 tracker_db_manager_unlock ();
1751 }
1752 }
1753
1754 static void
1755 tracker_db_cursor_finalize (GObject *object)
1756 {
1757 TrackerDBCursor *cursor;
1758 int i;
1759
1760 cursor = TRACKER_DB_CURSOR (object);
1761
1762 tracker_db_cursor_close (cursor);
1763
1764 g_free (cursor->types);
1765
1766 for (i = 0; i < cursor->n_variable_names; i++) {
1767 g_free (cursor->variable_names[i]);
1768 }
1769 g_free (cursor->variable_names);
1770
1771 G_OBJECT_CLASS (tracker_db_cursor_parent_class)->finalize (object);
1772 }
1773
1774 static void
1775 tracker_db_cursor_get_property (GObject *object,
1776 guint prop_id,
1777 GValue *value,
1778 GParamSpec *pspec)
1779 {
1780 TrackerDBCursor *cursor = TRACKER_DB_CURSOR (object);
1781 switch (prop_id) {
1782 case TRACKER_DB_CURSOR_PROP_N_COLUMNS:
1783 g_value_set_int (value, tracker_db_cursor_get_n_columns (cursor));
1784 break;
1785 default:
1786 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1787 }
1788 }
1789
1790 static void
1791 tracker_db_cursor_iter_next_thread (GSimpleAsyncResult *res,
1792 GObject *object,
1793 GCancellable *cancellable)
1794 {
1795 /* run in thread */
1796
1797 TrackerDBCursor *cursor = TRACKER_DB_CURSOR (object);
1798 GError *error = NULL;
1799 gboolean result;
1800
1801 result = db_cursor_iter_next (cursor, cancellable, &error);
1802 if (error) {
1803 g_simple_async_result_set_from_error (res, error);
1804 } else {
1805 g_simple_async_result_set_op_res_gboolean (res, result);
1806 }
1807 }
1808
1809 static void
1810 tracker_db_cursor_iter_next_async (TrackerDBCursor *cursor,
1811 GCancellable *cancellable,
1812 GAsyncReadyCallback callback,
1813 gpointer user_data)
1814 {
1815 GSimpleAsyncResult *res;
1816
1817 res = g_simple_async_result_new (G_OBJECT (cursor), callback, user_data, tracker_db_cursor_iter_next_async);
1818 g_simple_async_result_run_in_thread (res, tracker_db_cursor_iter_next_thread, 0, cancellable);
1819 g_object_unref (res);
1820 }
1821
1822 static gboolean
1823 tracker_db_cursor_iter_next_finish (TrackerDBCursor *cursor,
1824 GAsyncResult *res,
1825 GError **error)
1826 {
1827 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) {
1828 return FALSE;
1829 }
1830 return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
1831 }
1832
1833 static void
1834 tracker_db_cursor_class_init (TrackerDBCursorClass *class)
1835 {
1836 GObjectClass *object_class = G_OBJECT_CLASS (class);
1837 TrackerSparqlCursorClass *sparql_cursor_class = TRACKER_SPARQL_CURSOR_CLASS (class);
1838
1839 object_class->finalize = tracker_db_cursor_finalize;
1840 object_class->get_property = tracker_db_cursor_get_property;
1841
1842 sparql_cursor_class->get_value_type = (TrackerSparqlValueType (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_value_type;
1843 sparql_cursor_class->get_variable_name = (const gchar * (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_variable_name;
1844 sparql_cursor_class->get_n_columns = (gint (*) (TrackerSparqlCursor *)) tracker_db_cursor_get_n_columns;
1845 sparql_cursor_class->get_string = (const gchar * (*) (TrackerSparqlCursor *, gint, glong*)) tracker_db_cursor_get_string;
1846 sparql_cursor_class->next = (gboolean (*) (TrackerSparqlCursor *, GCancellable *, GError **)) tracker_db_cursor_iter_next;
1847 sparql_cursor_class->next_async = (void (*) (TrackerSparqlCursor *, GCancellable *, GAsyncReadyCallback, gpointer)) tracker_db_cursor_iter_next_async;
1848 sparql_cursor_class->next_finish = (gboolean (*) (TrackerSparqlCursor *, GAsyncResult *, GError **)) tracker_db_cursor_iter_next_finish;
1849 sparql_cursor_class->rewind = (void (*) (TrackerSparqlCursor *)) tracker_db_cursor_rewind;
1850 sparql_cursor_class->close = (void (*) (TrackerSparqlCursor *)) tracker_db_cursor_close;
1851
1852 sparql_cursor_class->get_integer = (gint64 (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_int;
1853 sparql_cursor_class->get_double = (gdouble (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_double;
1854 sparql_cursor_class->get_boolean = (gboolean (*) (TrackerSparqlCursor *, gint)) tracker_db_cursor_get_boolean;
1855
1856 g_object_class_override_property (object_class, TRACKER_DB_CURSOR_PROP_N_COLUMNS, "n-columns");
1857 }
1858
1859 static TrackerDBCursor *
1860 tracker_db_cursor_sqlite_new (sqlite3_stmt *sqlite_stmt,
1861 TrackerDBStatement *ref_stmt,
1862 TrackerPropertyType *types,
1863 gint n_types,
1864 const gchar **variable_names,
1865 gint n_variable_names,
1866 gboolean threadsafe)
1867 {
1868 TrackerDBCursor *cursor;
1869 TrackerDBInterface *iface;
1870
1871 /* As soon as we create a cursor, check if we need a collator reset
1872 * and notify the iface about the new cursor */
1873 iface = ref_stmt->db_interface;
1874 if (ATOMIC_EXCHANGE_AND_ADD (&iface->n_active_cursors, 1) == 0 &&
1875 g_atomic_int_compare_and_exchange (&(iface->collator_reset_requested), TRUE, FALSE)) {
1876 tracker_db_interface_sqlite_reset_collator (iface);
1877 }
1878
1879 cursor = g_object_new (TRACKER_TYPE_DB_CURSOR, NULL);
1880
1881 cursor->finished = FALSE;
1882
1883 /* used for direct access as libtracker-sparql is thread-safe and
1884 uses a single shared connection with SQLite mutex disabled */
1885 cursor->threadsafe = threadsafe;
1886
1887 cursor->stmt = sqlite_stmt;
1888 ref_stmt->stmt_is_sunk = TRUE;
1889 cursor->ref_stmt = g_object_ref (ref_stmt);
1890
1891 if (types) {
1892 gint i;
1893
1894 cursor->types = g_new (TrackerPropertyType, n_types);
1895 cursor->n_types = n_types;
1896 for (i = 0; i < n_types; i++) {
1897 cursor->types[i] = types[i];
1898 }
1899 }
1900
1901 if (variable_names) {
1902 gint i;
1903
1904 cursor->variable_names = g_new (gchar *, n_variable_names);
1905 cursor->n_variable_names = n_variable_names;
1906 for (i = 0; i < n_variable_names; i++) {
1907 cursor->variable_names[i] = g_strdup (variable_names[i]);
1908 }
1909 }
1910
1911 return cursor;
1912 }
1913
1914 void
1915 tracker_db_statement_bind_double (TrackerDBStatement *stmt,
1916 int index,
1917 double value)
1918 {
1919 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
1920
1921 g_assert (!stmt->stmt_is_sunk);
1922
1923 sqlite3_bind_double (stmt->stmt, index + 1, value);
1924 }
1925
1926 void
1927 tracker_db_statement_bind_int (TrackerDBStatement *stmt,
1928 int index,
1929 gint64 value)
1930 {
1931 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
1932
1933 g_assert (!stmt->stmt_is_sunk);
1934
1935 sqlite3_bind_int64 (stmt->stmt, index + 1, value);
1936 }
1937
1938 void
1939 tracker_db_statement_bind_null (TrackerDBStatement *stmt,
1940 int index)
1941 {
1942 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
1943
1944 g_assert (!stmt->stmt_is_sunk);
1945
1946 sqlite3_bind_null (stmt->stmt, index + 1);
1947 }
1948
1949 void
1950 tracker_db_statement_bind_text (TrackerDBStatement *stmt,
1951 int index,
1952 const gchar *value)
1953 {
1954 g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
1955
1956 g_assert (!stmt->stmt_is_sunk);
1957
1958 sqlite3_bind_text (stmt->stmt, index + 1, value, -1, SQLITE_TRANSIENT);
1959 }
1960
1961 void
1962 tracker_db_cursor_rewind (TrackerDBCursor *cursor)
1963 {
1964 g_return_if_fail (TRACKER_IS_DB_CURSOR (cursor));
1965
1966 if (cursor->threadsafe) {
1967 tracker_db_manager_lock ();
1968 }
1969
1970 sqlite3_reset (cursor->stmt);
1971 cursor->finished = FALSE;
1972
1973 if (cursor->threadsafe) {
1974 tracker_db_manager_unlock ();
1975 }
1976 }
1977
1978 gboolean
1979 tracker_db_cursor_iter_next (TrackerDBCursor *cursor,
1980 GCancellable *cancellable,
1981 GError **error)
1982 {
1983 return db_cursor_iter_next (cursor, cancellable, error);
1984 }
1985
1986
1987 static gboolean
1988 db_cursor_iter_next (TrackerDBCursor *cursor,
1989 GCancellable *cancellable,
1990 GError **error)
1991 {
1992 TrackerDBStatement *stmt = cursor->ref_stmt;
1993 TrackerDBInterface *iface = stmt->db_interface;
1994
1995 if (!cursor->finished) {
1996 guint result;
1997
1998 if (cursor->threadsafe) {
1999 tracker_db_manager_lock ();
2000 }
2001
2002 if (g_cancellable_is_cancelled (cancellable)) {
2003 result = SQLITE_INTERRUPT;
2004 sqlite3_reset (cursor->stmt);
2005 } else {
2006 /* only one statement can be active at the same time per interface */
2007 iface->cancellable = cancellable;
2008 result = stmt_step (cursor->stmt);
2009 iface->cancellable = NULL;
2010 }
2011
2012 if (result == SQLITE_INTERRUPT) {
2013 g_set_error (error,
2014 TRACKER_DB_INTERFACE_ERROR,
2015 TRACKER_DB_INTERRUPTED,
2016 "Interrupted");
2017 } else if (result != SQLITE_ROW && result != SQLITE_DONE) {
2018 g_set_error (error,
2019 TRACKER_DB_INTERFACE_ERROR,
2020 TRACKER_DB_QUERY_ERROR,
2021 "%s", sqlite3_errmsg (iface->db));
2022 }
2023
2024 cursor->finished = (result != SQLITE_ROW);
2025
2026 if (cursor->threadsafe) {
2027 tracker_db_manager_unlock ();
2028 }
2029 }
2030
2031 return (!cursor->finished);
2032 }
2033
2034 guint
2035 tracker_db_cursor_get_n_columns (TrackerDBCursor *cursor)
2036 {
2037 return sqlite3_column_count (cursor->stmt);
2038 }
2039
2040 void
2041 tracker_db_cursor_get_value (TrackerDBCursor *cursor,
2042 guint column,
2043 GValue *value)
2044 {
2045 gint col_type;
2046
2047 col_type = sqlite3_column_type (cursor->stmt, column);
2048
2049 switch (col_type) {
2050 case SQLITE_TEXT:
2051 g_value_init (value, G_TYPE_STRING);
2052 g_value_set_string (value, (gchar *) sqlite3_column_text (cursor->stmt, column));
2053 break;
2054 case SQLITE_INTEGER:
2055 g_value_init (value, G_TYPE_INT64);
2056 g_value_set_int64 (value, sqlite3_column_int64 (cursor->stmt, column));
2057 break;
2058 case SQLITE_FLOAT:
2059 g_value_init (value, G_TYPE_DOUBLE);
2060 g_value_set_double (value, sqlite3_column_double (cursor->stmt, column));
2061 break;
2062 case SQLITE_NULL:
2063 /* just ignore NULLs */
2064 break;
2065 default:
2066 g_critical ("Unknown sqlite3 database column type:%d", col_type);
2067 }
2068
2069 }
2070
2071 gint64
2072 tracker_db_cursor_get_int (TrackerDBCursor *cursor,
2073 guint column)
2074 {
2075 gint64 result;
2076
2077 if (cursor->threadsafe) {
2078 tracker_db_manager_lock ();
2079 }
2080
2081 result = (gint64) sqlite3_column_int64 (cursor->stmt, column);
2082
2083 if (cursor->threadsafe) {
2084 tracker_db_manager_unlock ();
2085 }
2086
2087 return result;
2088 }
2089
2090 gdouble
2091 tracker_db_cursor_get_double (TrackerDBCursor *cursor,
2092 guint column)
2093 {
2094 gdouble result;
2095
2096 if (cursor->threadsafe) {
2097 tracker_db_manager_lock ();
2098 }
2099
2100 result = (gdouble) sqlite3_column_double (cursor->stmt, column);
2101
2102 if (cursor->threadsafe) {
2103 tracker_db_manager_unlock ();
2104 }
2105
2106 return result;
2107 }
2108
2109 static gboolean
2110 tracker_db_cursor_get_boolean (TrackerSparqlCursor *sparql_cursor,
2111 guint column)
2112 {
2113 TrackerDBCursor *cursor = (TrackerDBCursor *) sparql_cursor;
2114 return (g_strcmp0 (tracker_db_cursor_get_string (cursor, column, NULL), "true") == 0);
2115 }
2116
2117 TrackerSparqlValueType
2118 tracker_db_cursor_get_value_type (TrackerDBCursor *cursor,
2119 guint column)
2120 {
2121 gint column_type;
2122 gint n_columns = sqlite3_column_count (cursor->stmt);
2123
2124 g_return_val_if_fail (column < n_columns, TRACKER_SPARQL_VALUE_TYPE_UNBOUND);
2125
2126 if (cursor->threadsafe) {
2127 tracker_db_manager_lock ();
2128 }
2129
2130 column_type = sqlite3_column_type (cursor->stmt, column);
2131
2132 if (cursor->threadsafe) {
2133 tracker_db_manager_unlock ();
2134 }
2135
2136 if (column_type == SQLITE_NULL) {
2137 return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
2138 } else if (column < cursor->n_types) {
2139 switch (cursor->types[column]) {
2140 case TRACKER_PROPERTY_TYPE_RESOURCE:
2141 return TRACKER_SPARQL_VALUE_TYPE_URI;
2142 case TRACKER_PROPERTY_TYPE_INTEGER:
2143 return TRACKER_SPARQL_VALUE_TYPE_INTEGER;
2144 case TRACKER_PROPERTY_TYPE_DOUBLE:
2145 return TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
2146 case TRACKER_PROPERTY_TYPE_DATETIME:
2147 return TRACKER_SPARQL_VALUE_TYPE_DATETIME;
2148 case TRACKER_PROPERTY_TYPE_BOOLEAN:
2149 return TRACKER_SPARQL_VALUE_TYPE_BOOLEAN;
2150 default:
2151 return TRACKER_SPARQL_VALUE_TYPE_STRING;
2152 }
2153 } else {
2154 return TRACKER_SPARQL_VALUE_TYPE_STRING;
2155 }
2156 }
2157
2158 const gchar*
2159 tracker_db_cursor_get_variable_name (TrackerDBCursor *cursor,
2160 guint column)
2161 {
2162 const gchar *result;
2163
2164 if (cursor->threadsafe) {
2165 tracker_db_manager_lock ();
2166 }
2167
2168 if (column < cursor->n_variable_names) {
2169 result = cursor->variable_names[column];
2170 } else {
2171 result = sqlite3_column_name (cursor->stmt, column);
2172 }
2173
2174 if (cursor->threadsafe) {
2175 tracker_db_manager_unlock ();
2176 }
2177
2178 return result;
2179 }
2180
2181 const gchar*
2182 tracker_db_cursor_get_string (TrackerDBCursor *cursor,
2183 guint column,
2184 glong *length)
2185 {
2186 const gchar *result;
2187
2188 if (cursor->threadsafe) {
2189 tracker_db_manager_lock ();
2190 }
2191
2192 if (length) {
2193 sqlite3_value *val = sqlite3_column_value (cursor->stmt, column);
2194
2195 *length = sqlite3_value_bytes (val);
2196 result = (const gchar *) sqlite3_value_text (val);
2197 } else {
2198 result = (const gchar *) sqlite3_column_text (cursor->stmt, column);
2199 }
2200
2201 if (cursor->threadsafe) {
2202 tracker_db_manager_unlock ();
2203 }
2204
2205 return result;
2206 }
2207
2208 void
2209 tracker_db_statement_execute (TrackerDBStatement *stmt,
2210 GError **error)
2211 {
2212 g_return_if_fail (!stmt->stmt_is_sunk);
2213
2214 execute_stmt (stmt->db_interface, stmt->stmt, NULL, error);
2215 }
2216
2217 TrackerDBCursor *
2218 tracker_db_statement_start_cursor (TrackerDBStatement *stmt,
2219 GError **error)
2220 {
2221 g_return_val_if_fail (!stmt->stmt_is_sunk, NULL);
2222
2223 return tracker_db_cursor_sqlite_new (stmt->stmt, stmt, NULL, 0, NULL, 0, FALSE);
2224 }
2225
2226 TrackerDBCursor *
2227 tracker_db_statement_start_sparql_cursor (TrackerDBStatement *stmt,
2228 TrackerPropertyType *types,
2229 gint n_types,
2230 const gchar **variable_names,
2231 gint n_variable_names,
2232 gboolean threadsafe,
2233 GError **error)
2234 {
2235 g_return_val_if_fail (!stmt->stmt_is_sunk, NULL);
2236
2237 return tracker_db_cursor_sqlite_new (stmt->stmt, stmt, types, n_types, variable_names, n_variable_names, threadsafe);
2238 }
2239
2240 static void
2241 tracker_db_statement_init (TrackerDBStatement *stmt)
2242 {
2243 }
2244
2245 static void
2246 tracker_db_cursor_init (TrackerDBCursor *cursor)
2247 {
2248 }
2249
2250 static void
2251 tracker_db_statement_sqlite_reset (TrackerDBStatement *stmt)
2252 {
2253 g_assert (!stmt->stmt_is_sunk);
2254
2255 sqlite3_reset (stmt->stmt);
2256 sqlite3_clear_bindings (stmt->stmt);
2257 }