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 <string.h>
23 #include <stdlib.h>
24 #include <regex.h>
25 #include <zlib.h>
26 #include <locale.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <errno.h>
33
34 #include <glib/gstdio.h>
35
36 #include <libtracker-common/tracker-common.h>
37 #include <libtracker-common/tracker-parser.h>
38
39 #include "tracker-db-manager.h"
40 #include "tracker-db-interface-sqlite.h"
41 #include "tracker-db-interface.h"
42 #include "tracker-data-manager.h"
43 #include "tracker-uuid.h"
44
45 #define UNKNOWN_STATUS 0.5
46
47 /* ZLib buffer settings */
48 #define ZLIB_BUF_SIZE 8192
49
50 #define MAX_INTERFACES_PER_CPU 16
51 #define MAX_INTERFACES (MAX_INTERFACES_PER_CPU * g_get_num_processors ())
52
53 /* Required minimum space needed to create databases (5Mb) */
54 #define TRACKER_DB_MIN_REQUIRED_SPACE 5242880
55
56 /* Set current database version we are working with */
57 #define TRACKER_DB_VERSION_NOW TRACKER_DB_VERSION_2_3
58
59 #define TRACKER_VACUUM_CHECK_SIZE ((goffset) 4 * 1024 * 1024 * 1024) /* 4GB */
60
61 #define IN_USE_FILENAME ".meta.isrunning"
62
63 #define TOSTRING1(x) #x
64 #define TOSTRING(x) TOSTRING1(x)
65 #define TRACKER_PARSER_VERSION_STRING TOSTRING(TRACKER_PARSER_VERSION)
66
67 #define FTS_FLAGS (TRACKER_DB_MANAGER_FTS_ENABLE_STEMMER | \
68 TRACKER_DB_MANAGER_FTS_ENABLE_UNACCENT | \
69 TRACKER_DB_MANAGER_FTS_ENABLE_STOP_WORDS | \
70 TRACKER_DB_MANAGER_FTS_IGNORE_NUMBERS)
71
72 typedef enum {
73 TRACKER_DB_VERSION_UNKNOWN, /* Unknown */
74 TRACKER_DB_VERSION_0_6_6, /* before indexer-split */
75 TRACKER_DB_VERSION_0_6_90, /* after indexer-split */
76 TRACKER_DB_VERSION_0_6_91, /* stable release */
77 TRACKER_DB_VERSION_0_6_92, /* current TRUNK */
78 TRACKER_DB_VERSION_0_7_0, /* vstore branch */
79 TRACKER_DB_VERSION_0_7_4, /* nothing special */
80 TRACKER_DB_VERSION_0_7_12, /* nmo ontology */
81 TRACKER_DB_VERSION_0_7_13, /* coalesce & writeback */
82 TRACKER_DB_VERSION_0_7_17, /* mlo ontology */
83 TRACKER_DB_VERSION_0_7_20, /* nco im ontology */
84 TRACKER_DB_VERSION_0_7_21, /* named graphs/localtime */
85 TRACKER_DB_VERSION_0_7_22, /* fts-limits branch */
86 TRACKER_DB_VERSION_0_7_28, /* RC1 + mto + nco:url */
87 TRACKER_DB_VERSION_0_8_0, /* stable release */
88 TRACKER_DB_VERSION_0_9_0, /* unstable release */
89 TRACKER_DB_VERSION_0_9_8, /* affiliation cardinality + volumes */
90 TRACKER_DB_VERSION_0_9_15, /* mtp:hidden */
91 TRACKER_DB_VERSION_0_9_16, /* Fix for NB#184823 */
92 TRACKER_DB_VERSION_0_9_19, /* collation */
93 TRACKER_DB_VERSION_0_9_21, /* Fix for NB#186055 */
94 TRACKER_DB_VERSION_0_9_24, /* nmo:PhoneMessage class */
95 TRACKER_DB_VERSION_0_9_34, /* ontology cache */
96 TRACKER_DB_VERSION_0_9_38, /* nie:url an inverse functional property */
97 TRACKER_DB_VERSION_0_15_2, /* fts4 */
98 TRACKER_DB_VERSION_2_3 /* sparql1.1 */
99 } TrackerDBVersion;
100
101 typedef struct {
102 TrackerDBInterface *iface;
103 const gchar *file;
104 const gchar *name;
105 gchar *abs_filename;
106 gint cache_size;
107 gint page_size;
108 } TrackerDBDefinition;
109
110 static TrackerDBDefinition db_base = {
111 NULL,
112 "meta.db",
113 "meta",
114 NULL,
115 TRACKER_DB_CACHE_SIZE_DEFAULT,
116 8192,
117 };
118
119 struct _TrackerDBManager {
120 GObject parent_instance;
121 TrackerDBDefinition db;
122 gboolean locations_initialized;
123 gchar *data_dir;
124 gchar *user_data_dir;
125 gchar *in_use_filename;
126 GFile *cache_location;
127 gchar *shared_cache_key;
128 TrackerDBManagerFlags flags;
129 guint s_cache_size;
130 guint u_cache_size;
131
132 gpointer vtab_data;
133
134 GWeakRef iface_data;
135
136 GAsyncQueue *interfaces;
137 };
138
139 enum {
140 SETUP_INTERFACE,
141 UPDATE_INTERFACE,
142 N_SIGNALS
143 };
144
145 static guint signals[N_SIGNALS] = { 0 };
146
147 G_DEFINE_TYPE (TrackerDBManager, tracker_db_manager, G_TYPE_OBJECT)
148
149 static TrackerDBInterface *tracker_db_manager_create_db_interface (TrackerDBManager *db_manager,
150 gboolean readonly,
151 GError **error);
152
153 static TrackerDBInterface * init_writable_db_interface (TrackerDBManager *db_manager);
154
155 TrackerDBManagerFlags
tracker_db_manager_get_flags(TrackerDBManager * db_manager,guint * select_cache_size,guint * update_cache_size)156 tracker_db_manager_get_flags (TrackerDBManager *db_manager,
157 guint *select_cache_size,
158 guint *update_cache_size)
159 {
160 if (select_cache_size)
161 *select_cache_size = db_manager->s_cache_size;
162
163 if (update_cache_size)
164 *update_cache_size = db_manager->u_cache_size;
165
166 return db_manager->flags;
167 }
168
169 static void
iface_set_params(TrackerDBInterface * iface,gboolean readonly,GError ** error)170 iface_set_params (TrackerDBInterface *iface,
171 gboolean readonly,
172 GError **error)
173 {
174 tracker_db_interface_execute_query (iface, NULL, "PRAGMA encoding = \"UTF-8\"");
175
176 if (readonly) {
177 tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = MEMORY;");
178 } else {
179 tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = FILE;");
180 }
181 }
182
183 static void
db_set_params(TrackerDBInterface * iface,const gchar * database,gint cache_size,gint page_size,gboolean enable_wal,GError ** error)184 db_set_params (TrackerDBInterface *iface,
185 const gchar *database,
186 gint cache_size,
187 gint page_size,
188 gboolean enable_wal,
189 GError **error)
190 {
191 GError *internal_error = NULL;
192 TrackerDBStatement *stmt;
193
194 TRACKER_NOTE (SQLITE, g_message (" Setting page size to %d", page_size));
195 tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".page_size = %d", database, page_size);
196
197 tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".synchronous = NORMAL", database);
198 tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".auto_vacuum = 0", database);
199
200 if (enable_wal) {
201 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
202 &internal_error,
203 "PRAGMA \"%s\".journal_mode = WAL", database);
204
205 if (internal_error) {
206 g_debug ("Can't set journal mode to WAL: '%s'",
207 internal_error->message);
208 g_propagate_error (error, internal_error);
209 } else {
210 TrackerDBCursor *cursor;
211
212 cursor = tracker_db_statement_start_cursor (stmt, NULL);
213 if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
214 if (g_ascii_strcasecmp (tracker_db_cursor_get_string (cursor, 0, NULL), "WAL") != 0) {
215 g_set_error (error,
216 TRACKER_DB_INTERFACE_ERROR,
217 TRACKER_DB_OPEN_ERROR,
218 "Can't set journal mode to WAL");
219 }
220 }
221 g_object_unref (cursor);
222 }
223
224 g_clear_object (&stmt);
225 }
226
227 tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".journal_size_limit = 10240000", database);
228
229 tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".cache_size = %d", database, cache_size);
230 TRACKER_NOTE (SQLITE, g_message (" Setting cache size to %d", cache_size));
231 }
232
233 static gboolean
tracker_db_manager_get_metadata(TrackerDBManager * db_manager,const gchar * key,GValue * value)234 tracker_db_manager_get_metadata (TrackerDBManager *db_manager,
235 const gchar *key,
236 GValue *value)
237 {
238 TrackerDBInterface *iface;
239 TrackerDBStatement *stmt;
240 TrackerDBCursor *cursor;
241
242 iface = tracker_db_manager_get_writable_db_interface (db_manager);
243 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
244 NULL,
245 "SELECT value FROM metadata WHERE key = ?");
246 if (!stmt)
247 return FALSE;
248
249 tracker_db_statement_bind_text (stmt, 0, key);
250 cursor = tracker_db_statement_start_cursor (stmt, NULL);
251 g_object_unref (stmt);
252
253 if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
254 g_clear_object (&cursor);
255 return FALSE;
256 }
257
258 tracker_db_cursor_get_value (cursor, 0, value);
259 g_object_unref (cursor);
260
261 return G_VALUE_TYPE (value) != G_TYPE_INVALID;
262 }
263
264 static void
tracker_db_manager_set_metadata(TrackerDBManager * db_manager,const gchar * key,GValue * value)265 tracker_db_manager_set_metadata (TrackerDBManager *db_manager,
266 const gchar *key,
267 GValue *value)
268 {
269 TrackerDBInterface *iface;
270 TrackerDBStatement *stmt;
271 GError *error = NULL;
272
273 iface = tracker_db_manager_get_writable_db_interface (db_manager);
274 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
275 &error,
276 "INSERT OR REPLACE INTO metadata VALUES (?, ?)");
277 if (stmt) {
278 tracker_db_statement_bind_text (stmt, 0, key);
279 tracker_db_statement_bind_value (stmt, 1, value);
280 tracker_db_statement_execute (stmt, &error);
281
282 g_object_unref (stmt);
283 }
284
285 if (error) {
286 g_critical ("Could not store database metadata: %s\n", error->message);
287 g_error_free (error);
288 }
289 }
290
291 static TrackerDBVersion
db_get_version(TrackerDBManager * db_manager)292 db_get_version (TrackerDBManager *db_manager)
293 {
294 TrackerDBInterface *iface;
295 TrackerDBStatement *stmt;
296 TrackerDBCursor *cursor;
297 TrackerDBVersion version;
298
299 iface = tracker_db_manager_get_writable_db_interface (db_manager);
300 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
301 NULL, "PRAGMA user_version");
302 if (!stmt)
303 return TRACKER_DB_VERSION_UNKNOWN;
304
305 cursor = tracker_db_statement_start_cursor (stmt, NULL);
306 g_object_unref (stmt);
307
308 if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
309 g_clear_object (&cursor);
310 return TRACKER_DB_VERSION_UNKNOWN;
311 }
312
313 version = tracker_db_cursor_get_int (cursor, 0);
314 g_object_unref (cursor);
315
316 return version;
317 }
318
319 static void
tracker_db_manager_update_version(TrackerDBManager * db_manager)320 tracker_db_manager_update_version (TrackerDBManager *db_manager)
321 {
322 TrackerDBInterface *iface;
323 TrackerDBStatement *stmt;
324 GError *error = NULL;
325
326 iface = tracker_db_manager_get_writable_db_interface (db_manager);
327 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
328 &error, "PRAGMA user_version = %d",
329 TRACKER_DB_VERSION_NOW);
330 if (stmt) {
331 tracker_db_statement_execute (stmt, &error);
332 g_object_unref (stmt);
333 }
334
335 if (error) {
336 g_critical ("Could not set database version: %s\n", error->message);
337 g_error_free (error);
338 }
339 }
340
341 static gchar *
db_get_locale(TrackerDBManager * db_manager)342 db_get_locale (TrackerDBManager *db_manager)
343 {
344 GValue value = G_VALUE_INIT;
345 gchar *locale = NULL;
346
347 if (!tracker_db_manager_get_metadata (db_manager, "locale", &value))
348 return NULL;
349
350 locale = g_value_dup_string (&value);
351 g_value_unset (&value);
352
353 return locale;
354 }
355
356 static void
db_set_locale(TrackerDBManager * db_manager,const gchar * locale)357 db_set_locale (TrackerDBManager *db_manager,
358 const gchar *locale)
359 {
360 GValue value = G_VALUE_INIT;
361
362 g_value_init (&value, G_TYPE_STRING);
363 g_value_set_string (&value, locale);
364
365 tracker_db_manager_set_metadata (db_manager, "locale", &value);
366 g_value_unset (&value);
367 }
368
369 gboolean
tracker_db_manager_locale_changed(TrackerDBManager * db_manager,GError ** error)370 tracker_db_manager_locale_changed (TrackerDBManager *db_manager,
371 GError **error)
372 {
373 gchar *db_locale;
374 gchar *current_locale;
375 gboolean changed;
376
377 /* Get current collation locale */
378 current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
379
380 /* Get db locale */
381 db_locale = db_get_locale (db_manager);
382
383 /* If they are different, recreate indexes. Note that having
384 * both to NULL is actually valid, they would default to
385 * the unicode collation without locale-specific stuff. */
386 if (g_strcmp0 (db_locale, current_locale) != 0) {
387 g_set_error (error,
388 TRACKER_DB_INTERFACE_ERROR,
389 TRACKER_DB_OPEN_ERROR,
390 "Locale change detected (DB:%s, User/App:%s)",
391 db_locale ? db_locale : "unknown",
392 current_locale);
393 changed = TRUE;
394 } else {
395 g_debug ("Current and DB locales match: '%s'", db_locale);
396 changed = FALSE;
397 }
398
399 g_free (db_locale);
400 g_free (current_locale);
401
402 return changed;
403 }
404
405 void
tracker_db_manager_set_current_locale(TrackerDBManager * db_manager)406 tracker_db_manager_set_current_locale (TrackerDBManager *db_manager)
407 {
408 gchar *current_locale;
409
410 /* Get current collation locale */
411 current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
412 g_debug ("Saving DB locale as: '%s'", current_locale);
413 db_set_locale (db_manager, current_locale);
414 g_free (current_locale);
415 }
416
417 static void
tracker_db_manager_ensure_location(TrackerDBManager * db_manager,GFile * cache_location)418 tracker_db_manager_ensure_location (TrackerDBManager *db_manager,
419 GFile *cache_location)
420 {
421 gchar *dir;
422
423 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0) {
424 return;
425 }
426
427 if (db_manager->locations_initialized) {
428 return;
429 }
430
431 db_manager->locations_initialized = TRUE;
432 db_manager->data_dir = g_file_get_path (cache_location);
433
434 db_manager->db = db_base;
435
436 dir = g_file_get_path (cache_location);
437 db_manager->db.abs_filename = g_build_filename (dir, db_manager->db.file, NULL);
438 g_free (dir);
439 }
440
441 gboolean
tracker_db_manager_db_exists(GFile * cache_location)442 tracker_db_manager_db_exists (GFile *cache_location)
443 {
444 gchar *dir;
445 gchar *filename;
446 gboolean db_exists = FALSE;
447
448 dir = g_file_get_path (cache_location);
449 filename = g_build_filename (dir, db_base.file, NULL);
450
451 db_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
452
453 g_free (dir);
454 g_free (filename);
455
456 return db_exists;
457 }
458
459 static gboolean
db_check_integrity(TrackerDBManager * db_manager)460 db_check_integrity (TrackerDBManager *db_manager)
461 {
462 GError *internal_error = NULL;
463 TrackerDBStatement *stmt;
464 TrackerDBCursor *cursor = NULL;
465
466 stmt = tracker_db_interface_create_statement (db_manager->db.iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
467 &internal_error,
468 "PRAGMA integrity_check(1)");
469
470 if (!stmt) {
471 if (internal_error) {
472 g_message ("Corrupt database: failed to create integrity_check statement: %s", internal_error->message);
473 }
474
475 g_clear_error (&internal_error);
476 return FALSE;
477 }
478
479 cursor = tracker_db_statement_start_cursor (stmt, NULL);
480 g_object_unref (stmt);
481
482 if (cursor) {
483 if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
484 const gchar *check_result;
485
486 check_result = tracker_db_cursor_get_string (cursor, 0, NULL);
487 if (g_strcmp0 (check_result, "ok") != 0) {
488 g_message ("Corrupt database: sqlite integrity check returned '%s'", check_result);
489 return FALSE;
490 }
491 }
492 g_object_unref (cursor);
493 }
494
495 /* ensure that database has been initialized by an earlier tracker-store start
496 by checking whether Resource table exists */
497 stmt = tracker_db_interface_create_statement (db_manager->db.iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
498 &internal_error,
499 "SELECT 1 FROM Resource");
500 if (!stmt) {
501 if (internal_error) {
502 g_message ("Corrupt database: failed to create resource check statement: %s", internal_error->message);
503 }
504
505 g_clear_error (&internal_error);
506 return FALSE;
507 }
508
509 g_object_unref (stmt);
510
511 return TRUE;
512 }
513
514 TrackerDBManager *
tracker_db_manager_new(TrackerDBManagerFlags flags,GFile * cache_location,gboolean * first_time,gboolean shared_cache,guint select_cache_size,guint update_cache_size,TrackerBusyCallback busy_callback,gpointer busy_user_data,GObject * iface_data,gpointer vtab_data,GError ** error)515 tracker_db_manager_new (TrackerDBManagerFlags flags,
516 GFile *cache_location,
517 gboolean *first_time,
518 gboolean shared_cache,
519 guint select_cache_size,
520 guint update_cache_size,
521 TrackerBusyCallback busy_callback,
522 gpointer busy_user_data,
523 GObject *iface_data,
524 gpointer vtab_data,
525 GError **error)
526 {
527 TrackerDBManager *db_manager;
528 TrackerDBVersion version;
529 int in_use_file;
530 TrackerDBInterface *resources_iface;
531 GError *internal_error = NULL;
532 gboolean need_to_create = FALSE;
533
534 db_manager = g_object_new (TRACKER_TYPE_DB_MANAGER, NULL);
535 db_manager->vtab_data = vtab_data;
536
537 /* First set defaults for return values */
538 if (first_time) {
539 *first_time = FALSE;
540 }
541
542 /* Set up locations */
543 db_manager->flags = flags;
544 db_manager->s_cache_size = select_cache_size;
545 db_manager->u_cache_size = update_cache_size;
546 db_manager->interfaces = g_async_queue_new_full (g_object_unref);
547
548 g_set_object (&db_manager->cache_location, cache_location);
549 g_weak_ref_init (&db_manager->iface_data, iface_data);
550
551 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) == 0) {
552 tracker_db_manager_ensure_location (db_manager, cache_location);
553 db_manager->in_use_filename = g_build_filename (db_manager->data_dir,
554 IN_USE_FILENAME,
555 NULL);
556
557 /* Don't do need_reindex checks for readonly (direct-access) */
558 if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
559
560 /* Make sure the directories exist */
561 g_mkdir_with_parents (db_manager->data_dir, 00755);
562 }
563 } else {
564 db_manager->shared_cache_key = tracker_generate_uuid (NULL);
565 }
566
567 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0) {
568 need_to_create = TRUE;
569 } else if (!g_file_test (db_manager->db.abs_filename, G_FILE_TEST_EXISTS)) {
570 if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
571 TRACKER_NOTE (SQLITE, g_message ("Could not find database file:'%s', will create it.", db_manager->db.abs_filename));
572 need_to_create = TRUE;
573 } else {
574 g_set_error (error,
575 TRACKER_DB_INTERFACE_ERROR,
576 TRACKER_DB_OPEN_ERROR,
577 "Could not find database file:'%s'.", db_manager->db.abs_filename);
578
579 g_object_unref (db_manager);
580 return NULL;
581 }
582 } else if ((flags & TRACKER_DB_MANAGER_SKIP_VERSION_CHECK) == 0) {
583 version = db_get_version (db_manager);
584
585 if (version < TRACKER_DB_VERSION_NOW) {
586 g_set_error (error,
587 TRACKER_DB_INTERFACE_ERROR,
588 TRACKER_DB_OPEN_ERROR,
589 "Database version is too old: got version %i, but %i is needed",
590 version, TRACKER_DB_VERSION_NOW);
591
592 g_object_unref (db_manager);
593 return NULL;
594 }
595 }
596
597 db_manager->locations_initialized = TRUE;
598
599 if ((flags & TRACKER_DB_MANAGER_READONLY) != 0) {
600 GValue value = G_VALUE_INIT;
601 TrackerDBManagerFlags fts_flags = 0;
602
603 if (tracker_db_manager_get_metadata (db_manager, "fts-flags", &value)) {
604 fts_flags = g_ascii_strtoll (g_value_get_string (&value), NULL, 10);
605 g_value_unset (&value);
606 }
607
608 /* Readonly connections should go with the FTS flags as stored
609 * in metadata.
610 */
611 db_manager->flags = (db_manager->flags & ~(FTS_FLAGS)) | fts_flags;
612 }
613
614 /* Set general database options */
615 if (shared_cache) {
616 TRACKER_NOTE (SQLITE, g_message ("Enabling database shared cache"));
617 tracker_db_interface_sqlite_enable_shared_cache ();
618 }
619
620 if (need_to_create) {
621 if (first_time) {
622 *first_time = TRUE;
623 }
624
625 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) == 0 &&
626 !tracker_file_system_has_enough_space (db_manager->data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, TRUE)) {
627 g_set_error (error,
628 TRACKER_DB_INTERFACE_ERROR,
629 TRACKER_DB_OPEN_ERROR,
630 "Filesystem does not have enough space");
631 return FALSE;
632 }
633
634 TRACKER_NOTE (SQLITE, g_message ("Creating database files for %s...", db_manager->db.abs_filename));
635
636 db_manager->db.iface = tracker_db_manager_create_db_interface (db_manager, FALSE, &internal_error);
637 if (internal_error) {
638 g_propagate_error (error, internal_error);
639 g_object_unref (db_manager);
640 return FALSE;
641 }
642
643 g_clear_object (&db_manager->db.iface);
644
645 tracker_db_manager_update_version (db_manager);
646 } else {
647 TRACKER_NOTE (SQLITE, g_message ("Loading files for database %s...", db_manager->db.abs_filename));
648
649 if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
650 /* Check that the database was closed cleanly and do a deeper integrity
651 * check if it wasn't, raising an error if we detect corruption. */
652
653 if (g_file_test (db_manager->in_use_filename, G_FILE_TEST_EXISTS)) {
654 gsize size = 0;
655 struct stat st;
656
657 TRACKER_NOTE (SQLITE, g_message ("Didn't shut down cleanly last time, doing integrity checks"));
658
659 if (g_stat (db_manager->db.abs_filename, &st) == 0) {
660 size = st.st_size;
661 }
662
663 /* Size is 1 when using echo > file.db, none of our databases
664 * are only one byte in size even initually. */
665 if (size <= 1) {
666 g_debug ("Database is corrupt: size is 1 byte or less.");
667 return FALSE;
668 }
669
670 db_manager->db.iface = tracker_db_manager_create_db_interface (db_manager, FALSE, &internal_error);
671
672 if (internal_error) {
673 /* If this already doesn't succeed, then surely the file is
674 * corrupt. No need to check for integrity anymore. */
675 g_propagate_error (error, internal_error);
676 g_object_unref (db_manager);
677 return NULL;
678 }
679
680 busy_callback ("Integrity checking", 0, busy_user_data);
681
682 if (db_check_integrity (db_manager) == FALSE) {
683 g_set_error (error,
684 TRACKER_DB_INTERFACE_ERROR,
685 TRACKER_DB_OPEN_ERROR,
686 "Corrupt db file");
687 g_object_unref (db_manager);
688 return NULL;
689 }
690 }
691 }
692 }
693
694 if ((flags & (TRACKER_DB_MANAGER_READONLY | TRACKER_DB_MANAGER_IN_MEMORY)) == 0) {
695 /* do not create in-use file for read-only mode (direct access) */
696 in_use_file = g_open (db_manager->in_use_filename,
697 O_WRONLY | O_APPEND | O_CREAT | O_SYNC,
698 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
699
700 if (in_use_file >= 0) {
701 fsync (in_use_file);
702 close (in_use_file);
703 }
704 }
705
706 resources_iface = tracker_db_manager_create_db_interface (db_manager,
707 TRUE, &internal_error);
708
709 if (internal_error) {
710 g_propagate_error (error, internal_error);
711 g_object_unref (db_manager);
712 return NULL;
713 }
714
715 g_clear_object (&resources_iface);
716
717 return db_manager;
718 }
719
720 void
tracker_db_manager_finalize(GObject * object)721 tracker_db_manager_finalize (GObject *object)
722 {
723 TrackerDBManager *db_manager = TRACKER_DB_MANAGER (object);
724 gboolean readonly = (db_manager->flags & TRACKER_DB_MANAGER_READONLY) != 0;
725
726 g_async_queue_unref (db_manager->interfaces);
727 g_free (db_manager->db.abs_filename);
728
729 if (db_manager->db.iface) {
730 if (!readonly)
731 tracker_db_interface_sqlite_wal_checkpoint (db_manager->db.iface, TRUE, NULL);
732 g_object_unref (db_manager->db.iface);
733 }
734
735 g_weak_ref_clear (&db_manager->iface_data);
736
737 g_free (db_manager->data_dir);
738
739 if (db_manager->in_use_filename && !readonly) {
740 /* do not delete in-use file for read-only mode (direct access) */
741 g_unlink (db_manager->in_use_filename);
742 }
743
744 g_free (db_manager->in_use_filename);
745 g_free (db_manager->shared_cache_key);
746
747 G_OBJECT_CLASS (tracker_db_manager_parent_class)->finalize (object);
748 }
749
750 static TrackerDBInterface *
tracker_db_manager_create_db_interface(TrackerDBManager * db_manager,gboolean readonly,GError ** error)751 tracker_db_manager_create_db_interface (TrackerDBManager *db_manager,
752 gboolean readonly,
753 GError **error)
754 {
755 TrackerDBInterface *connection;
756 GError *internal_error = NULL;
757 TrackerDBInterfaceFlags flags = 0;
758
759 if (readonly)
760 flags |= TRACKER_DB_INTERFACE_READONLY;
761 if (db_manager->flags & TRACKER_DB_MANAGER_ENABLE_MUTEXES)
762 flags |= TRACKER_DB_INTERFACE_USE_MUTEX;
763 if (db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY)
764 flags |= TRACKER_DB_INTERFACE_IN_MEMORY;
765
766 connection = tracker_db_interface_sqlite_new (db_manager->db.abs_filename,
767 db_manager->shared_cache_key,
768 flags,
769 &internal_error);
770 if (internal_error) {
771 g_propagate_error (error, internal_error);
772 return NULL;
773 }
774
775 tracker_db_interface_set_user_data (connection,
776 g_weak_ref_get (&db_manager->iface_data),
777 g_object_unref);
778
779 if (db_manager->vtab_data)
780 tracker_db_interface_init_vtabs (connection, db_manager->vtab_data);
781
782 iface_set_params (connection,
783 readonly,
784 &internal_error);
785 db_set_params (connection, "main",
786 db_manager->db.cache_size,
787 db_manager->db.page_size,
788 !(db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY),
789 &internal_error);
790
791 if (internal_error) {
792 g_propagate_error (error, internal_error);
793 g_object_unref (connection);
794 return NULL;
795 }
796
797 tracker_db_interface_set_max_stmt_cache_size (connection,
798 TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
799 db_manager->s_cache_size);
800
801 if (!readonly) {
802 tracker_db_interface_set_max_stmt_cache_size (connection,
803 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
804 db_manager->u_cache_size);
805 }
806
807 return connection;
808 }
809
810 /**
811 * tracker_db_manager_get_db_interface:
812 *
813 * Request a database connection to the database
814 *
815 * The caller must NOT g_object_unref the result
816 *
817 * returns: (callee-owns): a database connection
818 **/
819 TrackerDBInterface *
tracker_db_manager_get_db_interface(TrackerDBManager * db_manager)820 tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
821 {
822 GError *internal_error = NULL;
823 TrackerDBInterface *interface = NULL;
824 guint len, i;
825
826 /* The interfaces never actually leave the async queue,
827 * we use it as a thread synchronized LRU, which doesn't
828 * mean the interface found has no other active cursors,
829 * in which case we either optionally create a new
830 * TrackerDBInterface, or resign to sharing the obtained
831 * one with other threads (thus getting increased contention
832 * in the interface lock).
833 */
834 g_async_queue_lock (db_manager->interfaces);
835 len = g_async_queue_length_unlocked (db_manager->interfaces);
836
837 /* 1st. Find a free interface */
838 for (i = 0; i < len; i++) {
839 interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
840
841 if (!interface)
842 break;
843 if (!tracker_db_interface_get_is_used (interface))
844 break;
845
846 g_async_queue_push_unlocked (db_manager->interfaces, interface);
847 interface = NULL;
848 }
849
850 /* 2nd. If no more interfaces can be created, pick one */
851 if (!interface && len >= MAX_INTERFACES) {
852 interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
853 }
854
855 if (interface) {
856 g_signal_emit (db_manager, signals[UPDATE_INTERFACE], 0, interface);
857 } else {
858 /* 3rd. Create a new interface to satisfy the request */
859 interface = tracker_db_manager_create_db_interface (db_manager,
860 TRUE, &internal_error);
861
862 if (interface) {
863 g_signal_emit (db_manager, signals[SETUP_INTERFACE], 0, interface);
864 } else {
865 if (g_async_queue_length_unlocked (db_manager->interfaces) == 0) {
866 g_critical ("Error opening database: %s", internal_error->message);
867 g_error_free (internal_error);
868 g_async_queue_unlock (db_manager->interfaces);
869 return NULL;
870 } else {
871 g_error_free (internal_error);
872 /* Fetch the first interface back. Oh well */
873 interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
874 }
875 }
876 }
877
878 tracker_db_interface_ref_use (interface);
879
880 g_async_queue_push_unlocked (db_manager->interfaces, interface);
881 g_async_queue_unlock (db_manager->interfaces);
882
883 return interface;
884 }
885
886 static void
tracker_db_manager_init(TrackerDBManager * manager)887 tracker_db_manager_init (TrackerDBManager *manager)
888 {
889 }
890
891 static void
tracker_db_manager_class_init(TrackerDBManagerClass * klass)892 tracker_db_manager_class_init (TrackerDBManagerClass *klass)
893 {
894 GObjectClass *object_class = G_OBJECT_CLASS (klass);
895
896 object_class->finalize = tracker_db_manager_finalize;
897
898 signals[SETUP_INTERFACE] =
899 g_signal_new ("setup-interface",
900 G_TYPE_FROM_CLASS (klass),
901 G_SIGNAL_RUN_LAST, 0,
902 NULL, NULL,
903 g_cclosure_marshal_VOID__OBJECT,
904 G_TYPE_NONE,
905 1, TRACKER_TYPE_DB_INTERFACE);
906 signals[UPDATE_INTERFACE] =
907 g_signal_new ("update-interface",
908 G_TYPE_FROM_CLASS (klass),
909 G_SIGNAL_RUN_LAST, 0,
910 NULL, NULL,
911 g_cclosure_marshal_VOID__OBJECT,
912 G_TYPE_NONE,
913 1, TRACKER_TYPE_DB_INTERFACE);
914 }
915
916 static TrackerDBInterface *
init_writable_db_interface(TrackerDBManager * db_manager)917 init_writable_db_interface (TrackerDBManager *db_manager)
918 {
919 TrackerDBInterface *iface;
920 GError *error = NULL;
921 gboolean readonly;
922
923 /* Honor anyway the DBManager readonly flag */
924 readonly = (db_manager->flags & TRACKER_DB_MANAGER_READONLY) != 0;
925 iface = tracker_db_manager_create_db_interface (db_manager, readonly, &error);
926 if (error) {
927 g_critical ("Error opening readwrite database: %s", error->message);
928 g_error_free (error);
929 }
930
931 return iface;
932 }
933
934 TrackerDBInterface *
tracker_db_manager_get_writable_db_interface(TrackerDBManager * db_manager)935 tracker_db_manager_get_writable_db_interface (TrackerDBManager *db_manager)
936 {
937 if (db_manager->db.iface == NULL)
938 db_manager->db.iface = init_writable_db_interface (db_manager);
939
940 return db_manager->db.iface;
941 }
942
943 /**
944 * tracker_db_manager_has_enough_space:
945 *
946 * Checks whether the file system, where the database files are stored,
947 * has enough free space to allow modifications.
948 *
949 * returns: TRUE if there is enough space, FALSE otherwise
950 **/
951 gboolean
tracker_db_manager_has_enough_space(TrackerDBManager * db_manager)952 tracker_db_manager_has_enough_space (TrackerDBManager *db_manager)
953 {
954 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0)
955 return TRUE;
956 return tracker_file_system_has_enough_space (db_manager->data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, FALSE);
957 }
958
959 gboolean
tracker_db_manager_get_tokenizer_changed(TrackerDBManager * db_manager)960 tracker_db_manager_get_tokenizer_changed (TrackerDBManager *db_manager)
961 {
962 GValue value = G_VALUE_INIT;
963 const gchar *version;
964 TrackerDBManagerFlags flags;
965 gboolean changed;
966
967 if (!tracker_db_manager_get_metadata (db_manager, "fts-flags", &value))
968 return TRUE;
969
970 flags = g_ascii_strtoll (g_value_get_string (&value), NULL, 10);
971 g_value_unset (&value);
972
973 if ((db_manager->flags & TRACKER_DB_MANAGER_READONLY) == 0 &&
974 flags != (db_manager->flags & FTS_FLAGS)) {
975 return TRUE;
976 }
977
978 if (!tracker_db_manager_get_metadata (db_manager, "parser-version", &value))
979 return TRUE;
980
981 version = g_value_get_string (&value);
982 changed = strcmp (version, TRACKER_PARSER_VERSION_STRING) != 0;
983 g_value_unset (&value);
984
985 return changed;
986 }
987
988 void
tracker_db_manager_tokenizer_update(TrackerDBManager * db_manager)989 tracker_db_manager_tokenizer_update (TrackerDBManager *db_manager)
990 {
991 GValue value = G_VALUE_INIT;
992
993 g_value_init (&value, G_TYPE_STRING);
994 g_value_set_string (&value, TRACKER_PARSER_VERSION_STRING);
995 tracker_db_manager_set_metadata (db_manager, "parser-version", &value);
996 g_value_unset (&value);
997
998 g_value_init (&value, G_TYPE_INT64);
999 g_value_set_int64 (&value, (db_manager->flags & FTS_FLAGS));
1000 tracker_db_manager_set_metadata (db_manager, "fts-flags", &value);
1001 g_value_unset (&value);
1002 }
1003
1004 void
tracker_db_manager_check_perform_vacuum(TrackerDBManager * db_manager)1005 tracker_db_manager_check_perform_vacuum (TrackerDBManager *db_manager)
1006 {
1007 TrackerDBInterface *iface;
1008
1009 if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0)
1010 return;
1011 if (tracker_file_get_size (db_manager->db.abs_filename) < TRACKER_VACUUM_CHECK_SIZE)
1012 return;
1013
1014 iface = tracker_db_manager_get_writable_db_interface (db_manager);
1015 tracker_db_interface_execute_query (iface, NULL, "VACUUM");
1016 }
1017
1018 static gboolean
ensure_create_database_file(TrackerDBManager * db_manager,GFile * file,GError ** error)1019 ensure_create_database_file (TrackerDBManager *db_manager,
1020 GFile *file,
1021 GError **error)
1022 {
1023 TrackerDBInterface *iface;
1024 GError *file_error = NULL;
1025 gchar *path;
1026
1027 /* Ensure the database is created from scratch */
1028 if (!g_file_delete (file, NULL, &file_error)) {
1029 if (!g_error_matches (file_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1030 g_propagate_error (error, file_error);
1031 return FALSE;
1032 }
1033
1034 g_clear_error (&file_error);
1035 }
1036
1037 /* Create the database file in a separate interface, so we can
1038 * configure page_size and journal_mode outside a transaction, so
1039 * they apply throughout the whole lifetime.
1040 */
1041 path = g_file_get_path (file);
1042 iface = tracker_db_interface_sqlite_new (path,
1043 db_manager->shared_cache_key,
1044 0, error);
1045 g_free (path);
1046
1047 if (!iface)
1048 return FALSE;
1049
1050 tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d",
1051 db_manager->db.page_size);
1052 tracker_db_interface_execute_query (iface, NULL, "PRAGMA journal_mode = WAL");
1053
1054 g_object_unref (iface);
1055 return TRUE;
1056 }
1057
1058 gboolean
tracker_db_manager_attach_database(TrackerDBManager * db_manager,TrackerDBInterface * iface,const gchar * name,gboolean create,GError ** error)1059 tracker_db_manager_attach_database (TrackerDBManager *db_manager,
1060 TrackerDBInterface *iface,
1061 const gchar *name,
1062 gboolean create,
1063 GError **error)
1064 {
1065 gchar *filename, *escaped;
1066 GFile *file = NULL;
1067
1068 if (db_manager->cache_location) {
1069 filename = g_strdup_printf ("%s.db", name);
1070 escaped = g_uri_escape_string (filename, NULL, FALSE);
1071 file = g_file_get_child (db_manager->cache_location, escaped);
1072 g_free (filename);
1073 g_free (escaped);
1074
1075 if (create) {
1076 if (!ensure_create_database_file (db_manager, file, error)) {
1077 g_object_unref (file);
1078 return FALSE;
1079 }
1080 }
1081 }
1082
1083 if (!tracker_db_interface_attach_database (iface, file, name, error)) {
1084 g_clear_object (&file);
1085 return FALSE;
1086 }
1087
1088 g_clear_object (&file);
1089 db_set_params (iface, name,
1090 db_manager->db.cache_size,
1091 db_manager->db.page_size,
1092 !(db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY),
1093 error);
1094 return TRUE;
1095 }
1096
1097 gboolean
tracker_db_manager_detach_database(TrackerDBManager * db_manager,TrackerDBInterface * iface,const gchar * name,GError ** error)1098 tracker_db_manager_detach_database (TrackerDBManager *db_manager,
1099 TrackerDBInterface *iface,
1100 const gchar *name,
1101 GError **error)
1102 {
1103 return tracker_db_interface_detach_database (iface, name, error);
1104 }
1105
1106 void
tracker_db_manager_release_memory(TrackerDBManager * db_manager)1107 tracker_db_manager_release_memory (TrackerDBManager *db_manager)
1108 {
1109 TrackerDBInterface *iface;
1110 gint i, len;
1111
1112 g_async_queue_lock (db_manager->interfaces);
1113 len = g_async_queue_length_unlocked (db_manager->interfaces);
1114
1115 for (i = 0; i < len; i++) {
1116 iface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
1117 if (!iface)
1118 break;
1119
1120 if (tracker_db_interface_get_is_used (iface))
1121 g_async_queue_push_unlocked (db_manager->interfaces, iface);
1122 else
1123 g_object_unref (iface);
1124 }
1125
1126 if (g_async_queue_length_unlocked (db_manager->interfaces) < len) {
1127 g_debug ("Freed %d readonly interfaces",
1128 len - g_async_queue_length_unlocked (db_manager->interfaces));
1129 }
1130
1131 if (db_manager->db.iface) {
1132 gssize bytes;
1133
1134 bytes = tracker_db_interface_sqlite_release_memory (db_manager->db.iface);
1135
1136 if (bytes > 0) {
1137 g_debug ("Freed %" G_GSSIZE_MODIFIER "d bytes from writable interface",
1138 bytes);
1139 }
1140 }
1141
1142 g_async_queue_unlock (db_manager->interfaces);
1143 }
1144