1 /*
2 * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
3 * Copyright (C) 2007, Jason Kivlighn <jkivlighn@gmail.com>
4 * Copyright (C) 2007, Creative Commons <http://creativecommons.org>
5 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24
25 #include <string.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <zlib.h>
29 #include <inttypes.h>
30
31 #include <glib/gstdio.h>
32
33 #include <libtracker-fts/tracker-fts.h>
34
35 #include <libtracker-common/tracker-debug.h>
36 #include <libtracker-common/tracker-locale.h>
37
38 #include "tracker-class.h"
39 #include "tracker-data-manager.h"
40 #include "tracker-data-update.h"
41 #include "tracker-db-interface-sqlite.h"
42 #include "tracker-db-manager.h"
43 #include "tracker-namespace.h"
44 #include "tracker-ontologies.h"
45 #include "tracker-ontology.h"
46 #include "tracker-property.h"
47 #include "tracker-data-query.h"
48 #include "tracker-sparql-parser.h"
49 #include "tracker-turtle-reader.h"
50
51 #define RDF_PROPERTY TRACKER_PREFIX_RDF "Property"
52 #define RDF_TYPE TRACKER_PREFIX_RDF "type"
53
54 #define RDFS_CLASS TRACKER_PREFIX_RDFS "Class"
55 #define RDFS_DOMAIN TRACKER_PREFIX_RDFS "domain"
56 #define RDFS_RANGE TRACKER_PREFIX_RDFS "range"
57 #define RDFS_SUB_CLASS_OF TRACKER_PREFIX_RDFS "subClassOf"
58 #define RDFS_SUB_PROPERTY_OF TRACKER_PREFIX_RDFS "subPropertyOf"
59
60 #define NRL_INVERSE_FUNCTIONAL_PROPERTY TRACKER_PREFIX_NRL "InverseFunctionalProperty"
61 #define NRL_MAX_CARDINALITY TRACKER_PREFIX_NRL "maxCardinality"
62
63 #define NRL_LAST_MODIFIED TRACKER_PREFIX_NRL "lastModified"
64
65 #define ZLIBBUFSIZ 8192
66
67 struct _TrackerDataManager {
68 GObject parent_instance;
69
70 GFile *ontology_location;
71 GFile *cache_location;
72 guint initialized : 1;
73 guint flags;
74
75 gint select_cache_size;
76 gint update_cache_size;
77 guint generation;
78
79 TrackerDBManager *db_manager;
80 TrackerOntologies *ontologies;
81 TrackerData *data_update;
82
83 GHashTable *transaction_graphs;
84 GHashTable *graphs;
85
86 /* Cached remote connections */
87 GMutex connections_lock;
88 GHashTable *cached_connections;
89
90 gchar *status;
91 };
92
93 struct _TrackerDataManagerClass {
94 GObjectClass parent_instance;
95 };
96
97 typedef struct {
98 const gchar *from;
99 const gchar *to;
100 } Conversion;
101
102 static Conversion allowed_boolean_conversions[] = {
103 { "false", "true" },
104 { "true", "false" },
105 { NULL, NULL }
106 };
107
108 static Conversion allowed_cardinality_conversions[] = {
109 { "1", NULL },
110 { NULL, NULL }
111 };
112
113 static Conversion allowed_range_conversions[] = {
114 { TRACKER_PREFIX_XSD "integer", TRACKER_PREFIX_XSD "string" },
115 { TRACKER_PREFIX_XSD "integer", TRACKER_PREFIX_XSD "double" },
116 { TRACKER_PREFIX_XSD "integer", TRACKER_PREFIX_XSD "boolean" },
117
118 { TRACKER_PREFIX_XSD "string", TRACKER_PREFIX_XSD "integer" },
119 { TRACKER_PREFIX_XSD "string", TRACKER_PREFIX_XSD "double" },
120 { TRACKER_PREFIX_XSD "string", TRACKER_PREFIX_XSD "boolean" },
121
122 { TRACKER_PREFIX_XSD "double", TRACKER_PREFIX_XSD "integer" },
123 { TRACKER_PREFIX_XSD "double", TRACKER_PREFIX_XSD "string" },
124 { TRACKER_PREFIX_XSD "double", TRACKER_PREFIX_XSD "boolean" },
125
126 { NULL, NULL }
127 };
128
129 enum {
130 PROP_0,
131 PROP_STATUS,
132 N_PROPS
133 };
134
135 static gboolean tracker_data_manager_fts_changed (TrackerDataManager *manager);
136 static gboolean tracker_data_manager_update_fts (TrackerDataManager *manager,
137 TrackerDBInterface *iface,
138 const gchar *database,
139 GError **error);
140
141 static void tracker_data_manager_initable_iface_init (GInitableIface *iface);
142
G_DEFINE_TYPE_WITH_CODE(TrackerDataManager,tracker_data_manager,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,tracker_data_manager_initable_iface_init))143 G_DEFINE_TYPE_WITH_CODE (TrackerDataManager, tracker_data_manager, G_TYPE_OBJECT,
144 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, tracker_data_manager_initable_iface_init))
145
146 static void
147 tracker_data_manager_init (TrackerDataManager *manager)
148 {
149 manager->generation = 1;
150 g_mutex_init (&manager->connections_lock);
151 }
152
153 GQuark
tracker_data_ontology_error_quark(void)154 tracker_data_ontology_error_quark (void)
155 {
156 return g_quark_from_static_string ("tracker-data-ontology-error-quark");
157 }
158
159 static GHashTable *
tracker_data_manager_ensure_graphs(TrackerDataManager * manager,TrackerDBInterface * iface,GError ** error)160 tracker_data_manager_ensure_graphs (TrackerDataManager *manager,
161 TrackerDBInterface *iface,
162 GError **error)
163 {
164 TrackerDBCursor *cursor = NULL;
165 TrackerDBStatement *stmt;
166 GHashTable *graphs;
167
168 if (manager->graphs)
169 return manager->graphs;
170
171 graphs = g_hash_table_new_full (g_str_hash,
172 g_str_equal,
173 g_free,
174 NULL);
175
176 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, error,
177 "SELECT ID, Uri FROM Resource WHERE ID IN (SELECT ID FROM Graph)");
178 if (!stmt) {
179 g_hash_table_unref (graphs);
180 return NULL;
181 }
182
183 cursor = tracker_db_statement_start_cursor (stmt, error);
184 g_object_unref (stmt);
185
186 if (!cursor) {
187 g_hash_table_unref (graphs);
188 return NULL;
189 }
190
191 while (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
192 const gchar *name;
193 gint id;
194
195 id = tracker_db_cursor_get_int (cursor, 0);
196 name = tracker_db_cursor_get_string (cursor, 1, NULL);
197
198 g_hash_table_insert (graphs, g_strdup (name),
199 GINT_TO_POINTER (id));
200 }
201
202 g_object_unref (cursor);
203 manager->graphs = graphs;
204 return graphs;
205 }
206
207 GHashTable *
tracker_data_manager_get_graphs(TrackerDataManager * manager,gboolean in_transaction)208 tracker_data_manager_get_graphs (TrackerDataManager *manager,
209 gboolean in_transaction)
210 {
211 if (manager->transaction_graphs && in_transaction)
212 return manager->transaction_graphs;
213
214 return manager->graphs;
215 }
216
217 static void
handle_unsupported_ontology_change(TrackerDataManager * manager,const gchar * ontology_path,const gchar * subject,const gchar * change,const gchar * old,const gchar * attempted_new,GError ** error)218 handle_unsupported_ontology_change (TrackerDataManager *manager,
219 const gchar *ontology_path,
220 const gchar *subject,
221 const gchar *change,
222 const gchar *old,
223 const gchar *attempted_new,
224 GError **error)
225 {
226 g_set_error (error, TRACKER_DATA_ONTOLOGY_ERROR,
227 TRACKER_DATA_UNSUPPORTED_ONTOLOGY_CHANGE,
228 "%s: Unsupported ontology change for %s: can't change %s (old=%s, attempted new=%s)",
229 ontology_path != NULL ? ontology_path : "Unknown",
230 subject != NULL ? subject : "Unknown",
231 change != NULL ? change : "Unknown",
232 old != NULL ? old : "Unknown",
233 attempted_new != NULL ? attempted_new : "Unknown");
234 }
235
236 static void
set_secondary_index_for_single_value_property(TrackerDBInterface * iface,const gchar * database,const gchar * service_name,const gchar * field_name,const gchar * second_field_name,gboolean enabled,GError ** error)237 set_secondary_index_for_single_value_property (TrackerDBInterface *iface,
238 const gchar *database,
239 const gchar *service_name,
240 const gchar *field_name,
241 const gchar *second_field_name,
242 gboolean enabled,
243 GError **error)
244 {
245 GError *internal_error = NULL;
246
247 TRACKER_NOTE (ONTOLOGY_CHANGES,
248 g_message ("Dropping secondary index (single-value property): "
249 "DROP INDEX IF EXISTS \"%s_%s\"",
250 service_name, field_name));
251
252 tracker_db_interface_execute_query (iface, &internal_error,
253 "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
254 database,
255 service_name,
256 field_name);
257
258 if (internal_error) {
259 g_propagate_error (error, internal_error);
260 return;
261 }
262
263 if (enabled) {
264 TRACKER_NOTE (ONTOLOGY_CHANGES,
265 g_message ("Creating secondary index (single-value property): "
266 "CREATE INDEX \"%s_%s\" ON \"%s\" (\"%s\", \"%s\")",
267 service_name, field_name, service_name, field_name, second_field_name));
268
269 tracker_db_interface_execute_query (iface, &internal_error,
270 "CREATE INDEX \"%s\".\"%s_%s\" ON \"%s\" (\"%s\", \"%s\")",
271 database,
272 service_name,
273 field_name,
274 service_name,
275 field_name,
276 second_field_name);
277
278 if (internal_error) {
279 g_propagate_error (error, internal_error);
280 }
281 }
282 }
283
284 static void
set_index_for_single_value_property(TrackerDBInterface * iface,const gchar * database,const gchar * service_name,const gchar * field_name,gboolean enabled,gboolean datetime,GError ** error)285 set_index_for_single_value_property (TrackerDBInterface *iface,
286 const gchar *database,
287 const gchar *service_name,
288 const gchar *field_name,
289 gboolean enabled,
290 gboolean datetime,
291 GError **error)
292 {
293 GError *internal_error = NULL;
294
295 TRACKER_NOTE (ONTOLOGY_CHANGES,
296 g_message ("Dropping index (single-value property): "
297 "DROP INDEX IF EXISTS \"%s_%s\"",
298 service_name, field_name));
299
300 tracker_db_interface_execute_query (iface, &internal_error,
301 "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
302 database,
303 service_name,
304 field_name);
305
306 if (internal_error) {
307 g_propagate_error (error, internal_error);
308 return;
309 }
310
311 if (enabled) {
312 gchar *expr;
313
314 if (datetime)
315 expr = g_strdup_printf ("SparqlTimeSort(\"%s\")", field_name);
316 else
317 expr = g_strdup_printf ("\"%s\"", field_name);
318
319 TRACKER_NOTE (ONTOLOGY_CHANGES,
320 g_message ("Creating index (single-value property): "
321 "CREATE INDEX \"%s_%s\" ON \"%s\" (%s)",
322 service_name, field_name, service_name, expr));
323
324 tracker_db_interface_execute_query (iface, &internal_error,
325 "CREATE INDEX \"%s\".\"%s_%s\" ON \"%s\" (%s)",
326 database,
327 service_name,
328 field_name,
329 service_name,
330 expr);
331 g_free (expr);
332
333 if (internal_error) {
334 g_propagate_error (error, internal_error);
335 }
336 }
337 }
338
339 static void
set_index_for_multi_value_property(TrackerDBInterface * iface,const gchar * database,const gchar * service_name,const gchar * field_name,gboolean enabled,gboolean recreate,gboolean datetime,GError ** error)340 set_index_for_multi_value_property (TrackerDBInterface *iface,
341 const gchar *database,
342 const gchar *service_name,
343 const gchar *field_name,
344 gboolean enabled,
345 gboolean recreate,
346 gboolean datetime,
347 GError **error)
348 {
349 GError *internal_error = NULL;
350 gchar *expr;
351
352 TRACKER_NOTE (ONTOLOGY_CHANGES,
353 g_message ("Dropping index (multi-value property): "
354 "DROP INDEX IF EXISTS \"%s_%s_ID_ID\"",
355 service_name, field_name));
356
357 tracker_db_interface_execute_query (iface, &internal_error,
358 "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID_ID\"",
359 database,
360 service_name,
361 field_name);
362
363 if (internal_error) {
364 g_propagate_error (error, internal_error);
365 return;
366 }
367
368 /* Useful to have this here for the cases where we want to fully
369 * re-create the indexes even without an ontology change (when locale
370 * of the user changes) */
371 TRACKER_NOTE (ONTOLOGY_CHANGES,
372 g_message ("Dropping index (multi-value property): "
373 "DROP INDEX IF EXISTS \"%s_%s_ID\"",
374 service_name, field_name));
375 tracker_db_interface_execute_query (iface, &internal_error,
376 "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID\"",
377 database,
378 service_name,
379 field_name);
380
381 if (internal_error) {
382 g_propagate_error (error, internal_error);
383 return;
384 }
385
386 if (!recreate) {
387 return;
388 }
389
390 if (datetime)
391 expr = g_strdup_printf ("SparqlTimeSort(\"%s\")", field_name);
392 else
393 expr = g_strdup_printf ("\"%s\"", field_name);
394
395 if (enabled) {
396 TRACKER_NOTE (ONTOLOGY_CHANGES,
397 g_message ("Creating index (multi-value property): "
398 "CREATE INDEX \"%s_%s_ID\" ON \"%s_%s\" (ID)",
399 service_name, field_name, service_name, field_name));
400
401 tracker_db_interface_execute_query (iface, &internal_error,
402 "CREATE INDEX \"%s\".\"%s_%s_ID\" ON \"%s_%s\" (ID)",
403 database,
404 service_name,
405 field_name,
406 service_name,
407 field_name);
408
409 if (internal_error)
410 goto out;
411
412 TRACKER_NOTE (ONTOLOGY_CHANGES,
413 g_message ("Creating index (multi-value property): "
414 "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (%s, ID)",
415 service_name, field_name, service_name, field_name, expr));
416
417 tracker_db_interface_execute_query (iface, &internal_error,
418 "CREATE UNIQUE INDEX \"%s\".\"%s_%s_ID_ID\" ON \"%s_%s\" (%s, ID)",
419 database,
420 service_name,
421 field_name,
422 service_name,
423 field_name,
424 expr);
425
426 if (internal_error)
427 goto out;
428 } else {
429 TRACKER_NOTE (ONTOLOGY_CHANGES,
430 g_message ("Creating index (multi-value property): "
431 "CREATE UNIQUE INDEX \"%s_%s_ID_ID\" ON \"%s_%s\" (ID, %s)",
432 service_name, field_name, service_name, field_name, expr));
433
434 tracker_db_interface_execute_query (iface, &internal_error,
435 "CREATE UNIQUE INDEX \"%s\".\"%s_%s_ID_ID\" ON \"%s_%s\" (ID, %s)",
436 database,
437 service_name,
438 field_name,
439 service_name,
440 field_name,
441 expr);
442
443 if (internal_error)
444 goto out;
445 }
446
447 out:
448 if (internal_error) {
449 g_propagate_error (error, internal_error);
450 }
451
452 g_free (expr);
453 }
454
455 static gboolean
is_allowed_conversion(const gchar * oldv,const gchar * newv,Conversion allowed[])456 is_allowed_conversion (const gchar *oldv,
457 const gchar *newv,
458 Conversion allowed[])
459 {
460 guint i;
461
462 for (i = 0; allowed[i].from != NULL; i++) {
463 if (g_strcmp0 (allowed[i].from, oldv) == 0) {
464 if (g_strcmp0 (allowed[i].to, newv) == 0) {
465 return TRUE;
466 }
467 }
468 }
469
470 return FALSE;
471 }
472
473 static gboolean
update_property_value(TrackerDataManager * manager,const gchar * ontology_path,const gchar * kind,const gchar * subject,const gchar * predicate,const gchar * object,Conversion allowed[],TrackerClass * class,TrackerProperty * property,GError ** error_in)474 update_property_value (TrackerDataManager *manager,
475 const gchar *ontology_path,
476 const gchar *kind,
477 const gchar *subject,
478 const gchar *predicate,
479 const gchar *object,
480 Conversion allowed[],
481 TrackerClass *class,
482 TrackerProperty *property,
483 GError **error_in)
484 {
485 GError *error = NULL;
486 gboolean needed = TRUE;
487 gboolean is_new = FALSE;
488 GBytes *bytes;
489
490 if (class) {
491 is_new = tracker_class_get_is_new (class);
492 } else if (property) {
493 is_new = tracker_property_get_is_new (property);
494 }
495
496 if (!is_new) {
497 gchar *query = NULL;
498 TrackerDBCursor *cursor;
499
500 query = g_strdup_printf ("SELECT ?old_value WHERE { "
501 "<%s> %s ?old_value "
502 "}", subject, kind);
503
504 cursor = tracker_data_query_sparql_cursor (manager, query, &error);
505
506 if (cursor && tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
507 const gchar *str = NULL;
508
509 str = tracker_db_cursor_get_string (cursor, 0, NULL);
510
511 if (g_strcmp0 (object, str) == 0) {
512 needed = FALSE;
513 } else {
514 gboolean unsup_onto_err = FALSE;
515
516 if (allowed && !is_allowed_conversion (str, object, allowed)) {
517 handle_unsupported_ontology_change (manager,
518 ontology_path,
519 subject,
520 kind,
521 str,
522 object,
523 error_in);
524 needed = FALSE;
525 unsup_onto_err = TRUE;
526 }
527
528 if (!unsup_onto_err) {
529 bytes = g_bytes_new (str, strlen (str) + 1);
530 tracker_data_delete_statement (manager->data_update, NULL, subject, predicate, bytes, &error);
531 g_bytes_unref (bytes);
532
533 if (!error)
534 tracker_data_update_buffer_flush (manager->data_update, &error);
535 }
536 }
537
538 } else {
539 if (object && (g_strcmp0 (object, "false") == 0)) {
540 needed = FALSE;
541 } else {
542 needed = (object != NULL);
543 }
544 }
545 g_free (query);
546 if (cursor) {
547 g_object_unref (cursor);
548 }
549 } else {
550 needed = FALSE;
551 }
552
553
554 if (!error && needed && object) {
555 bytes = g_bytes_new (object, strlen (object) + 1);
556 tracker_data_insert_statement (manager->data_update, NULL, subject,
557 predicate, bytes,
558 &error);
559 g_bytes_unref (bytes);
560
561 if (!error)
562 tracker_data_update_buffer_flush (manager->data_update, &error);
563 }
564
565 if (error) {
566 g_critical ("Ontology change, %s", error->message);
567 g_clear_error (&error);
568 }
569
570 return needed;
571 }
572
573 static void
check_range_conversion_is_allowed(TrackerDataManager * manager,const gchar * ontology_path,const gchar * subject,const gchar * predicate,const gchar * object,GError ** error)574 check_range_conversion_is_allowed (TrackerDataManager *manager,
575 const gchar *ontology_path,
576 const gchar *subject,
577 const gchar *predicate,
578 const gchar *object,
579 GError **error)
580 {
581 TrackerDBCursor *cursor;
582 gchar *query;
583
584 query = g_strdup_printf ("SELECT ?old_value WHERE { "
585 "<%s> rdfs:range ?old_value "
586 "}", subject);
587
588 cursor = tracker_data_query_sparql_cursor (manager, query, NULL);
589
590 g_free (query);
591
592 if (cursor && tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
593 const gchar *str;
594
595 str = tracker_db_cursor_get_string (cursor, 0, NULL);
596
597 if (g_strcmp0 (object, str) != 0) {
598 if (!is_allowed_conversion (str, object, allowed_range_conversions)) {
599 handle_unsupported_ontology_change (manager,
600 ontology_path,
601 subject,
602 "rdfs:range",
603 str,
604 object,
605 error);
606 }
607 }
608 }
609
610 if (cursor) {
611 g_object_unref (cursor);
612 }
613 }
614
615 static void
fix_indexed_on_db(TrackerDataManager * manager,const gchar * database,TrackerProperty * property,gboolean recreate,GError ** error)616 fix_indexed_on_db (TrackerDataManager *manager,
617 const gchar *database,
618 TrackerProperty *property,
619 gboolean recreate,
620 GError **error)
621 {
622 GError *internal_error = NULL;
623 TrackerDBInterface *iface;
624 TrackerClass *class;
625 const gchar *service_name;
626 const gchar *field_name;
627 gboolean datetime;
628
629 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
630
631 class = tracker_property_get_domain (property);
632 field_name = tracker_property_get_name (property);
633 service_name = tracker_class_get_name (class);
634 datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
635
636 if (tracker_property_get_multiple_values (property)) {
637 set_index_for_multi_value_property (iface, database, service_name, field_name,
638 tracker_property_get_indexed (property),
639 recreate,
640 datetime,
641 &internal_error);
642 } else {
643 TrackerProperty *secondary_index;
644 TrackerClass **domain_index_classes;
645
646 secondary_index = tracker_property_get_secondary_index (property);
647 if (secondary_index == NULL) {
648 set_index_for_single_value_property (iface, database, service_name, field_name,
649 recreate && tracker_property_get_indexed (property),
650 datetime,
651 &internal_error);
652 } else {
653 set_secondary_index_for_single_value_property (iface, database, service_name, field_name,
654 tracker_property_get_name (secondary_index),
655 recreate && tracker_property_get_indexed (property),
656 &internal_error);
657 }
658
659 /* single-valued properties may also have domain-specific indexes */
660 domain_index_classes = tracker_property_get_domain_indexes (property);
661 while (!internal_error && domain_index_classes && *domain_index_classes) {
662 set_index_for_single_value_property (iface,
663 database,
664 tracker_class_get_name (*domain_index_classes),
665 field_name,
666 recreate,
667 datetime,
668 &internal_error);
669 domain_index_classes++;
670 }
671 }
672
673 if (internal_error) {
674 g_propagate_error (error, internal_error);
675 }
676 }
677
678 static void
fix_indexed(TrackerDataManager * manager,TrackerProperty * property,gboolean recreate,GError ** error)679 fix_indexed (TrackerDataManager *manager,
680 TrackerProperty *property,
681 gboolean recreate,
682 GError **error)
683 {
684 TrackerDBInterface *iface;
685 GHashTable *graphs;
686 GHashTableIter iter;
687 GError *internal_error = NULL;
688 gpointer value;
689
690 iface = tracker_data_manager_get_writable_db_interface (manager);
691 graphs = tracker_data_manager_ensure_graphs (manager, iface, &internal_error);
692 if (internal_error) {
693 g_propagate_error (error, internal_error);
694 return;
695 }
696
697 g_hash_table_iter_init (&iter, graphs);
698
699 while (g_hash_table_iter_next (&iter, &value, NULL)) {
700 fix_indexed_on_db (manager, value, property, recreate,
701 &internal_error);
702 if (internal_error) {
703 g_propagate_error (error, internal_error);
704 break;
705 }
706 }
707 }
708
709 static void
tracker_data_ontology_load_statement(TrackerDataManager * manager,const gchar * ontology_path,const gchar * subject,const gchar * predicate,const gchar * object,gboolean in_update,GHashTable * classes,GHashTable * properties,GPtrArray * seen_classes,GPtrArray * seen_properties,GError ** error)710 tracker_data_ontology_load_statement (TrackerDataManager *manager,
711 const gchar *ontology_path,
712 const gchar *subject,
713 const gchar *predicate,
714 const gchar *object,
715 gboolean in_update,
716 GHashTable *classes,
717 GHashTable *properties,
718 GPtrArray *seen_classes,
719 GPtrArray *seen_properties,
720 GError **error)
721 {
722 if (g_strcmp0 (predicate, RDF_TYPE) == 0) {
723 if (g_strcmp0 (object, RDFS_CLASS) == 0) {
724 TrackerClass *class;
725 gint subject_id;
726
727 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
728
729 if (class != NULL) {
730 if (seen_classes)
731 g_ptr_array_add (seen_classes, g_object_ref (class));
732 if (!in_update) {
733 g_critical ("%s: Duplicate definition of class %s", ontology_path, subject);
734 } else {
735 /* Reset for a correct post-check */
736 tracker_class_reset_domain_indexes (class);
737 tracker_class_reset_super_classes (class);
738 tracker_class_set_notify (class, FALSE);
739 }
740 return;
741 }
742
743 subject_id = tracker_data_update_ensure_resource (manager->data_update,
744 subject, NULL, error);
745 if (!subject_id)
746 return;
747
748 class = tracker_class_new (FALSE);
749 tracker_class_set_ontologies (class, manager->ontologies);
750 tracker_class_set_is_new (class, in_update);
751 tracker_class_set_uri (class, subject);
752 tracker_class_set_id (class, subject_id);
753 tracker_ontologies_add_class (manager->ontologies, class);
754 tracker_ontologies_add_id_uri_pair (manager->ontologies, subject_id, subject);
755
756 if (seen_classes)
757 g_ptr_array_add (seen_classes, g_object_ref (class));
758
759 if (classes) {
760 g_hash_table_insert (classes, GINT_TO_POINTER (subject_id), class);
761 } else {
762 g_object_unref (class);
763 }
764
765 } else if (g_strcmp0 (object, RDF_PROPERTY) == 0) {
766 TrackerProperty *property;
767 gint subject_id;
768
769 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
770 if (property != NULL) {
771 if (seen_properties)
772 g_ptr_array_add (seen_properties, g_object_ref (property));
773 if (!in_update) {
774 g_critical ("%s: Duplicate definition of property %s", ontology_path, subject);
775 } else {
776 /* Reset for a correct post and pre-check */
777 tracker_property_set_last_multiple_values (property, TRUE);
778 tracker_property_reset_domain_indexes (property);
779 tracker_property_reset_super_properties (property);
780 tracker_property_set_indexed (property, FALSE);
781 tracker_property_set_cardinality_changed (property, FALSE);
782 tracker_property_set_secondary_index (property, NULL);
783 tracker_property_set_is_inverse_functional_property (property, FALSE);
784 tracker_property_set_multiple_values (property, TRUE);
785 tracker_property_set_fulltext_indexed (property, FALSE);
786 }
787 return;
788 }
789
790 subject_id = tracker_data_update_ensure_resource (manager->data_update,
791 subject,
792 NULL, error);
793 if (!subject_id)
794 return;
795
796 property = tracker_property_new (FALSE);
797 tracker_property_set_ontologies (property, manager->ontologies);
798 tracker_property_set_is_new (property, in_update);
799 tracker_property_set_uri (property, subject);
800 tracker_property_set_id (property, subject_id);
801 tracker_property_set_multiple_values (property, TRUE);
802 tracker_ontologies_add_property (manager->ontologies, property);
803 tracker_ontologies_add_id_uri_pair (manager->ontologies, subject_id, subject);
804
805 if (seen_properties)
806 g_ptr_array_add (seen_properties, g_object_ref (property));
807
808 if (properties) {
809 g_hash_table_insert (properties, GINT_TO_POINTER (subject_id), property);
810 } else {
811 g_object_unref (property);
812 }
813
814 } else if (g_strcmp0 (object, NRL_INVERSE_FUNCTIONAL_PROPERTY) == 0) {
815 TrackerProperty *property;
816
817 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
818 if (property == NULL) {
819 g_critical ("%s: Unknown property %s", ontology_path, subject);
820 return;
821 }
822
823 tracker_property_set_is_inverse_functional_property (property, TRUE);
824 } else if (g_strcmp0 (object, TRACKER_PREFIX_NRL "Namespace") == 0) {
825 TrackerNamespace *namespace;
826
827 if (tracker_ontologies_get_namespace_by_uri (manager->ontologies, subject) != NULL) {
828 if (!in_update)
829 g_critical ("%s: Duplicate definition of namespace %s", ontology_path, subject);
830 return;
831 }
832
833 namespace = tracker_namespace_new (FALSE);
834 tracker_namespace_set_ontologies (namespace, manager->ontologies);
835 tracker_namespace_set_is_new (namespace, in_update);
836 tracker_namespace_set_uri (namespace, subject);
837 tracker_ontologies_add_namespace (manager->ontologies, namespace);
838 g_object_unref (namespace);
839
840 } else if (g_strcmp0 (object, TRACKER_PREFIX_NRL "Ontology") == 0) {
841 TrackerOntology *ontology;
842
843 if (tracker_ontologies_get_ontology_by_uri (manager->ontologies, subject) != NULL) {
844 if (!in_update)
845 g_critical ("%s: Duplicate definition of ontology %s", ontology_path, subject);
846 return;
847 }
848
849 ontology = tracker_ontology_new ();
850 tracker_ontology_set_ontologies (ontology, manager->ontologies);
851 tracker_ontology_set_is_new (ontology, in_update);
852 tracker_ontology_set_uri (ontology, subject);
853 tracker_ontologies_add_ontology (manager->ontologies, ontology);
854 g_object_unref (ontology);
855
856 }
857 } else if (g_strcmp0 (predicate, RDFS_SUB_CLASS_OF) == 0) {
858 TrackerClass *class, *super_class;
859 gboolean is_new;
860
861 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
862 if (class == NULL) {
863 g_critical ("%s: Unknown class %s", ontology_path, subject);
864 return;
865 }
866
867 is_new = tracker_class_get_is_new (class);
868 if (is_new != in_update) {
869 gboolean ignore = FALSE;
870 /* Detect unsupported ontology change */
871 if (in_update == TRUE && is_new == FALSE && g_strcmp0 (object, TRACKER_PREFIX_RDFS "Resource") != 0) {
872 TrackerClass **super_classes = tracker_class_get_super_classes (class);
873 gboolean had = FALSE;
874
875 super_class = tracker_ontologies_get_class_by_uri (manager->ontologies, object);
876 if (super_class == NULL) {
877 g_critical ("%s: Unknown class %s", ontology_path, object);
878 return;
879 }
880
881 while (*super_classes) {
882 if (*super_classes == super_class) {
883 ignore = TRUE;
884 TRACKER_NOTE (ONTOLOGY_CHANGES,
885 g_message ("%s: Class %s already has rdfs:subClassOf in %s",
886 ontology_path, object, subject));
887 break;
888 }
889 super_classes++;
890 }
891
892 super_classes = tracker_class_get_last_super_classes (class);
893 if (super_classes) {
894 while (*super_classes) {
895 if (super_class == *super_classes) {
896 had = TRUE;
897 }
898 super_classes++;
899 }
900 }
901
902 /* This doesn't detect removed rdfs:subClassOf situations, it
903 * only checks whether no new ones are being added. For
904 * detecting the removal of a rdfs:subClassOf, please check the
905 * tracker_data_ontology_process_changes_pre_db stuff */
906
907
908 if (!ignore && !had) {
909 handle_unsupported_ontology_change (manager,
910 ontology_path,
911 tracker_class_get_name (class),
912 "rdfs:subClassOf",
913 "-",
914 tracker_class_get_name (super_class),
915 error);
916 }
917 }
918
919 if (!ignore) {
920 super_class = tracker_ontologies_get_class_by_uri (manager->ontologies, object);
921 tracker_class_add_super_class (class, super_class);
922 }
923
924 return;
925 }
926
927 super_class = tracker_ontologies_get_class_by_uri (manager->ontologies, object);
928 if (super_class == NULL) {
929 g_critical ("%s: Unknown class %s", ontology_path, object);
930 return;
931 }
932
933 tracker_class_add_super_class (class, super_class);
934
935 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "notify") == 0) {
936 TrackerClass *class;
937
938 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
939
940 if (class == NULL) {
941 g_critical ("%s: Unknown class %s", ontology_path, subject);
942 return;
943 }
944
945 tracker_class_set_notify (class, (strcmp (object, "true") == 0));
946 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "domainIndex") == 0) {
947 TrackerClass *class;
948 TrackerProperty *property;
949 TrackerProperty **properties;
950 gboolean ignore = FALSE;
951 gboolean had = FALSE;
952 guint n_props, i;
953
954 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
955
956 if (class == NULL) {
957 g_critical ("%s: Unknown class %s", ontology_path, subject);
958 return;
959 }
960
961 property = tracker_ontologies_get_property_by_uri (manager->ontologies, object);
962
963 if (property == NULL) {
964
965 /* In this case the import of the TTL will still make the introspection
966 * have the URI set as a nrl:domainIndex for class. The critical
967 * will have happened, but future operations might not cope with this
968 * situation. TODO: add error handling so that the entire ontology
969 * change operation is discarded, for example ignore the entire
970 * .ontology file and rollback all changes that happened. I would
971 * prefer a hard abort() here over a g_critical(), to be honest.
972 *
973 * Of course don't we yet allow just anybody to alter the ontology
974 * files. So very serious is this lack of thorough error handling
975 * not. Let's just not make mistakes when changing the .ontology
976 * files for now. */
977
978 g_critical ("%s: Unknown property %s for nrl:domainIndex in %s."
979 "Don't release this .ontology change!",
980 ontology_path, object, subject);
981 return;
982 }
983
984 if (tracker_property_get_multiple_values (property)) {
985 g_critical ("%s: Property %s has multiple values while trying to add it as nrl:domainIndex in %s, this isn't supported",
986 ontology_path, object, subject);
987 return;
988 }
989
990 properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
991 for (i = 0; i < n_props; i++) {
992 if (tracker_property_get_domain (properties[i]) == class &&
993 properties[i] == property) {
994 g_critical ("%s: Property %s is already a first-class property of %s while trying to add it as nrl:domainIndex",
995 ontology_path, object, subject);
996 }
997 }
998
999 properties = tracker_class_get_domain_indexes (class);
1000 while (*properties) {
1001 if (property == *properties) {
1002 TRACKER_NOTE (ONTOLOGY_CHANGES,
1003 g_message ("%s: Property %s already a nrl:domainIndex in %s",
1004 ontology_path, object, subject));
1005 ignore = TRUE;
1006 }
1007 properties++;
1008 }
1009
1010 properties = tracker_class_get_last_domain_indexes (class);
1011 if (properties) {
1012 while (*properties) {
1013 if (property == *properties) {
1014 had = TRUE;
1015 }
1016 properties++;
1017 }
1018 }
1019
1020 /* This doesn't detect removed nrl:domainIndex situations, it
1021 * only checks whether no new ones are being added. For
1022 * detecting the removal of a nrl:domainIndex, please check the
1023 * tracker_data_ontology_process_changes_pre_db stuff */
1024
1025 if (!ignore) {
1026 if (!had) {
1027 tracker_property_set_is_new_domain_index (property, class, in_update);
1028 }
1029 tracker_class_add_domain_index (class, property);
1030 tracker_property_add_domain_index (property, class);
1031 }
1032
1033 } else if (g_strcmp0 (predicate, RDFS_SUB_PROPERTY_OF) == 0) {
1034 TrackerProperty *property, *super_property;
1035 gboolean is_new;
1036
1037 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1038 if (property == NULL) {
1039 g_critical ("%s: Unknown property %s", ontology_path, subject);
1040 return;
1041 }
1042
1043 is_new = tracker_property_get_is_new (property);
1044 if (is_new != in_update) {
1045 gboolean ignore = FALSE;
1046 /* Detect unsupported ontology change */
1047 if (in_update == TRUE && is_new == FALSE) {
1048 TrackerProperty **super_properties = tracker_property_get_super_properties (property);
1049 gboolean had = FALSE;
1050
1051 super_property = tracker_ontologies_get_property_by_uri (manager->ontologies, object);
1052 if (super_property == NULL) {
1053 g_critical ("%s: Unknown property %s", ontology_path, object);
1054 return;
1055 }
1056
1057 while (*super_properties) {
1058 if (*super_properties == super_property) {
1059 ignore = TRUE;
1060 TRACKER_NOTE (ONTOLOGY_CHANGES,
1061 g_message ("%s: Property %s already has rdfs:subPropertyOf in %s",
1062 ontology_path, object, subject));
1063 break;
1064 }
1065 super_properties++;
1066 }
1067
1068 super_properties = tracker_property_get_last_super_properties (property);
1069 if (super_properties) {
1070 while (*super_properties) {
1071 if (super_property == *super_properties) {
1072 had = TRUE;
1073 }
1074 super_properties++;
1075 }
1076 }
1077
1078 /* This doesn't detect removed rdfs:subPropertyOf situations, it
1079 * only checks whether no new ones are being added. For
1080 * detecting the removal of a rdfs:subPropertyOf, please check the
1081 * tracker_data_ontology_process_changes_pre_db stuff */
1082
1083 if (!ignore && !had) {
1084 handle_unsupported_ontology_change (manager,
1085 ontology_path,
1086 tracker_property_get_name (property),
1087 "rdfs:subPropertyOf",
1088 "-",
1089 tracker_property_get_name (super_property),
1090 error);
1091 }
1092 }
1093
1094 if (!ignore) {
1095 super_property = tracker_ontologies_get_property_by_uri (manager->ontologies, object);
1096 tracker_property_add_super_property (property, super_property);
1097 }
1098
1099 return;
1100 }
1101
1102 super_property = tracker_ontologies_get_property_by_uri (manager->ontologies, object);
1103 if (super_property == NULL) {
1104 g_critical ("%s: Unknown property %s", ontology_path, object);
1105 return;
1106 }
1107
1108 tracker_property_add_super_property (property, super_property);
1109 } else if (g_strcmp0 (predicate, RDFS_DOMAIN) == 0) {
1110 TrackerProperty *property;
1111 TrackerClass *domain;
1112 gboolean is_new;
1113
1114 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1115 if (property == NULL) {
1116 g_critical ("%s: Unknown property %s", ontology_path, subject);
1117 return;
1118 }
1119
1120 domain = tracker_ontologies_get_class_by_uri (manager->ontologies, object);
1121 if (domain == NULL) {
1122 g_critical ("%s: Unknown class %s", ontology_path, object);
1123 return;
1124 }
1125
1126 is_new = tracker_property_get_is_new (property);
1127 if (is_new != in_update) {
1128 /* Detect unsupported ontology change */
1129 if (in_update == TRUE && is_new == FALSE) {
1130 TrackerClass *old_domain = tracker_property_get_domain (property);
1131 if (old_domain != domain) {
1132 handle_unsupported_ontology_change (manager,
1133 ontology_path,
1134 tracker_property_get_name (property),
1135 "rdfs:domain",
1136 tracker_class_get_name (old_domain),
1137 tracker_class_get_name (domain),
1138 error);
1139 }
1140 }
1141 return;
1142 }
1143
1144 tracker_property_set_domain (property, domain);
1145 } else if (g_strcmp0 (predicate, RDFS_RANGE) == 0) {
1146 TrackerProperty *property;
1147 TrackerClass *range;
1148
1149 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1150 if (property == NULL) {
1151 g_critical ("%s: Unknown property %s", ontology_path, subject);
1152 return;
1153 }
1154
1155 if (tracker_property_get_is_new (property) != in_update) {
1156 GError *err = NULL;
1157 check_range_conversion_is_allowed (manager,
1158 ontology_path,
1159 subject,
1160 predicate,
1161 object,
1162 &err);
1163 if (err) {
1164 g_propagate_error (error, err);
1165 return;
1166 }
1167 }
1168
1169 range = tracker_ontologies_get_class_by_uri (manager->ontologies, object);
1170 if (range == NULL) {
1171 g_critical ("%s: Unknown class %s", ontology_path, object);
1172 return;
1173 }
1174
1175 tracker_property_set_range (property, range);
1176 } else if (g_strcmp0 (predicate, NRL_MAX_CARDINALITY) == 0) {
1177 TrackerProperty *property;
1178
1179 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1180 if (property == NULL) {
1181 g_critical ("%s: Unknown property %s", ontology_path, subject);
1182 return;
1183 }
1184
1185 if (atoi (object) == 1) {
1186 tracker_property_set_multiple_values (property, FALSE);
1187 tracker_property_set_last_multiple_values (property, FALSE);
1188 } else {
1189 tracker_property_set_multiple_values (property, TRUE);
1190 tracker_property_set_last_multiple_values (property, TRUE);
1191 }
1192
1193 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "indexed") == 0) {
1194 TrackerProperty *property;
1195
1196 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1197 if (property == NULL) {
1198 g_critical ("%s: Unknown property %s", ontology_path, subject);
1199 return;
1200 }
1201
1202 tracker_property_set_indexed (property, (strcmp (object, "true") == 0));
1203 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "secondaryIndex") == 0) {
1204 TrackerProperty *property, *secondary_index;
1205
1206 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1207 if (property == NULL) {
1208 g_critical ("%s: Unknown property %s", ontology_path, subject);
1209 return;
1210 }
1211
1212 secondary_index = tracker_ontologies_get_property_by_uri (manager->ontologies, object);
1213 if (secondary_index == NULL) {
1214 g_critical ("%s: Unknown property %s", ontology_path, object);
1215 return;
1216 }
1217
1218 tracker_property_set_secondary_index (property, secondary_index);
1219 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "fulltextIndexed") == 0) {
1220 TrackerProperty *property;
1221
1222 property = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1223 if (property == NULL) {
1224 g_critical ("%s: Unknown property %s", ontology_path, subject);
1225 return;
1226 }
1227
1228 tracker_property_set_fulltext_indexed (property,
1229 strcmp (object, "true") == 0);
1230 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "prefix") == 0) {
1231 TrackerNamespace *namespace;
1232
1233 namespace = tracker_ontologies_get_namespace_by_uri (manager->ontologies, subject);
1234 if (namespace == NULL) {
1235 g_critical ("%s: Unknown namespace %s", ontology_path, subject);
1236 return;
1237 }
1238
1239 if (tracker_namespace_get_is_new (namespace) != in_update) {
1240 return;
1241 }
1242
1243 tracker_namespace_set_prefix (namespace, object);
1244 } else if (g_strcmp0 (predicate, NRL_LAST_MODIFIED) == 0) {
1245 TrackerOntology *ontology;
1246 GDateTime *datetime;
1247 GError *error = NULL;
1248
1249 ontology = tracker_ontologies_get_ontology_by_uri (manager->ontologies, subject);
1250 if (ontology == NULL) {
1251 g_critical ("%s: Unknown ontology %s", ontology_path, subject);
1252 return;
1253 }
1254
1255 if (tracker_ontology_get_is_new (ontology) != in_update) {
1256 return;
1257 }
1258
1259 datetime = tracker_date_new_from_iso8601 (object, &error);
1260 if (!datetime) {
1261 g_critical ("%s: error parsing nrl:lastModified: %s",
1262 ontology_path, error->message);
1263 g_error_free (error);
1264 return;
1265 }
1266
1267 tracker_ontology_set_last_modified (ontology, g_date_time_to_unix (datetime));
1268 g_date_time_unref (datetime);
1269 }
1270 }
1271
1272
1273 static void
check_for_deleted_domain_index(TrackerDataManager * manager,TrackerClass * class)1274 check_for_deleted_domain_index (TrackerDataManager *manager,
1275 TrackerClass *class)
1276 {
1277 TrackerProperty **last_domain_indexes;
1278 GSList *hfound = NULL, *deleted = NULL;
1279
1280 last_domain_indexes = tracker_class_get_last_domain_indexes (class);
1281
1282 if (!last_domain_indexes) {
1283 return;
1284 }
1285
1286 while (*last_domain_indexes) {
1287 TrackerProperty *last_domain_index = *last_domain_indexes;
1288 gboolean found = FALSE;
1289 TrackerProperty **domain_indexes;
1290
1291 domain_indexes = tracker_class_get_domain_indexes (class);
1292
1293 while (*domain_indexes) {
1294 TrackerProperty *domain_index = *domain_indexes;
1295 if (last_domain_index == domain_index) {
1296 found = TRUE;
1297 hfound = g_slist_prepend (hfound, domain_index);
1298 break;
1299 }
1300 domain_indexes++;
1301 }
1302
1303 if (!found) {
1304 deleted = g_slist_prepend (deleted, last_domain_index);
1305 }
1306
1307 last_domain_indexes++;
1308 }
1309
1310
1311 if (deleted) {
1312 GSList *l;
1313 TrackerProperty **properties;
1314 guint n_props, i;
1315
1316 tracker_class_set_db_schema_changed (class, TRUE);
1317
1318 properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
1319 for (i = 0; i < n_props; i++) {
1320 if (tracker_property_get_domain (properties[i]) == class &&
1321 !tracker_property_get_multiple_values (properties[i])) {
1322
1323 /* These aren't domain-indexes, but it's just a flag for the
1324 * functionality that'll recreate the table to know that the
1325 * property must be involved in the recreation and copy */
1326
1327 tracker_property_set_is_new_domain_index (properties[i], class, TRUE);
1328 }
1329 }
1330
1331 for (l = hfound; l != NULL; l = l->next) {
1332 TrackerProperty *prop = l->data;
1333 TRACKER_NOTE (ONTOLOGY_CHANGES,
1334 g_message ("Ontology change: keeping nrl:domainIndex: %s",
1335 tracker_property_get_name (prop)));
1336 tracker_property_set_is_new_domain_index (prop, class, TRUE);
1337 }
1338
1339 for (l = deleted; l != NULL; l = l->next) {
1340 GError *error = NULL;
1341 TrackerProperty *prop = l->data;
1342 const gchar *uri;
1343 GBytes *bytes;
1344
1345 TRACKER_NOTE (ONTOLOGY_CHANGES,
1346 g_message ("Ontology change: deleting nrl:domainIndex: %s",
1347 tracker_property_get_name (prop)));
1348 tracker_property_del_domain_index (prop, class);
1349 tracker_class_del_domain_index (class, prop);
1350
1351 uri = tracker_property_get_uri (prop);
1352 bytes = g_bytes_new (uri, strlen (uri) + 1);
1353 tracker_data_delete_statement (manager->data_update, NULL,
1354 tracker_class_get_uri (class),
1355 TRACKER_PREFIX_NRL "domainIndex",
1356 bytes,
1357 &error);
1358 g_bytes_unref (bytes);
1359
1360 if (error) {
1361 g_critical ("Ontology change, %s", error->message);
1362 g_clear_error (&error);
1363 } else {
1364 tracker_data_update_buffer_flush (manager->data_update, &error);
1365 if (error) {
1366 g_critical ("Ontology change, %s", error->message);
1367 g_clear_error (&error);
1368 }
1369 }
1370 }
1371
1372 g_slist_free (deleted);
1373 }
1374
1375 g_slist_free (hfound);
1376 }
1377
1378 static void
check_for_deleted_super_classes(TrackerDataManager * manager,TrackerClass * class,GError ** error)1379 check_for_deleted_super_classes (TrackerDataManager *manager,
1380 TrackerClass *class,
1381 GError **error)
1382 {
1383 TrackerClass **last_super_classes;
1384
1385 last_super_classes = tracker_class_get_last_super_classes (class);
1386
1387 if (!last_super_classes) {
1388 return;
1389 }
1390
1391 while (*last_super_classes) {
1392 TrackerClass *last_super_class = *last_super_classes;
1393 gboolean found = FALSE;
1394 TrackerClass **super_classes;
1395
1396 if (g_strcmp0 (tracker_class_get_uri (last_super_class), TRACKER_PREFIX_RDFS "Resource") == 0) {
1397 last_super_classes++;
1398 continue;
1399 }
1400
1401 super_classes = tracker_class_get_super_classes (class);
1402
1403 while (*super_classes) {
1404 TrackerClass *super_class = *super_classes;
1405
1406 if (last_super_class == super_class) {
1407 found = TRUE;
1408 break;
1409 }
1410 super_classes++;
1411 }
1412
1413 if (!found) {
1414 const gchar *ontology_path = "Unknown";
1415 const gchar *subject = tracker_class_get_uri (class);
1416
1417 handle_unsupported_ontology_change (manager,
1418 ontology_path,
1419 subject,
1420 "rdfs:subClassOf", "-", "-",
1421 error);
1422 return;
1423 }
1424
1425 last_super_classes++;
1426 }
1427 }
1428
1429 static void
check_for_max_cardinality_change(TrackerDataManager * manager,TrackerProperty * property,GError ** error)1430 check_for_max_cardinality_change (TrackerDataManager *manager,
1431 TrackerProperty *property,
1432 GError **error)
1433 {
1434 gboolean orig_multiple_values = tracker_property_get_orig_multiple_values (property);
1435 gboolean new_multiple_values = tracker_property_get_multiple_values (property);
1436 GError *n_error = NULL;
1437 const gchar *ontology_path = "Unknown";
1438
1439 if (tracker_property_get_is_new (property) == FALSE &&
1440 (orig_multiple_values != new_multiple_values &&
1441 orig_multiple_values == TRUE)) {
1442 const gchar *ontology_path = "Unknown";
1443 const gchar *subject = tracker_property_get_uri (property);
1444
1445 handle_unsupported_ontology_change (manager,
1446 ontology_path,
1447 subject,
1448 "nrl:maxCardinality", "none", "1",
1449 &n_error);
1450 if (n_error) {
1451 g_propagate_error (error, n_error);
1452 return;
1453 }
1454 } else if (tracker_property_get_is_new (property) == FALSE &&
1455 orig_multiple_values != new_multiple_values &&
1456 orig_multiple_values == FALSE) {
1457 const gchar *subject = tracker_property_get_uri (property);
1458
1459 if (update_property_value (manager, ontology_path,
1460 "nrl:maxCardinality",
1461 subject,
1462 TRACKER_PREFIX_NRL "maxCardinality",
1463 NULL, allowed_cardinality_conversions,
1464 NULL, property,
1465 &n_error)) {
1466 TrackerClass *class;
1467 class = tracker_property_get_domain(property);
1468
1469 tracker_property_set_db_schema_changed (property, TRUE);
1470 tracker_property_set_cardinality_changed (property, TRUE);
1471 tracker_class_set_db_schema_changed (class, TRUE);
1472 }
1473
1474 if (n_error) {
1475 g_propagate_error (error, n_error);
1476 return;
1477 }
1478 }
1479 }
1480
1481 static void
check_for_deleted_super_properties(TrackerDataManager * manager,TrackerProperty * property,GError ** error)1482 check_for_deleted_super_properties (TrackerDataManager *manager,
1483 TrackerProperty *property,
1484 GError **error)
1485 {
1486 TrackerProperty **last_super_properties;
1487 GList *to_remove = NULL;
1488
1489 last_super_properties = tracker_property_get_last_super_properties (property);
1490
1491 if (!last_super_properties) {
1492 return;
1493 }
1494
1495 while (*last_super_properties) {
1496 TrackerProperty *last_super_property = *last_super_properties;
1497 gboolean found = FALSE;
1498 TrackerProperty **super_properties;
1499
1500 super_properties = tracker_property_get_super_properties (property);
1501
1502 while (*super_properties) {
1503 TrackerProperty *super_property = *super_properties;
1504
1505 if (last_super_property == super_property) {
1506 found = TRUE;
1507 break;
1508 }
1509 super_properties++;
1510 }
1511
1512 if (!found) {
1513 to_remove = g_list_prepend (to_remove, last_super_property);
1514 }
1515
1516 last_super_properties++;
1517 }
1518
1519 if (to_remove) {
1520 GList *copy = to_remove;
1521
1522 while (copy) {
1523 GError *n_error = NULL;
1524 TrackerProperty *prop_to_remove = copy->data;
1525 const gchar *object = tracker_property_get_uri (prop_to_remove);
1526 const gchar *subject = tracker_property_get_uri (property);
1527 GBytes *bytes;
1528
1529 tracker_property_del_super_property (property, prop_to_remove);
1530
1531 bytes = g_bytes_new (object, strlen (object) + 1);
1532 tracker_data_delete_statement (manager->data_update, NULL, subject,
1533 TRACKER_PREFIX_RDFS "subPropertyOf",
1534 bytes, &n_error);
1535 g_bytes_unref (bytes);
1536
1537 if (!n_error) {
1538 tracker_data_update_buffer_flush (manager->data_update, &n_error);
1539 }
1540
1541 if (n_error) {
1542 g_propagate_error (error, n_error);
1543 return;
1544 }
1545
1546 copy = copy->next;
1547 }
1548 g_list_free (to_remove);
1549 }
1550 }
1551
1552 static void
tracker_data_ontology_process_changes_pre_db(TrackerDataManager * manager,GPtrArray * seen_classes,GPtrArray * seen_properties,GError ** error)1553 tracker_data_ontology_process_changes_pre_db (TrackerDataManager *manager,
1554 GPtrArray *seen_classes,
1555 GPtrArray *seen_properties,
1556 GError **error)
1557 {
1558 gint i;
1559 if (seen_classes) {
1560 for (i = 0; i < seen_classes->len; i++) {
1561 GError *n_error = NULL;
1562 TrackerClass *class = g_ptr_array_index (seen_classes, i);
1563
1564 check_for_deleted_domain_index (manager, class);
1565 check_for_deleted_super_classes (manager, class, &n_error);
1566
1567 if (n_error) {
1568 g_propagate_error (error, n_error);
1569 return;
1570 }
1571 }
1572 }
1573
1574 if (seen_properties) {
1575 for (i = 0; i < seen_properties->len; i++) {
1576 GError *n_error = NULL;
1577 TrackerProperty *property = g_ptr_array_index (seen_properties, i);
1578
1579 check_for_max_cardinality_change (manager, property, &n_error);
1580
1581 if (n_error) {
1582 g_propagate_error (error, n_error);
1583 return;
1584 }
1585
1586 check_for_deleted_super_properties (manager, property, &n_error);
1587
1588 if (n_error) {
1589 g_propagate_error (error, n_error);
1590 return;
1591 }
1592 }
1593 }
1594 }
1595
1596 static void
tracker_data_ontology_process_changes_post_db(TrackerDataManager * manager,GPtrArray * seen_classes,GPtrArray * seen_properties,GError ** error)1597 tracker_data_ontology_process_changes_post_db (TrackerDataManager *manager,
1598 GPtrArray *seen_classes,
1599 GPtrArray *seen_properties,
1600 GError **error)
1601 {
1602 gint i;
1603 /* TODO: Collect the ontology-paths of the seen events for proper error reporting */
1604 const gchar *ontology_path = "Unknown";
1605
1606 /* This updates property-property changes and marks classes for necessity
1607 * of having their tables recreated later. There's support for
1608 * nrl:notify and nrl:indexed */
1609
1610 if (seen_classes) {
1611 for (i = 0; i < seen_classes->len; i++) {
1612 TrackerClass *class = g_ptr_array_index (seen_classes, i);
1613 const gchar *subject;
1614 GError *n_error = NULL;
1615
1616 subject = tracker_class_get_uri (class);
1617
1618 if (tracker_class_get_notify (class)) {
1619 update_property_value (manager, ontology_path,
1620 "nrl:notify",
1621 subject,
1622 TRACKER_PREFIX_NRL "notify",
1623 "true", allowed_boolean_conversions,
1624 class, NULL, &n_error);
1625 } else {
1626 update_property_value (manager, ontology_path,
1627 "nrl:notify",
1628 subject,
1629 TRACKER_PREFIX_NRL "notify",
1630 "false", allowed_boolean_conversions,
1631 class, NULL, &n_error);
1632 }
1633
1634 if (n_error) {
1635 g_propagate_error (error, n_error);
1636 return;
1637 }
1638 }
1639 }
1640
1641 if (seen_properties) {
1642 for (i = 0; i < seen_properties->len; i++) {
1643 TrackerProperty *property = g_ptr_array_index (seen_properties, i);
1644 const gchar *subject;
1645 gchar *query;
1646 TrackerProperty *secondary_index;
1647 gboolean indexed_set = FALSE, in_onto;
1648 GError *n_error = NULL;
1649 TrackerSparqlCursor *cursor;
1650
1651 subject = tracker_property_get_uri (property);
1652
1653 /* Check for nrl:InverseFunctionalProperty changes (not supported) */
1654 in_onto = tracker_property_get_is_inverse_functional_property (property);
1655
1656 query = g_strdup_printf ("ASK { <%s> a nrl:InverseFunctionalProperty }", subject);
1657 cursor = TRACKER_SPARQL_CURSOR (tracker_data_query_sparql_cursor (manager, query, &n_error));
1658 g_free (query);
1659
1660 if (n_error) {
1661 g_propagate_error (error, n_error);
1662 return;
1663 }
1664
1665 if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
1666 if (tracker_sparql_cursor_get_boolean (cursor, 0) != in_onto) {
1667 handle_unsupported_ontology_change (manager,
1668 ontology_path,
1669 subject,
1670 "nrl:InverseFunctionalProperty", "-", "-",
1671 &n_error);
1672
1673 if (n_error) {
1674 g_object_unref (cursor);
1675 g_propagate_error (error, n_error);
1676 return;
1677 }
1678 }
1679 }
1680
1681 if (cursor) {
1682 g_object_unref (cursor);
1683 }
1684
1685 if (tracker_property_get_indexed (property)) {
1686 if (update_property_value (manager, ontology_path,
1687 "nrl:indexed",
1688 subject,
1689 TRACKER_PREFIX_NRL "indexed",
1690 "true", allowed_boolean_conversions,
1691 NULL, property, &n_error)) {
1692 fix_indexed (manager, property, TRUE, &n_error);
1693 indexed_set = TRUE;
1694 }
1695 } else {
1696 if (update_property_value (manager, ontology_path,
1697 "nrl:indexed",
1698 subject,
1699 TRACKER_PREFIX_NRL "indexed",
1700 "false", allowed_boolean_conversions,
1701 NULL, property, &n_error)) {
1702 fix_indexed (manager, property, TRUE, &n_error);
1703 indexed_set = TRUE;
1704 }
1705 }
1706
1707 if (n_error) {
1708 g_propagate_error (error, n_error);
1709 return;
1710 }
1711
1712 secondary_index = tracker_property_get_secondary_index (property);
1713
1714 if (secondary_index) {
1715 if (update_property_value (manager, ontology_path,
1716 "nrl:secondaryIndex",
1717 subject,
1718 TRACKER_PREFIX_NRL "secondaryIndex",
1719 tracker_property_get_uri (secondary_index), NULL,
1720 NULL, property, &n_error)) {
1721 if (!indexed_set) {
1722 fix_indexed (manager, property, TRUE, &n_error);
1723 }
1724 }
1725 } else {
1726 if (update_property_value (manager, ontology_path,
1727 "nrl:secondaryIndex",
1728 subject,
1729 TRACKER_PREFIX_NRL "secondaryIndex",
1730 NULL, NULL,
1731 NULL, property, &n_error)) {
1732 if (!indexed_set) {
1733 fix_indexed (manager, property, TRUE, &n_error);
1734 }
1735 }
1736 }
1737
1738 if (n_error) {
1739 g_propagate_error (error, n_error);
1740 return;
1741 }
1742
1743 if (update_property_value (manager, ontology_path,
1744 "rdfs:range", subject, TRACKER_PREFIX_RDFS "range",
1745 tracker_class_get_uri (tracker_property_get_range (property)),
1746 allowed_range_conversions,
1747 NULL, property, &n_error)) {
1748 TrackerClass *class;
1749
1750 class = tracker_property_get_domain (property);
1751 tracker_class_set_db_schema_changed (class, TRUE);
1752 tracker_property_set_db_schema_changed (property, TRUE);
1753 }
1754
1755 if (n_error) {
1756 g_propagate_error (error, n_error);
1757 return;
1758 }
1759 }
1760 }
1761 }
1762
1763 static void
tracker_data_ontology_process_changes_post_import(GPtrArray * seen_classes,GPtrArray * seen_properties)1764 tracker_data_ontology_process_changes_post_import (GPtrArray *seen_classes,
1765 GPtrArray *seen_properties)
1766 {
1767 return;
1768 }
1769
1770 static void
tracker_data_ontology_free_seen(GPtrArray * seen)1771 tracker_data_ontology_free_seen (GPtrArray *seen)
1772 {
1773 if (seen) {
1774 g_ptr_array_foreach (seen, (GFunc) g_object_unref, NULL);
1775 g_ptr_array_free (seen, TRUE);
1776 }
1777 }
1778
1779 static void
load_ontology_file(TrackerDataManager * manager,GFile * file,gboolean in_update,GPtrArray * seen_classes,GPtrArray * seen_properties,GError ** error)1780 load_ontology_file (TrackerDataManager *manager,
1781 GFile *file,
1782 gboolean in_update,
1783 GPtrArray *seen_classes,
1784 GPtrArray *seen_properties,
1785 GError **error)
1786 {
1787 TrackerTurtleReader *reader;
1788 GError *ttl_error = NULL;
1789 gchar *ontology_uri;
1790 const gchar *subject, *predicate, *object;
1791
1792 reader = tracker_turtle_reader_new_for_file (file, &ttl_error);
1793
1794 if (ttl_error) {
1795 g_propagate_error (error, ttl_error);
1796 return;
1797 }
1798
1799 ontology_uri = g_file_get_uri (file);
1800
1801 /* Post checks are only needed for ontology updates, not the initial
1802 * ontology */
1803
1804 while (tracker_turtle_reader_next (reader,
1805 &subject, &predicate, &object,
1806 NULL, NULL, &ttl_error)) {
1807 GError *ontology_error = NULL;
1808
1809 tracker_data_ontology_load_statement (manager, ontology_uri,
1810 subject, predicate, object,
1811 in_update, NULL, NULL,
1812 seen_classes, seen_properties, &ontology_error);
1813
1814 if (ontology_error) {
1815 g_propagate_error (error, ontology_error);
1816 break;
1817 }
1818 }
1819
1820 g_free (ontology_uri);
1821 g_object_unref (reader);
1822
1823 if (ttl_error) {
1824 g_propagate_error (error, ttl_error);
1825 }
1826 }
1827
1828
1829 static TrackerOntology*
get_ontology_from_file(TrackerDataManager * manager,GFile * file)1830 get_ontology_from_file (TrackerDataManager *manager,
1831 GFile *file)
1832 {
1833 const gchar *subject, *predicate, *object;
1834 TrackerTurtleReader *reader;
1835 GError *error = NULL;
1836 GHashTable *ontology_uris;
1837 TrackerOntology *ret = NULL;
1838
1839 reader = tracker_turtle_reader_new_for_file (file, &error);
1840
1841 if (error) {
1842 g_critical ("Turtle parse error: %s", error->message);
1843 g_error_free (error);
1844 return NULL;
1845 }
1846
1847 ontology_uris = g_hash_table_new_full (g_str_hash,
1848 g_str_equal,
1849 g_free,
1850 g_object_unref);
1851
1852 while (tracker_turtle_reader_next (reader,
1853 &subject, &predicate, &object,
1854 NULL, NULL, &error)) {
1855 if (g_strcmp0 (predicate, RDF_TYPE) == 0) {
1856 if (g_strcmp0 (object, TRACKER_PREFIX_NRL "Ontology") == 0) {
1857 TrackerOntology *ontology;
1858
1859 ontology = tracker_ontology_new ();
1860 tracker_ontology_set_ontologies (ontology, manager->ontologies);
1861 tracker_ontology_set_uri (ontology, subject);
1862
1863 /* Passes ownership */
1864 g_hash_table_insert (ontology_uris,
1865 g_strdup (subject),
1866 ontology);
1867 }
1868 } else if (g_strcmp0 (predicate, NRL_LAST_MODIFIED) == 0) {
1869 TrackerOntology *ontology;
1870 GDateTime *datetime;
1871 GError *error = NULL;
1872
1873 ontology = g_hash_table_lookup (ontology_uris, subject);
1874 if (ontology == NULL) {
1875 gchar *uri = g_file_get_uri (file);
1876 g_critical ("%s: Unknown ontology %s", uri, subject);
1877 g_free (uri);
1878 return NULL;
1879 }
1880
1881 datetime = tracker_date_new_from_iso8601 (object, NULL);
1882 if (!datetime) {
1883 g_critical ("%s: error parsing nrl:lastModified: %s",
1884 subject, error->message);
1885 g_error_free (error);
1886 return NULL;
1887 }
1888
1889 tracker_ontology_set_last_modified (ontology, g_date_time_to_unix (datetime));
1890 g_date_time_unref (datetime);
1891
1892 /* This one is here because lower ontology_uris is destroyed, and
1893 * else would this one's reference also be destroyed with it */
1894 ret = g_object_ref (ontology);
1895
1896 break;
1897 }
1898 }
1899
1900 g_hash_table_unref (ontology_uris);
1901 g_object_unref (reader);
1902
1903 if (error) {
1904 g_critical ("Turtle parse error: %s", error->message);
1905 g_error_free (error);
1906 }
1907
1908 if (ret == NULL) {
1909 gchar *uri = g_file_get_uri (file);
1910 g_critical ("Ontology file has no nrl:lastModified header: %s", uri);
1911 g_free (uri);
1912 }
1913
1914 return ret;
1915 }
1916
1917 static void
tracker_data_ontology_process_statement(TrackerDataManager * manager,const gchar * subject,const gchar * predicate,const gchar * object,gboolean in_update)1918 tracker_data_ontology_process_statement (TrackerDataManager *manager,
1919 const gchar *subject,
1920 const gchar *predicate,
1921 const gchar *object,
1922 gboolean in_update)
1923 {
1924 TrackerProperty *property;
1925 GError *error = NULL;
1926 GBytes *bytes;
1927
1928 if (g_strcmp0 (predicate, RDF_TYPE) == 0) {
1929 if (g_strcmp0 (object, RDFS_CLASS) == 0) {
1930 TrackerClass *class;
1931
1932 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
1933
1934 if (class && tracker_class_get_is_new (class) != in_update) {
1935 return;
1936 }
1937 } else if (g_strcmp0 (object, RDF_PROPERTY) == 0) {
1938 TrackerProperty *prop;
1939
1940 prop = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1941
1942 if (prop && tracker_property_get_is_new (prop) != in_update) {
1943 return;
1944 }
1945 } else if (g_strcmp0 (object, TRACKER_PREFIX_NRL "Namespace") == 0) {
1946 TrackerNamespace *namespace;
1947
1948 namespace = tracker_ontologies_get_namespace_by_uri (manager->ontologies, subject);
1949
1950 if (namespace && tracker_namespace_get_is_new (namespace) != in_update) {
1951 return;
1952 }
1953 } else if (g_strcmp0 (object, TRACKER_PREFIX_NRL "Ontology") == 0) {
1954 TrackerOntology *ontology;
1955
1956 ontology = tracker_ontologies_get_ontology_by_uri (manager->ontologies, subject);
1957
1958 if (ontology && tracker_ontology_get_is_new (ontology) != in_update) {
1959 return;
1960 }
1961 }
1962 } else if (g_strcmp0 (predicate, RDFS_SUB_CLASS_OF) == 0) {
1963 TrackerClass *class;
1964
1965 class = tracker_ontologies_get_class_by_uri (manager->ontologies, subject);
1966
1967 if (class && tracker_class_get_is_new (class) != in_update) {
1968 return;
1969 }
1970 } else if (g_strcmp0 (predicate, RDFS_SUB_PROPERTY_OF) == 0 ||
1971 g_strcmp0 (predicate, RDFS_DOMAIN) == 0 ||
1972 g_strcmp0 (predicate, RDFS_RANGE) == 0 ||
1973 /* g_strcmp0 (predicate, NRL_MAX_CARDINALITY) == 0 || */
1974 g_strcmp0 (predicate, TRACKER_PREFIX_NRL "indexed") == 0 ||
1975 g_strcmp0 (predicate, TRACKER_PREFIX_NRL "fulltextIndexed") == 0) {
1976 TrackerProperty *prop;
1977
1978 prop = tracker_ontologies_get_property_by_uri (manager->ontologies, subject);
1979
1980 if (prop && tracker_property_get_is_new (prop) != in_update) {
1981 return;
1982 }
1983 } else if (g_strcmp0 (predicate, TRACKER_PREFIX_NRL "prefix") == 0) {
1984 TrackerNamespace *namespace;
1985
1986 namespace = tracker_ontologies_get_namespace_by_uri (manager->ontologies, subject);
1987
1988 if (namespace && tracker_namespace_get_is_new (namespace) != in_update) {
1989 return;
1990 }
1991 } else if (g_strcmp0 (predicate, NRL_LAST_MODIFIED) == 0) {
1992 TrackerOntology *ontology;
1993
1994 ontology = tracker_ontologies_get_ontology_by_uri (manager->ontologies, subject);
1995
1996 if (ontology && tracker_ontology_get_is_new (ontology) != in_update) {
1997 return;
1998 }
1999 }
2000
2001 bytes = g_bytes_new (object, strlen (object) + 1);
2002 property = tracker_ontologies_get_property_by_uri (manager->ontologies, predicate);
2003
2004 if (tracker_property_get_is_new (property) ||
2005 tracker_property_get_multiple_values (property)) {
2006 tracker_data_insert_statement (manager->data_update, NULL,
2007 subject, predicate, bytes,
2008 &error);
2009 } else {
2010 tracker_data_update_statement (manager->data_update, NULL,
2011 subject, predicate, bytes,
2012 &error);
2013 }
2014
2015 g_bytes_unref (bytes);
2016
2017 if (error != NULL) {
2018 g_critical ("%s", error->message);
2019 g_error_free (error);
2020 return;
2021 }
2022 }
2023
2024 static void
import_ontology_file(TrackerDataManager * manager,GFile * file,gboolean in_update)2025 import_ontology_file (TrackerDataManager *manager,
2026 GFile *file,
2027 gboolean in_update)
2028 {
2029 const gchar *subject, *predicate, *object;
2030 GError *error = NULL;
2031 TrackerTurtleReader* reader;
2032
2033 reader = tracker_turtle_reader_new_for_file (file, &error);
2034
2035 if (error != NULL) {
2036 g_critical ("%s", error->message);
2037 g_error_free (error);
2038 return;
2039 }
2040
2041 while (tracker_turtle_reader_next (reader,
2042 &subject, &predicate, &object,
2043 NULL, NULL, &error)) {
2044 tracker_data_ontology_process_statement (manager,
2045 subject, predicate, object,
2046 in_update);
2047 }
2048
2049 g_object_unref (reader);
2050
2051 if (error) {
2052 g_critical ("%s", error->message);
2053 g_error_free (error);
2054 }
2055 }
2056
2057 static void
class_add_super_classes_from_db(TrackerDBInterface * iface,TrackerDataManager * manager,TrackerClass * class)2058 class_add_super_classes_from_db (TrackerDBInterface *iface,
2059 TrackerDataManager *manager,
2060 TrackerClass *class)
2061 {
2062 TrackerDBStatement *stmt;
2063 TrackerDBCursor *cursor;
2064 GError *error = NULL;
2065
2066 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
2067 "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdfs:subClassOf\") "
2068 "FROM \"rdfs:Class_rdfs:subClassOf\" "
2069 "WHERE ID = (SELECT ID FROM Resource WHERE Uri = ?)");
2070
2071 if (!stmt) {
2072 g_critical ("%s", error->message);
2073 g_error_free (error);
2074 return;
2075 }
2076
2077 tracker_db_statement_bind_text (stmt, 0, tracker_class_get_uri (class));
2078 cursor = tracker_db_statement_start_cursor (stmt, NULL);
2079 g_object_unref (stmt);
2080
2081 if (cursor) {
2082 while (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
2083 TrackerClass *super_class;
2084 const gchar *super_class_uri;
2085
2086 super_class_uri = tracker_db_cursor_get_string (cursor, 0, NULL);
2087 super_class = tracker_ontologies_get_class_by_uri (manager->ontologies, super_class_uri);
2088 tracker_class_add_super_class (class, super_class);
2089 }
2090
2091 g_object_unref (cursor);
2092 }
2093 }
2094
2095
2096 static void
class_add_domain_indexes_from_db(TrackerDBInterface * iface,TrackerDataManager * manager,TrackerClass * class)2097 class_add_domain_indexes_from_db (TrackerDBInterface *iface,
2098 TrackerDataManager *manager,
2099 TrackerClass *class)
2100 {
2101 TrackerDBStatement *stmt;
2102 TrackerDBCursor *cursor;
2103 GError *error = NULL;
2104
2105 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
2106 "SELECT (SELECT Uri FROM Resource WHERE ID = \"nrl:domainIndex\") "
2107 "FROM \"rdfs:Class_nrl:domainIndex\" "
2108 "WHERE ID = (SELECT ID FROM Resource WHERE Uri = ?)");
2109
2110 if (!stmt) {
2111 g_critical ("%s", error->message);
2112 g_error_free (error);
2113 return;
2114 }
2115
2116 tracker_db_statement_bind_text (stmt, 0, tracker_class_get_uri (class));
2117 cursor = tracker_db_statement_start_cursor (stmt, NULL);
2118 g_object_unref (stmt);
2119
2120 if (cursor) {
2121 while (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
2122 TrackerProperty *domain_index;
2123 const gchar *domain_index_uri;
2124
2125 domain_index_uri = tracker_db_cursor_get_string (cursor, 0, NULL);
2126 domain_index = tracker_ontologies_get_property_by_uri (manager->ontologies, domain_index_uri);
2127 tracker_class_add_domain_index (class, domain_index);
2128 tracker_property_add_domain_index (domain_index, class);
2129 }
2130
2131 g_object_unref (cursor);
2132 }
2133 }
2134
2135 static void
property_add_super_properties_from_db(TrackerDBInterface * iface,TrackerDataManager * manager,TrackerProperty * property)2136 property_add_super_properties_from_db (TrackerDBInterface *iface,
2137 TrackerDataManager *manager,
2138 TrackerProperty *property)
2139 {
2140 TrackerDBStatement *stmt;
2141 TrackerDBCursor *cursor;
2142 GError *error = NULL;
2143
2144 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
2145 "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdfs:subPropertyOf\") "
2146 "FROM \"rdf:Property_rdfs:subPropertyOf\" "
2147 "WHERE ID = (SELECT ID FROM Resource WHERE Uri = ?)");
2148
2149 if (!stmt) {
2150 g_critical ("%s", error->message);
2151 g_error_free (error);
2152 return;
2153 }
2154
2155 tracker_db_statement_bind_text (stmt, 0, tracker_property_get_uri (property));
2156 cursor = tracker_db_statement_start_cursor (stmt, NULL);
2157 g_object_unref (stmt);
2158
2159 if (cursor) {
2160 while (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
2161 TrackerProperty *super_property;
2162 const gchar *super_property_uri;
2163
2164 super_property_uri = tracker_db_cursor_get_string (cursor, 0, NULL);
2165 super_property = tracker_ontologies_get_property_by_uri (manager->ontologies, super_property_uri);
2166 tracker_property_add_super_property (property, super_property);
2167 }
2168
2169 g_object_unref (cursor);
2170 }
2171 }
2172
2173 static void
db_get_static_data(TrackerDBInterface * iface,TrackerDataManager * manager,GError ** error)2174 db_get_static_data (TrackerDBInterface *iface,
2175 TrackerDataManager *manager,
2176 GError **error)
2177 {
2178 TrackerDBStatement *stmt;
2179 TrackerDBCursor *cursor = NULL;
2180 TrackerClass **classes;
2181 guint n_classes, i;
2182 GError *internal_error = NULL;
2183
2184 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &internal_error,
2185 "SELECT (SELECT Uri FROM Resource WHERE ID = \"nrl:Ontology\".ID), "
2186 " \"nrl:lastModified\" "
2187 "FROM \"nrl:Ontology\"");
2188
2189 if (stmt) {
2190 cursor = tracker_db_statement_start_cursor (stmt, &internal_error);
2191 g_object_unref (stmt);
2192 }
2193
2194 if (cursor) {
2195 while (tracker_db_cursor_iter_next (cursor, NULL, &internal_error)) {
2196 TrackerOntology *ontology;
2197 const gchar *uri;
2198 time_t last_mod;
2199
2200 ontology = tracker_ontology_new ();
2201 tracker_ontology_set_ontologies (ontology, manager->ontologies);
2202
2203 uri = tracker_db_cursor_get_string (cursor, 0, NULL);
2204 last_mod = (time_t) tracker_db_cursor_get_int (cursor, 1);
2205
2206 tracker_ontology_set_is_new (ontology, FALSE);
2207 tracker_ontology_set_uri (ontology, uri);
2208 tracker_ontology_set_last_modified (ontology, last_mod);
2209 tracker_ontologies_add_ontology (manager->ontologies, ontology);
2210
2211 g_object_unref (ontology);
2212 }
2213
2214 g_object_unref (cursor);
2215 cursor = NULL;
2216 }
2217
2218 if (internal_error) {
2219 g_propagate_error (error, internal_error);
2220 return;
2221 }
2222
2223 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &internal_error,
2224 "SELECT (SELECT Uri FROM Resource WHERE ID = \"nrl:Namespace\".ID), "
2225 "\"nrl:prefix\" "
2226 "FROM \"nrl:Namespace\"");
2227
2228 if (stmt) {
2229 cursor = tracker_db_statement_start_cursor (stmt, &internal_error);
2230 g_object_unref (stmt);
2231 }
2232
2233 if (cursor) {
2234 while (tracker_db_cursor_iter_next (cursor, NULL, &internal_error)) {
2235 TrackerNamespace *namespace;
2236 const gchar *uri, *prefix;
2237
2238 namespace = tracker_namespace_new (FALSE);
2239
2240 uri = tracker_db_cursor_get_string (cursor, 0, NULL);
2241 prefix = tracker_db_cursor_get_string (cursor, 1, NULL);
2242
2243 tracker_namespace_set_ontologies (namespace, manager->ontologies);
2244 tracker_namespace_set_is_new (namespace, FALSE);
2245 tracker_namespace_set_uri (namespace, uri);
2246 tracker_namespace_set_prefix (namespace, prefix);
2247 tracker_ontologies_add_namespace (manager->ontologies, namespace);
2248
2249 g_object_unref (namespace);
2250
2251 }
2252
2253 g_object_unref (cursor);
2254 cursor = NULL;
2255 }
2256
2257 if (internal_error) {
2258 g_propagate_error (error, internal_error);
2259 return;
2260 }
2261
2262 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &internal_error,
2263 "SELECT \"rdfs:Class\".ID, "
2264 "(SELECT Uri FROM Resource WHERE ID = \"rdfs:Class\".ID), "
2265 "\"nrl:notify\" "
2266 "FROM \"rdfs:Class\" ORDER BY ID");
2267
2268 if (stmt) {
2269 cursor = tracker_db_statement_start_cursor (stmt, &internal_error);
2270 g_object_unref (stmt);
2271 }
2272
2273 if (cursor) {
2274 while (tracker_db_cursor_iter_next (cursor, NULL, &internal_error)) {
2275 TrackerClass *class;
2276 const gchar *uri;
2277 gint id;
2278 GValue value = { 0 };
2279 gboolean notify;
2280
2281 class = tracker_class_new (FALSE);
2282
2283 id = tracker_db_cursor_get_int (cursor, 0);
2284 uri = tracker_db_cursor_get_string (cursor, 1, NULL);
2285
2286 tracker_db_cursor_get_value (cursor, 2, &value);
2287
2288 if (G_VALUE_TYPE (&value) != 0) {
2289 notify = (g_value_get_int64 (&value) == 1);
2290 g_value_unset (&value);
2291 } else {
2292 /* NULL */
2293 notify = FALSE;
2294 }
2295
2296 tracker_class_set_ontologies (class, manager->ontologies);
2297 tracker_class_set_db_schema_changed (class, FALSE);
2298 tracker_class_set_is_new (class, FALSE);
2299 tracker_class_set_uri (class, uri);
2300 tracker_class_set_notify (class, notify);
2301
2302 class_add_super_classes_from_db (iface, manager, class);
2303
2304 /* We add domain indexes later , we first need to load the properties */
2305
2306 tracker_ontologies_add_class (manager->ontologies, class);
2307 tracker_ontologies_add_id_uri_pair (manager->ontologies, id, uri);
2308 tracker_class_set_id (class, id);
2309
2310 g_object_unref (class);
2311 }
2312
2313 g_object_unref (cursor);
2314 cursor = NULL;
2315 }
2316
2317 if (internal_error) {
2318 g_propagate_error (error, internal_error);
2319 return;
2320 }
2321
2322 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &internal_error,
2323 "SELECT \"rdf:Property\".ID, (SELECT Uri FROM Resource WHERE ID = \"rdf:Property\".ID), "
2324 "(SELECT Uri FROM Resource WHERE ID = \"rdfs:domain\"), "
2325 "(SELECT Uri FROM Resource WHERE ID = \"rdfs:range\"), "
2326 "\"nrl:maxCardinality\", "
2327 "\"nrl:indexed\", "
2328 "(SELECT Uri FROM Resource WHERE ID = \"nrl:secondaryIndex\"), "
2329 "\"nrl:fulltextIndexed\", "
2330 "(SELECT 1 FROM \"rdfs:Resource_rdf:type\" WHERE ID = \"rdf:Property\".ID AND "
2331 "\"rdf:type\" = (SELECT ID FROM Resource WHERE Uri = '" NRL_INVERSE_FUNCTIONAL_PROPERTY "')) "
2332 "FROM \"rdf:Property\" ORDER BY ID");
2333
2334 if (stmt) {
2335 cursor = tracker_db_statement_start_cursor (stmt, &internal_error);
2336 g_object_unref (stmt);
2337 }
2338
2339 if (cursor) {
2340 while (tracker_db_cursor_iter_next (cursor, NULL, &internal_error)) {
2341 GValue value = { 0 };
2342 TrackerProperty *property;
2343 const gchar *uri, *domain_uri, *range_uri, *secondary_index_uri;
2344 gboolean multi_valued, indexed, fulltext_indexed;
2345 gboolean is_inverse_functional_property;
2346 gint id;
2347
2348 property = tracker_property_new (FALSE);
2349
2350 id = tracker_db_cursor_get_int (cursor, 0);
2351 uri = tracker_db_cursor_get_string (cursor, 1, NULL);
2352 domain_uri = tracker_db_cursor_get_string (cursor, 2, NULL);
2353 range_uri = tracker_db_cursor_get_string (cursor, 3, NULL);
2354
2355 tracker_db_cursor_get_value (cursor, 4, &value);
2356
2357 if (G_VALUE_TYPE (&value) != 0) {
2358 multi_valued = (g_value_get_int64 (&value) > 1);
2359 g_value_unset (&value);
2360 } else {
2361 /* nrl:maxCardinality not set
2362 not limited to single value */
2363 multi_valued = TRUE;
2364 }
2365
2366 tracker_db_cursor_get_value (cursor, 5, &value);
2367
2368 if (G_VALUE_TYPE (&value) != 0) {
2369 indexed = (g_value_get_int64 (&value) == 1);
2370 g_value_unset (&value);
2371 } else {
2372 /* NULL */
2373 indexed = FALSE;
2374 }
2375
2376 secondary_index_uri = tracker_db_cursor_get_string (cursor, 6, NULL);
2377
2378 tracker_db_cursor_get_value (cursor, 7, &value);
2379
2380 if (G_VALUE_TYPE (&value) != 0) {
2381 fulltext_indexed = (g_value_get_int64 (&value) == 1);
2382 g_value_unset (&value);
2383 } else {
2384 /* NULL */
2385 fulltext_indexed = FALSE;
2386 }
2387
2388 /* NRL_INVERSE_FUNCTIONAL_PROPERTY column */
2389 tracker_db_cursor_get_value (cursor, 8, &value);
2390
2391 if (G_VALUE_TYPE (&value) != 0) {
2392 is_inverse_functional_property = TRUE;
2393 g_value_unset (&value);
2394 } else {
2395 /* NULL */
2396 is_inverse_functional_property = FALSE;
2397 }
2398
2399 tracker_property_set_ontologies (property, manager->ontologies);
2400 tracker_property_set_is_new_domain_index (property, tracker_ontologies_get_class_by_uri (manager->ontologies, domain_uri), FALSE);
2401 tracker_property_set_is_new (property, FALSE);
2402 tracker_property_set_cardinality_changed (property, FALSE);
2403 tracker_property_set_uri (property, uri);
2404 tracker_property_set_id (property, id);
2405 tracker_property_set_domain (property, tracker_ontologies_get_class_by_uri (manager->ontologies, domain_uri));
2406 tracker_property_set_range (property, tracker_ontologies_get_class_by_uri (manager->ontologies, range_uri));
2407 tracker_property_set_multiple_values (property, multi_valued);
2408 tracker_property_set_orig_multiple_values (property, multi_valued);
2409 tracker_property_set_indexed (property, indexed);
2410
2411 tracker_property_set_db_schema_changed (property, FALSE);
2412
2413 if (secondary_index_uri) {
2414 tracker_property_set_secondary_index (property, tracker_ontologies_get_property_by_uri (manager->ontologies, secondary_index_uri));
2415 }
2416
2417 tracker_property_set_orig_fulltext_indexed (property, fulltext_indexed);
2418 tracker_property_set_fulltext_indexed (property, fulltext_indexed);
2419 tracker_property_set_is_inverse_functional_property (property, is_inverse_functional_property);
2420
2421 /* super properties are only used in updates, never for queries */
2422 if ((tracker_db_manager_get_flags (manager->db_manager, NULL, NULL) & TRACKER_DB_MANAGER_READONLY) == 0) {
2423 property_add_super_properties_from_db (iface, manager, property);
2424 }
2425
2426 tracker_ontologies_add_property (manager->ontologies, property);
2427 tracker_ontologies_add_id_uri_pair (manager->ontologies, id, uri);
2428
2429 g_object_unref (property);
2430
2431 }
2432
2433 g_object_unref (cursor);
2434 cursor = NULL;
2435 }
2436
2437 /* Now that the properties are loaded we can do this foreach class */
2438 classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
2439 for (i = 0; i < n_classes; i++) {
2440 class_add_domain_indexes_from_db (iface, manager, classes[i]);
2441 }
2442
2443 if (internal_error) {
2444 g_propagate_error (error, internal_error);
2445 return;
2446 }
2447 }
2448
2449 static void
insert_uri_in_resource_table(TrackerDataManager * manager,TrackerDBInterface * iface,const gchar * uri,gint id,GError ** error)2450 insert_uri_in_resource_table (TrackerDataManager *manager,
2451 TrackerDBInterface *iface,
2452 const gchar *uri,
2453 gint id,
2454 GError **error)
2455 {
2456 TrackerDBStatement *stmt;
2457 GError *internal_error = NULL;
2458
2459 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
2460 &internal_error,
2461 "INSERT OR IGNORE "
2462 "INTO main.Resource "
2463 "(ID, Uri) "
2464 "VALUES (?, ?)");
2465 if (internal_error) {
2466 g_propagate_error (error, internal_error);
2467 return;
2468 }
2469
2470 tracker_db_statement_bind_int (stmt, 0, id);
2471 tracker_db_statement_bind_text (stmt, 1, uri);
2472 tracker_db_statement_execute (stmt, &internal_error);
2473
2474 if (internal_error) {
2475 g_object_unref (stmt);
2476 g_propagate_error (error, internal_error);
2477 return;
2478 }
2479
2480 g_object_unref (stmt);
2481
2482 }
2483
2484 static void
range_change_for(TrackerProperty * property,GString * in_col_sql,GString * sel_col_sql,const gchar * field_name)2485 range_change_for (TrackerProperty *property,
2486 GString *in_col_sql,
2487 GString *sel_col_sql,
2488 const gchar *field_name)
2489 {
2490 /* TODO: TYPE_RESOURCE and TYPE_DATETIME are completely unhandled atm, we
2491 * should forbid conversion from anything to resource or datetime in error
2492 * handling earlier */
2493
2494 g_string_append_printf (in_col_sql, ", \"%s\"", field_name);
2495
2496 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_INTEGER ||
2497 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DOUBLE) {
2498 g_string_append_printf (sel_col_sql, ", \"%s\" + 0", field_name);
2499 } else if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) {
2500
2501 /* TODO (see above) */
2502
2503 g_string_append_printf (sel_col_sql, ", \"%s\"", field_name);
2504 } else if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_BOOLEAN) {
2505 g_string_append_printf (sel_col_sql, ", \"%s\" != 0", field_name);
2506 } else {
2507 g_string_append_printf (sel_col_sql, ", \"%s\"", field_name);
2508 }
2509 }
2510
2511 static void
create_decomposed_metadata_property_table(TrackerDBInterface * iface,TrackerProperty * property,const gchar * database,const gchar * service_name,TrackerClass * service,const gchar ** sql_type_for_single_value,gboolean in_update,gboolean in_change,GError ** error)2512 create_decomposed_metadata_property_table (TrackerDBInterface *iface,
2513 TrackerProperty *property,
2514 const gchar *database,
2515 const gchar *service_name,
2516 TrackerClass *service,
2517 const gchar **sql_type_for_single_value,
2518 gboolean in_update,
2519 gboolean in_change,
2520 GError **error)
2521 {
2522 GError *internal_error = NULL;
2523 const char *field_name;
2524 const char *sql_type;
2525 gboolean not_single, datetime;
2526
2527 field_name = tracker_property_get_name (property);
2528
2529 not_single = !sql_type_for_single_value;
2530
2531 switch (tracker_property_get_data_type (property)) {
2532 case TRACKER_PROPERTY_TYPE_STRING:
2533 case TRACKER_PROPERTY_TYPE_LANGSTRING:
2534 sql_type = "TEXT";
2535 break;
2536 case TRACKER_PROPERTY_TYPE_INTEGER:
2537 case TRACKER_PROPERTY_TYPE_BOOLEAN:
2538 case TRACKER_PROPERTY_TYPE_DATE:
2539 case TRACKER_PROPERTY_TYPE_DATETIME:
2540 case TRACKER_PROPERTY_TYPE_RESOURCE:
2541 sql_type = "INTEGER";
2542 break;
2543 case TRACKER_PROPERTY_TYPE_DOUBLE:
2544 sql_type = "REAL";
2545 break;
2546 default:
2547 sql_type = "";
2548 break;
2549 }
2550
2551 datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
2552
2553 if (!in_update || (in_update && (tracker_property_get_is_new (property) ||
2554 tracker_property_get_is_new_domain_index (property, service) ||
2555 tracker_property_get_cardinality_changed (property) ||
2556 tracker_property_get_db_schema_changed (property)))) {
2557 if (not_single || tracker_property_get_multiple_values (property)) {
2558 GString *sql = NULL;
2559 GString *in_col_sql = NULL;
2560 GString *sel_col_sql = NULL;
2561
2562 /* multiple values */
2563
2564 if (in_update) {
2565 TRACKER_NOTE (ONTOLOGY_CHANGES,
2566 g_message ("Altering database for class '%s' property '%s': multi value",
2567 service_name, field_name));
2568 }
2569
2570 if (in_change && !tracker_property_get_is_new (property) && !tracker_property_get_cardinality_changed (property)) {
2571 TRACKER_NOTE (ONTOLOGY_CHANGES,
2572 g_message ("Drop index: DROP INDEX IF EXISTS \"%s_%s_ID\"\nRename: ALTER TABLE \"%s_%s\" RENAME TO \"%s_%s_TEMP\"",
2573 service_name, field_name, service_name, field_name, service_name, field_name));
2574
2575 tracker_db_interface_execute_query (iface, &internal_error,
2576 "DROP INDEX IF EXISTS \"%s\".\"%s_%s_ID\"",
2577 database,
2578 service_name,
2579 field_name);
2580
2581 if (internal_error) {
2582 g_propagate_error (error, internal_error);
2583 goto error_out;
2584 }
2585
2586 tracker_db_interface_execute_query (iface, &internal_error,
2587 "ALTER TABLE \"%s\".\"%s_%s\" RENAME TO \"%s_%s_TEMP\"",
2588 database, service_name, field_name,
2589 service_name, field_name);
2590
2591 if (internal_error) {
2592 g_propagate_error (error, internal_error);
2593 goto error_out;
2594 }
2595 } else if (in_change && tracker_property_get_cardinality_changed (property)) {
2596 /* We should be dropping all indices colliding with the new table name */
2597 tracker_db_interface_execute_query (iface, &internal_error,
2598 "DROP INDEX IF EXISTS \"%s\".\"%s_%s\"",
2599 database,
2600 service_name,
2601 field_name);
2602 }
2603
2604 sql = g_string_new ("");
2605 g_string_append_printf (sql,
2606 "CREATE TABLE \"%s\".\"%s_%s\" ("
2607 "ID INTEGER NOT NULL, "
2608 "\"%s\" %s NOT NULL",
2609 database,
2610 service_name,
2611 field_name,
2612 field_name,
2613 sql_type);
2614
2615 if (in_change && !tracker_property_get_is_new (property)) {
2616 in_col_sql = g_string_new ("ID");
2617 sel_col_sql = g_string_new ("ID");
2618
2619 range_change_for (property, in_col_sql, sel_col_sql, field_name);
2620 }
2621
2622 tracker_db_interface_execute_query (iface, &internal_error,
2623 "%s)", sql->str);
2624
2625 if (internal_error) {
2626 g_propagate_error (error, internal_error);
2627 goto error_out;
2628 }
2629
2630 /* multiple values */
2631 if (tracker_property_get_indexed (property)) {
2632 /* use different UNIQUE index for properties whose
2633 * value should be indexed to minimize index size */
2634 set_index_for_multi_value_property (iface, database, service_name, field_name, TRUE, TRUE,
2635 datetime,
2636 &internal_error);
2637 if (internal_error) {
2638 g_propagate_error (error, internal_error);
2639 goto error_out;
2640 }
2641 } else {
2642 set_index_for_multi_value_property (iface, database, service_name, field_name, FALSE, TRUE,
2643 datetime,
2644 &internal_error);
2645 /* we still have to include the property value in
2646 * the unique index for proper constraints */
2647 if (internal_error) {
2648 g_propagate_error (error, internal_error);
2649 goto error_out;
2650 }
2651 }
2652
2653 if (in_change && !tracker_property_get_is_new (property) &&
2654 !tracker_property_get_cardinality_changed (property) && in_col_sql && sel_col_sql) {
2655 gchar *query;
2656
2657 query = g_strdup_printf ("INSERT INTO \"%s\".\"%s_%s\"(%s) "
2658 "SELECT %s FROM \"%s\".\"%s_%s_TEMP\"",
2659 database, service_name, field_name, in_col_sql->str,
2660 sel_col_sql->str, database, service_name, field_name);
2661
2662 tracker_db_interface_execute_query (iface, &internal_error, "%s", query);
2663
2664 if (internal_error) {
2665 g_free (query);
2666 g_propagate_error (error, internal_error);
2667 goto error_out;
2668 }
2669
2670 g_free (query);
2671 tracker_db_interface_execute_query (iface, &internal_error, "DROP TABLE \"%s\".\"%s_%s_TEMP\"",
2672 database, service_name, field_name);
2673
2674 if (internal_error) {
2675 g_propagate_error (error, internal_error);
2676 goto error_out;
2677 }
2678 }
2679
2680 /* multiple values */
2681 if (tracker_property_get_indexed (property)) {
2682 /* use different UNIQUE index for properties whose
2683 * value should be indexed to minimize index size */
2684 set_index_for_multi_value_property (iface, database, service_name, field_name, TRUE, TRUE,
2685 datetime,
2686 &internal_error);
2687 if (internal_error) {
2688 g_propagate_error (error, internal_error);
2689 goto error_out;
2690 }
2691 } else {
2692 set_index_for_multi_value_property (iface, database, service_name, field_name, FALSE, TRUE,
2693 datetime,
2694 &internal_error);
2695 if (internal_error) {
2696 g_propagate_error (error, internal_error);
2697 goto error_out;
2698 }
2699 /* we still have to include the property value in
2700 * the unique index for proper constraints */
2701 }
2702
2703 error_out:
2704
2705 if (sql) {
2706 g_string_free (sql, TRUE);
2707 }
2708
2709 if (sel_col_sql) {
2710 g_string_free (sel_col_sql, TRUE);
2711 }
2712
2713 if (in_col_sql) {
2714 g_string_free (in_col_sql, TRUE);
2715 }
2716 } else if (sql_type_for_single_value) {
2717 *sql_type_for_single_value = sql_type;
2718 }
2719 }
2720 }
2721
2722 static gboolean
is_a_domain_index(TrackerProperty ** domain_indexes,TrackerProperty * property)2723 is_a_domain_index (TrackerProperty **domain_indexes, TrackerProperty *property)
2724 {
2725 while (*domain_indexes) {
2726
2727 if (*domain_indexes == property) {
2728 return TRUE;
2729 }
2730
2731 domain_indexes++;
2732 }
2733
2734 return FALSE;
2735 }
2736
2737 static void
copy_from_domain_to_domain_index(TrackerDBInterface * iface,const gchar * database,TrackerProperty * domain_index,const gchar * column_name,const gchar * column_suffix,TrackerClass * dest_domain,GError ** error)2738 copy_from_domain_to_domain_index (TrackerDBInterface *iface,
2739 const gchar *database,
2740 TrackerProperty *domain_index,
2741 const gchar *column_name,
2742 const gchar *column_suffix,
2743 TrackerClass *dest_domain,
2744 GError **error)
2745 {
2746 GError *internal_error = NULL;
2747 TrackerClass *source_domain;
2748 const gchar *source_name, *dest_name;
2749 gchar *query;
2750
2751 source_domain = tracker_property_get_domain (domain_index);
2752 source_name = tracker_class_get_name (source_domain);
2753 dest_name = tracker_class_get_name (dest_domain);
2754
2755 query = g_strdup_printf ("UPDATE \"%s\".\"%s\" SET \"%s%s\"=("
2756 "SELECT \"%s%s\" FROM \"%s\".\"%s\" "
2757 "WHERE \"%s\".ID = \"%s\".ID)",
2758 database,
2759 dest_name,
2760 column_name,
2761 column_suffix ? column_suffix : "",
2762 column_name,
2763 column_suffix ? column_suffix : "",
2764 database,
2765 source_name,
2766 source_name,
2767 dest_name);
2768
2769 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Copying: '%s'", query));
2770
2771 tracker_db_interface_execute_query (iface, &internal_error, "%s", query);
2772
2773 if (internal_error) {
2774 g_propagate_error (error, internal_error);
2775 }
2776
2777 g_free (query);
2778 }
2779
2780 typedef struct {
2781 TrackerProperty *prop;
2782 const gchar *field_name;
2783 const gchar *suffix;
2784 } ScheduleCopy;
2785
2786 static void
schedule_copy(GPtrArray * schedule,TrackerProperty * prop,const gchar * field_name,const gchar * suffix)2787 schedule_copy (GPtrArray *schedule,
2788 TrackerProperty *prop,
2789 const gchar *field_name,
2790 const gchar *suffix)
2791 {
2792 ScheduleCopy *sched = g_new0 (ScheduleCopy, 1);
2793 sched->prop = prop;
2794 sched->field_name = field_name,
2795 sched->suffix = suffix;
2796 g_ptr_array_add (schedule, sched);
2797 }
2798
2799 static void
create_decomposed_metadata_tables(TrackerDataManager * manager,TrackerDBInterface * iface,const gchar * database,TrackerClass * service,gboolean in_update,gboolean in_change,GError ** error)2800 create_decomposed_metadata_tables (TrackerDataManager *manager,
2801 TrackerDBInterface *iface,
2802 const gchar *database,
2803 TrackerClass *service,
2804 gboolean in_update,
2805 gboolean in_change,
2806 GError **error)
2807 {
2808 const char *service_name;
2809 GString *create_sql = NULL;
2810 GString *in_col_sql = NULL;
2811 GString *sel_col_sql = NULL;
2812 TrackerProperty **properties, *property, **domain_indexes;
2813 GSList *class_properties = NULL, *field_it;
2814 guint i, n_props;
2815 gboolean in_alter = in_update;
2816 GError *internal_error = NULL;
2817 GPtrArray *copy_schedule = NULL;
2818
2819 g_return_if_fail (TRACKER_IS_CLASS (service));
2820
2821 service_name = tracker_class_get_name (service);
2822
2823 g_return_if_fail (service_name != NULL);
2824
2825 if (g_str_has_prefix (service_name, "xsd:")) {
2826 /* xsd classes do not derive from rdfs:Resource and do not need separate tables */
2827 return;
2828 }
2829
2830 if (in_change && !tracker_class_get_is_new (service)) {
2831 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Rename: ALTER TABLE \"%s\" RENAME TO \"%s_TEMP\"", service_name, service_name));
2832 tracker_db_interface_execute_query (iface, &internal_error,
2833 "ALTER TABLE \"%s\".\"%s\" RENAME TO \"%s_TEMP\"",
2834 database, service_name, service_name);
2835 in_col_sql = g_string_new ("ID");
2836 sel_col_sql = g_string_new ("ID");
2837 if (internal_error) {
2838 g_propagate_error (error, internal_error);
2839 goto error_out;
2840 }
2841 }
2842
2843 if (in_change || !in_update || (in_update && tracker_class_get_is_new (service))) {
2844 if (in_update)
2845 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Altering database with new class '%s' (create)", service_name));
2846 in_alter = FALSE;
2847 create_sql = g_string_new ("");
2848 g_string_append_printf (create_sql, "CREATE TABLE \"%s\".\"%s\" (ID INTEGER NOT NULL PRIMARY KEY",
2849 database, service_name);
2850 }
2851
2852 properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
2853 domain_indexes = tracker_class_get_domain_indexes (service);
2854
2855 for (i = 0; i < n_props; i++) {
2856 gboolean is_domain_index, datetime;
2857
2858 property = properties[i];
2859 is_domain_index = is_a_domain_index (domain_indexes, property);
2860 datetime = tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME;
2861
2862 if (tracker_property_get_domain (property) == service || is_domain_index) {
2863 const gchar *sql_type_for_single_value = NULL;
2864 gboolean put_change;
2865 const gchar *field_name;
2866
2867 create_decomposed_metadata_property_table (iface, property,
2868 database,
2869 service_name,
2870 service,
2871 &sql_type_for_single_value,
2872 in_alter,
2873 in_change,
2874 &internal_error);
2875
2876 if (internal_error) {
2877 g_propagate_error (error, internal_error);
2878 goto error_out;
2879 }
2880
2881 field_name = tracker_property_get_name (property);
2882
2883 if (sql_type_for_single_value) {
2884 /* single value */
2885
2886 if (in_update) {
2887 TRACKER_NOTE (ONTOLOGY_CHANGES,
2888 g_message ("%sAltering database for class '%s' property '%s': single value (%s)",
2889 in_alter ? "" : " ",
2890 service_name,
2891 field_name,
2892 in_alter ? "alter" : "create"));
2893 }
2894
2895 if (!in_alter) {
2896 put_change = TRUE;
2897 class_properties = g_slist_prepend (class_properties, property);
2898
2899 g_string_append_printf (create_sql, ", \"%s\" %s",
2900 field_name,
2901 sql_type_for_single_value);
2902
2903 if (!copy_schedule) {
2904 copy_schedule = g_ptr_array_new_with_free_func (g_free);
2905 }
2906
2907 if (is_domain_index && tracker_property_get_is_new_domain_index (property, service)) {
2908 schedule_copy (copy_schedule, property, field_name, NULL);
2909 }
2910
2911 if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0 ||
2912 g_ascii_strcasecmp (sql_type_for_single_value, "BLOB") == 0) {
2913 g_string_append (create_sql, " COLLATE " TRACKER_COLLATION_NAME);
2914 }
2915
2916 if (tracker_property_get_is_inverse_functional_property (property)) {
2917 g_string_append (create_sql, " UNIQUE");
2918 }
2919 } else if ((!is_domain_index && tracker_property_get_is_new (property)) ||
2920 (is_domain_index && tracker_property_get_is_new_domain_index (property, service))) {
2921 GString *alter_sql = NULL;
2922
2923 put_change = FALSE;
2924 class_properties = g_slist_prepend (class_properties, property);
2925
2926 alter_sql = g_string_new ("ALTER TABLE ");
2927 g_string_append_printf (alter_sql, "\"%s\".\"%s\" ADD COLUMN \"%s\" %s",
2928 database,
2929 service_name,
2930 field_name,
2931 sql_type_for_single_value);
2932
2933 if (g_ascii_strcasecmp (sql_type_for_single_value, "TEXT") == 0 ||
2934 g_ascii_strcasecmp (sql_type_for_single_value, "BLOB") == 0) {
2935 g_string_append (alter_sql, " COLLATE " TRACKER_COLLATION_NAME);
2936 }
2937
2938 if (tracker_property_get_is_inverse_functional_property (property)) {
2939 g_string_append (alter_sql, " UNIQUE");
2940 }
2941
2942 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Altering: '%s'", alter_sql->str));
2943 tracker_db_interface_execute_query (iface, &internal_error, "%s", alter_sql->str);
2944 if (internal_error) {
2945 g_string_free (alter_sql, TRUE);
2946 g_propagate_error (error, internal_error);
2947 goto error_out;
2948 } else if (is_domain_index) {
2949 copy_from_domain_to_domain_index (iface, database, property,
2950 field_name, NULL,
2951 service,
2952 &internal_error);
2953 if (internal_error) {
2954 g_string_free (alter_sql, TRUE);
2955 g_propagate_error (error, internal_error);
2956 goto error_out;
2957 }
2958
2959 /* This is implicit for all domain-specific-indices */
2960 set_index_for_single_value_property (iface, database, service_name,
2961 field_name, TRUE,
2962 datetime,
2963 &internal_error);
2964 if (internal_error) {
2965 g_string_free (alter_sql, TRUE);
2966 g_propagate_error (error, internal_error);
2967 goto error_out;
2968 }
2969 }
2970
2971 g_string_free (alter_sql, TRUE);
2972 } else {
2973 put_change = TRUE;
2974 }
2975
2976 if (in_change && put_change && in_col_sql && sel_col_sql) {
2977 range_change_for (property, in_col_sql, sel_col_sql, field_name);
2978 }
2979 }
2980 }
2981 }
2982
2983 if (create_sql) {
2984 g_string_append (create_sql, ")");
2985 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Creating: '%s'", create_sql->str));
2986 tracker_db_interface_execute_query (iface, &internal_error,
2987 "%s", create_sql->str);
2988 if (internal_error) {
2989 g_propagate_error (error, internal_error);
2990 goto error_out;
2991 }
2992 }
2993
2994 /* create index for single-valued fields */
2995 for (field_it = class_properties; field_it != NULL; field_it = field_it->next) {
2996 TrackerProperty *field, *secondary_index;
2997 const char *field_name;
2998 gboolean is_domain_index, datetime;
2999
3000 field = field_it->data;
3001
3002 /* This is implicit for all domain-specific-indices */
3003 is_domain_index = is_a_domain_index (domain_indexes, field);
3004
3005 if (!tracker_property_get_multiple_values (field)
3006 && (tracker_property_get_indexed (field) || is_domain_index)) {
3007
3008 field_name = tracker_property_get_name (field);
3009 datetime = tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_DATETIME;
3010
3011 secondary_index = tracker_property_get_secondary_index (field);
3012 if (secondary_index == NULL) {
3013 set_index_for_single_value_property (iface, database, service_name,
3014 field_name, TRUE,
3015 datetime,
3016 &internal_error);
3017 if (internal_error) {
3018 g_propagate_error (error, internal_error);
3019 goto error_out;
3020 }
3021 } else {
3022 set_secondary_index_for_single_value_property (iface, database, service_name, field_name,
3023 tracker_property_get_name (secondary_index),
3024 TRUE, &internal_error);
3025 if (internal_error) {
3026 g_propagate_error (error, internal_error);
3027 goto error_out;
3028 }
3029 }
3030 }
3031 }
3032
3033 if (!tracker_class_get_is_new (service) && in_change && sel_col_sql && in_col_sql) {
3034 guint i;
3035 gchar *query;
3036
3037 query = g_strdup_printf ("INSERT INTO \"%s\".\"%s\"(%s) "
3038 "SELECT %s FROM \"%s\".\"%s_TEMP\"",
3039 database, service_name, in_col_sql->str,
3040 sel_col_sql->str, database, service_name);
3041
3042 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Copy: %s", query));
3043
3044 tracker_db_interface_execute_query (iface, &internal_error, "%s", query);
3045 g_free (query);
3046
3047 if (internal_error) {
3048 g_propagate_error (error, internal_error);
3049 goto error_out;
3050 }
3051
3052 for (i = 0; i < n_props; i++) {
3053 property = properties[i];
3054
3055 if (tracker_property_get_domain (property) == service && tracker_property_get_cardinality_changed (property)) {
3056 GString *n_sel_col_sql, *n_in_col_sql;
3057 const gchar *field_name = tracker_property_get_name (property);
3058
3059 n_in_col_sql = g_string_new ("ID");
3060 n_sel_col_sql = g_string_new ("ID");
3061
3062 /* Function does what it must do, so reusable atm */
3063 range_change_for (property, n_in_col_sql, n_sel_col_sql, field_name);
3064
3065 /* Columns happen to be the same for decomposed multi-value and single value atm */
3066
3067 query = g_strdup_printf ("INSERT INTO \"%s\".\"%s_%s\"(%s) "
3068 "SELECT %s FROM \"%s\".\"%s_TEMP\" "
3069 "WHERE ID IS NOT NULL AND \"%s\" IS NOT NULL",
3070 database, service_name, field_name,
3071 n_in_col_sql->str, n_sel_col_sql->str,
3072 database, service_name, field_name);
3073
3074 g_string_free (n_in_col_sql, TRUE);
3075 g_string_free (n_sel_col_sql, TRUE);
3076
3077 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Copy supported nlr:maxCardinality change: %s", query));
3078
3079 tracker_db_interface_execute_query (iface, &internal_error, "%s", query);
3080
3081 if (internal_error) {
3082 g_propagate_error (error, internal_error);
3083 goto error_out;
3084 }
3085
3086 g_free (query);
3087 }
3088 }
3089
3090 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Rename (drop): DROP TABLE \"%s_TEMP\"", service_name));
3091 tracker_db_interface_execute_query (iface, &internal_error,
3092 "DROP TABLE \"%s\".\"%s_TEMP\"",
3093 database, service_name);
3094
3095 if (internal_error) {
3096 g_propagate_error (error, internal_error);
3097 goto error_out;
3098 }
3099 }
3100
3101 /* FIXME: We are trusting object refcount will stay intact across
3102 * ontology changes. One situation where this is not true are
3103 * removal or properties with rdfs:Resource range.
3104 */
3105
3106 if (copy_schedule) {
3107 guint i;
3108 for (i = 0; i < copy_schedule->len; i++) {
3109 ScheduleCopy *sched = g_ptr_array_index (copy_schedule, i);
3110 copy_from_domain_to_domain_index (iface, database, sched->prop,
3111 sched->field_name, sched->suffix,
3112 service,
3113 &internal_error);
3114
3115 if (internal_error) {
3116 g_propagate_error (error, internal_error);
3117 break;
3118 }
3119 }
3120 }
3121
3122 error_out:
3123
3124 if (copy_schedule) {
3125 g_ptr_array_free (copy_schedule, TRUE);
3126 }
3127
3128 if (create_sql) {
3129 g_string_free (create_sql, TRUE);
3130 }
3131
3132 g_slist_free (class_properties);
3133
3134 if (in_col_sql) {
3135 g_string_free (in_col_sql, TRUE);
3136 }
3137
3138 if (sel_col_sql) {
3139 g_string_free (sel_col_sql, TRUE);
3140 }
3141 }
3142
3143 static void
tracker_data_ontology_import_finished(TrackerDataManager * manager)3144 tracker_data_ontology_import_finished (TrackerDataManager *manager)
3145 {
3146 TrackerClass **classes;
3147 TrackerProperty **properties;
3148 guint i, n_props, n_classes;
3149
3150 classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
3151 properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
3152
3153 for (i = 0; i < n_classes; i++) {
3154 tracker_class_set_is_new (classes[i], FALSE);
3155 tracker_class_set_db_schema_changed (classes[i], FALSE);
3156 }
3157
3158 for (i = 0; i < n_props; i++) {
3159 tracker_property_set_is_new_domain_index (properties[i], NULL, FALSE);
3160 tracker_property_set_is_new (properties[i], FALSE);
3161 tracker_property_set_db_schema_changed (properties[i], FALSE);
3162 tracker_property_set_cardinality_changed (properties[i], FALSE);
3163 }
3164 }
3165
3166 static gboolean
query_table_exists(TrackerDBInterface * iface,const gchar * table_name,GError ** error)3167 query_table_exists (TrackerDBInterface *iface,
3168 const gchar *table_name,
3169 GError **error)
3170 {
3171 TrackerDBCursor *cursor = NULL;
3172 TrackerDBStatement *stmt;
3173 gboolean exists = FALSE;
3174
3175 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, error,
3176 "SELECT 1 FROM sqlite_master WHERE tbl_name=\"%s\" AND type=\"table\"",
3177 table_name);
3178 if (stmt) {
3179 cursor = tracker_db_statement_start_cursor (stmt, error);
3180 g_object_unref (stmt);
3181 }
3182
3183 if (cursor) {
3184 if (tracker_db_cursor_iter_next (cursor, NULL, error)) {
3185 exists = TRUE;
3186 }
3187 g_object_unref (cursor);
3188 }
3189
3190 return exists;
3191 }
3192
3193 static gboolean
create_base_tables(TrackerDataManager * manager,TrackerDBInterface * iface,GError ** error)3194 create_base_tables (TrackerDataManager *manager,
3195 TrackerDBInterface *iface,
3196 GError **error)
3197 {
3198 GError *internal_error = NULL;
3199
3200 tracker_db_interface_execute_query (iface, &internal_error,
3201 "CREATE TABLE Resource (ID INTEGER NOT NULL PRIMARY KEY,"
3202 " Uri TEXT NOT NULL, BlankNode INTEGER DEFAULT 0, UNIQUE (Uri))");
3203
3204 if (internal_error) {
3205 g_propagate_error (error, internal_error);
3206 return FALSE;
3207 }
3208
3209 tracker_db_interface_execute_query (iface, &internal_error,
3210 "CREATE TABLE Graph (ID INTEGER NOT NULL PRIMARY KEY)");
3211
3212 if (internal_error) {
3213 g_propagate_error (error, internal_error);
3214 return FALSE;
3215 }
3216
3217 tracker_db_interface_execute_query (iface, &internal_error,
3218 "CREATE TABLE metadata (key TEXT NOT NULL PRIMARY KEY, value TEXT)");
3219
3220 if (internal_error) {
3221 g_propagate_error (error, internal_error);
3222 return FALSE;
3223 }
3224
3225 return TRUE;
3226 }
3227
3228 static gboolean
tracker_data_ontology_setup_db(TrackerDataManager * manager,TrackerDBInterface * iface,const gchar * database,gboolean in_update,GError ** error)3229 tracker_data_ontology_setup_db (TrackerDataManager *manager,
3230 TrackerDBInterface *iface,
3231 const gchar *database,
3232 gboolean in_update,
3233 GError **error)
3234 {
3235 GError *internal_error = NULL;
3236 TrackerClass **classes;
3237 guint i, n_classes;
3238
3239 tracker_db_interface_execute_query (iface, &internal_error,
3240 "CREATE TABLE IF NOT EXISTS "
3241 " \"%s\".Refcount (ID INTEGER NOT NULL PRIMARY KEY,"
3242 " Refcount INTEGER DEFAULT 0)",
3243 database);
3244
3245 if (internal_error) {
3246 g_propagate_error (error, internal_error);
3247 return FALSE;
3248 }
3249
3250 classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
3251
3252 /* create tables */
3253 for (i = 0; i < n_classes; i++) {
3254 /* Also !is_new classes are processed, they might have new properties */
3255 create_decomposed_metadata_tables (manager, iface, database, classes[i], in_update,
3256 tracker_class_get_db_schema_changed (classes[i]),
3257 &internal_error);
3258
3259 if (internal_error) {
3260 g_propagate_error (error, internal_error);
3261 return FALSE;
3262 }
3263 }
3264
3265 return TRUE;
3266 }
3267
3268 static void
tracker_data_ontology_import_into_db(TrackerDataManager * manager,TrackerDBInterface * iface,gboolean in_update,GError ** error)3269 tracker_data_ontology_import_into_db (TrackerDataManager *manager,
3270 TrackerDBInterface *iface,
3271 gboolean in_update,
3272 GError **error)
3273 {
3274 TrackerClass **classes;
3275 TrackerProperty **properties;
3276 guint i, n_props, n_classes;
3277
3278 classes = tracker_ontologies_get_classes (manager->ontologies, &n_classes);
3279 properties = tracker_ontologies_get_properties (manager->ontologies, &n_props);
3280
3281 /* insert classes into rdfs:Resource table */
3282 for (i = 0; i < n_classes; i++) {
3283 if (tracker_class_get_is_new (classes[i]) == in_update) {
3284 GError *internal_error = NULL;
3285
3286 insert_uri_in_resource_table (manager, iface,
3287 tracker_class_get_uri (classes[i]),
3288 tracker_class_get_id (classes[i]),
3289 &internal_error);
3290
3291 if (internal_error) {
3292 g_propagate_error (error, internal_error);
3293 return;
3294 }
3295 }
3296 }
3297
3298 /* insert properties into rdfs:Resource table */
3299 for (i = 0; i < n_props; i++) {
3300 if (tracker_property_get_is_new (properties[i]) == in_update) {
3301 GError *internal_error = NULL;
3302
3303 insert_uri_in_resource_table (manager, iface,
3304 tracker_property_get_uri (properties[i]),
3305 tracker_property_get_id (properties[i]),
3306 &internal_error);
3307
3308 if (internal_error) {
3309 g_propagate_error (error, internal_error);
3310 return;
3311 }
3312 }
3313 }
3314
3315 }
3316
3317 static gint
compare_file_names(GFile * file_a,GFile * file_b)3318 compare_file_names (GFile *file_a,
3319 GFile *file_b)
3320 {
3321 gchar *name_a, *name_b;
3322 gint return_val;
3323
3324 name_a = g_file_get_basename (file_a);
3325 name_b = g_file_get_basename (file_b);
3326 return_val = strcmp (name_a, name_b);
3327
3328 g_free (name_a);
3329 g_free (name_b);
3330
3331 return return_val;
3332 }
3333
3334 static GList*
get_ontologies(TrackerDataManager * manager,GFile * ontologies,GError ** error)3335 get_ontologies (TrackerDataManager *manager,
3336 GFile *ontologies,
3337 GError **error)
3338 {
3339 GFileEnumerator *enumerator;
3340 GList *sorted = NULL;
3341
3342 enumerator = g_file_enumerate_children (ontologies,
3343 G_FILE_ATTRIBUTE_STANDARD_NAME,
3344 G_FILE_QUERY_INFO_NONE,
3345 NULL, error);
3346 if (!enumerator)
3347 return NULL;
3348
3349 while (TRUE) {
3350 GFileInfo *info;
3351 GFile *child;
3352 const gchar *name;
3353
3354 if (!g_file_enumerator_iterate (enumerator, &info, &child, NULL, error)) {
3355 g_list_free_full (sorted, g_object_unref);
3356 g_object_unref (enumerator);
3357 return NULL;
3358 }
3359
3360 if (!info)
3361 break;
3362
3363 name = g_file_info_get_name (info);
3364 if (g_str_has_suffix (name, ".ontology"))
3365 sorted = g_list_prepend (sorted, g_object_ref (child));
3366 }
3367
3368 sorted = g_list_sort (sorted, (GCompareFunc) compare_file_names);
3369
3370 /* Add our builtin ontologies so they are loaded first */
3371 sorted = g_list_prepend (sorted, g_file_new_for_uri ("resource://org/freedesktop/tracker/ontology/20-dc.ontology"));
3372 sorted = g_list_prepend (sorted, g_file_new_for_uri ("resource://org/freedesktop/tracker/ontology/12-nrl.ontology"));
3373 sorted = g_list_prepend (sorted, g_file_new_for_uri ("resource://org/freedesktop/tracker/ontology/11-rdf.ontology"));
3374 sorted = g_list_prepend (sorted, g_file_new_for_uri ("resource://org/freedesktop/tracker/ontology/10-xsd.ontology"));
3375
3376 g_object_unref (enumerator);
3377
3378 return sorted;
3379 }
3380
3381 static void
tracker_data_manager_update_status(TrackerDataManager * manager,const gchar * status)3382 tracker_data_manager_update_status (TrackerDataManager *manager,
3383 const gchar *status)
3384 {
3385 g_free (manager->status);
3386 manager->status = g_strdup (status);
3387 g_object_notify (G_OBJECT (manager), "status");
3388 }
3389
3390 static void
busy_callback(const gchar * status,gdouble progress,gpointer user_data)3391 busy_callback (const gchar *status,
3392 gdouble progress,
3393 gpointer user_data)
3394 {
3395 tracker_data_manager_update_status (user_data, status);
3396 }
3397
3398
3399 static void
tracker_data_manager_recreate_indexes(TrackerDataManager * manager,GError ** error)3400 tracker_data_manager_recreate_indexes (TrackerDataManager *manager,
3401 GError **error)
3402 {
3403 GError *internal_error = NULL;
3404 TrackerProperty **properties;
3405 guint n_properties;
3406 guint i;
3407
3408 properties = tracker_ontologies_get_properties (manager->ontologies, &n_properties);
3409 if (!properties) {
3410 g_critical ("Couldn't get all properties to recreate indexes");
3411 return;
3412 }
3413
3414 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Dropping all indexes..."));
3415 for (i = 0; i < n_properties; i++) {
3416 fix_indexed (manager, properties [i], FALSE, &internal_error);
3417
3418 if (internal_error) {
3419 g_propagate_error (error, internal_error);
3420 return;
3421 }
3422 }
3423
3424 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Starting index re-creation..."));
3425 for (i = 0; i < n_properties; i++) {
3426 fix_indexed (manager, properties [i], TRUE, &internal_error);
3427
3428 if (internal_error) {
3429 g_critical ("Unable to create index for %s: %s",
3430 tracker_property_get_name (properties[i]),
3431 internal_error->message);
3432 g_clear_error (&internal_error);
3433 }
3434
3435 busy_callback ("Recreating indexes",
3436 (gdouble) ((gdouble) i / (gdouble) n_properties),
3437 manager);
3438 }
3439 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message (" Finished index re-creation..."));
3440 }
3441
3442 static gboolean
write_ontologies_gvdb(TrackerDataManager * manager,gboolean overwrite,GError ** error)3443 write_ontologies_gvdb (TrackerDataManager *manager,
3444 gboolean overwrite,
3445 GError **error)
3446 {
3447 gboolean retval = TRUE;
3448 gchar *filename;
3449 GFile *child;
3450
3451 if ((manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0)
3452 return TRUE;
3453 if (!manager->cache_location)
3454 return TRUE;
3455
3456 child = g_file_get_child (manager->cache_location, "ontologies.gvdb");
3457 filename = g_file_get_path (child);
3458 g_object_unref (child);
3459
3460 if (overwrite || !g_file_test (filename, G_FILE_TEST_EXISTS)) {
3461 retval = tracker_ontologies_write_gvdb (manager->ontologies, filename, error);
3462 }
3463
3464 g_free (filename);
3465
3466 return retval;
3467 }
3468
3469 static void
load_ontologies_gvdb(TrackerDataManager * manager,GError ** error)3470 load_ontologies_gvdb (TrackerDataManager *manager,
3471 GError **error)
3472 {
3473 gchar *filename;
3474 GFile *child;
3475
3476 child = g_file_get_child (manager->cache_location, "ontologies.gvdb");
3477 filename = g_file_get_path (child);
3478 g_object_unref (child);
3479
3480 tracker_ontologies_load_gvdb (manager->ontologies, filename, error);
3481 g_free (filename);
3482 }
3483
3484 static gboolean
tracker_data_manager_fts_changed(TrackerDataManager * manager)3485 tracker_data_manager_fts_changed (TrackerDataManager *manager)
3486 {
3487 TrackerProperty **properties;
3488 gboolean has_changed = FALSE;
3489 guint i, len;
3490
3491 properties = tracker_ontologies_get_properties (manager->ontologies, &len);
3492
3493 for (i = 0; i < len; i++) {
3494 TrackerClass *class;
3495
3496 if (tracker_property_get_fulltext_indexed (properties[i]) !=
3497 tracker_property_get_orig_fulltext_indexed (properties[i])) {
3498 has_changed |= TRUE;
3499 }
3500
3501 if (!tracker_property_get_fulltext_indexed (properties[i])) {
3502 continue;
3503 }
3504
3505 has_changed |= tracker_property_get_is_new (properties[i]);
3506
3507 /* We must also regenerate FTS if any table in the view
3508 * updated its schema.
3509 */
3510 class = tracker_property_get_domain (properties[i]);
3511 has_changed |= tracker_class_get_db_schema_changed (class);
3512 }
3513
3514 return has_changed;
3515 }
3516
3517 static void
ontology_get_fts_properties(TrackerDataManager * manager,GHashTable ** fts_properties,GHashTable ** multivalued)3518 ontology_get_fts_properties (TrackerDataManager *manager,
3519 GHashTable **fts_properties,
3520 GHashTable **multivalued)
3521 {
3522 TrackerProperty **properties;
3523 guint i, len;
3524
3525 properties = tracker_ontologies_get_properties (manager->ontologies, &len);
3526 *multivalued = g_hash_table_new (g_str_hash, g_str_equal);
3527 *fts_properties = g_hash_table_new_full (g_str_hash, g_str_equal,
3528 NULL, (GDestroyNotify) g_list_free);
3529
3530 for (i = 0; i < len; i++) {
3531 const gchar *name, *table_name;
3532 GList *list;
3533
3534 if (!tracker_property_get_fulltext_indexed (properties[i])) {
3535 continue;
3536 }
3537
3538 table_name = tracker_property_get_table_name (properties[i]);
3539 name = tracker_property_get_name (properties[i]);
3540 list = g_hash_table_lookup (*fts_properties, table_name);
3541
3542 if (tracker_property_get_multiple_values (properties[i])) {
3543 g_hash_table_insert (*multivalued, (gpointer) table_name,
3544 GUINT_TO_POINTER (TRUE));
3545 }
3546
3547 if (!list) {
3548 list = g_list_prepend (NULL, (gpointer) name);
3549 g_hash_table_insert (*fts_properties, (gpointer) table_name, list);
3550 } else {
3551 g_hash_table_steal (*fts_properties, (gpointer) table_name);
3552 list = g_list_insert_sorted (list, (gpointer) name,
3553 (GCompareFunc) strcmp);
3554 g_hash_table_insert (*fts_properties, (gpointer) table_name, list);
3555 }
3556 }
3557 }
3558
3559 static gboolean
rebuild_fts_tokens(TrackerDataManager * manager,TrackerDBInterface * iface,GError ** error)3560 rebuild_fts_tokens (TrackerDataManager *manager,
3561 TrackerDBInterface *iface,
3562 GError **error)
3563 {
3564 GHashTableIter iter;
3565 gchar *graph;
3566
3567 g_debug ("Rebuilding FTS tokens, this may take a moment...");
3568 if (!tracker_db_interface_sqlite_fts_rebuild_tokens (iface, "main", error))
3569 return FALSE;
3570
3571 g_hash_table_iter_init (&iter, manager->graphs);
3572 while (g_hash_table_iter_next (&iter, (gpointer*) &graph, NULL)) {
3573 if (!tracker_db_interface_sqlite_fts_rebuild_tokens (iface, graph, error))
3574 return FALSE;
3575 }
3576
3577 g_debug ("FTS tokens rebuilt");
3578 /* Update the stamp file */
3579 tracker_db_manager_tokenizer_update (manager->db_manager);
3580
3581 return TRUE;
3582 }
3583
3584 static gboolean
tracker_data_manager_init_fts(TrackerDataManager * manager,TrackerDBInterface * iface,const gchar * database,gboolean create,GError ** error)3585 tracker_data_manager_init_fts (TrackerDataManager *manager,
3586 TrackerDBInterface *iface,
3587 const gchar *database,
3588 gboolean create,
3589 GError **error)
3590 {
3591 GHashTable *fts_props, *multivalued;
3592 gboolean retval;
3593
3594 ontology_get_fts_properties (manager, &fts_props, &multivalued);
3595 retval = tracker_db_interface_sqlite_fts_init (iface,
3596 database,
3597 fts_props,
3598 multivalued, create,
3599 error);
3600 g_hash_table_unref (fts_props);
3601 g_hash_table_unref (multivalued);
3602
3603 return retval;
3604 }
3605
3606 static gboolean
tracker_data_manager_update_fts(TrackerDataManager * manager,TrackerDBInterface * iface,const gchar * database,GError ** error)3607 tracker_data_manager_update_fts (TrackerDataManager *manager,
3608 TrackerDBInterface *iface,
3609 const gchar *database,
3610 GError **error)
3611 {
3612 GHashTable *fts_properties, *multivalued;
3613 gboolean retval;
3614
3615 ontology_get_fts_properties (manager, &fts_properties, &multivalued);
3616 retval = tracker_db_interface_sqlite_fts_alter_table (iface, database,
3617 fts_properties,
3618 multivalued,
3619 error);
3620 g_hash_table_unref (fts_properties);
3621 g_hash_table_unref (multivalued);
3622
3623 return retval;
3624 }
3625
3626 GFile *
tracker_data_manager_get_cache_location(TrackerDataManager * manager)3627 tracker_data_manager_get_cache_location (TrackerDataManager *manager)
3628 {
3629 return manager->cache_location ? g_object_ref (manager->cache_location) : NULL;
3630 }
3631
3632 TrackerDataManager *
tracker_data_manager_new(TrackerDBManagerFlags flags,GFile * cache_location,GFile * ontology_location,guint select_cache_size,guint update_cache_size)3633 tracker_data_manager_new (TrackerDBManagerFlags flags,
3634 GFile *cache_location,
3635 GFile *ontology_location,
3636 guint select_cache_size,
3637 guint update_cache_size)
3638 {
3639 TrackerDataManager *manager;
3640
3641 if ((flags & TRACKER_DB_MANAGER_IN_MEMORY) == 0 && !cache_location) {
3642 g_warning ("Data storage location must be provided");
3643 return NULL;
3644 }
3645
3646 manager = g_object_new (TRACKER_TYPE_DATA_MANAGER, NULL);
3647
3648 /* TODO: Make these properties */
3649 g_set_object (&manager->cache_location, cache_location);
3650 g_set_object (&manager->ontology_location, ontology_location);
3651 manager->flags = flags;
3652 manager->select_cache_size = select_cache_size;
3653 manager->update_cache_size = update_cache_size;
3654
3655 return manager;
3656 }
3657
3658 static void
update_ontology_last_modified(TrackerDataManager * manager,TrackerDBInterface * iface,TrackerOntology * ontology,GError ** error)3659 update_ontology_last_modified (TrackerDataManager *manager,
3660 TrackerDBInterface *iface,
3661 TrackerOntology *ontology,
3662 GError **error)
3663 {
3664 TrackerDBStatement *stmt;
3665 const gchar *ontology_uri;
3666 time_t last_mod;
3667
3668 ontology_uri = tracker_ontology_get_uri (ontology);
3669 last_mod = tracker_ontology_get_last_modified (ontology);
3670 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, error,
3671 "UPDATE \"nrl:Ontology\" SET \"nrl:lastModified\"= ? "
3672 "WHERE \"nrl:Ontology\".ID = "
3673 "(SELECT Resource.ID FROM Resource WHERE "
3674 "Resource.Uri = ?)");
3675 if (stmt) {
3676 tracker_db_statement_bind_int (stmt, 0, last_mod);
3677 tracker_db_statement_bind_text (stmt, 1, ontology_uri);
3678 tracker_db_statement_execute (stmt, error);
3679 g_object_unref (stmt);
3680 }
3681 }
3682
3683 static gboolean
tracker_data_manager_initialize_iface(TrackerDataManager * data_manager,TrackerDBInterface * iface,GError ** error)3684 tracker_data_manager_initialize_iface (TrackerDataManager *data_manager,
3685 TrackerDBInterface *iface,
3686 GError **error)
3687 {
3688 GHashTable *graphs;
3689
3690 graphs = tracker_data_manager_ensure_graphs (data_manager, iface, NULL);
3691
3692 if (graphs) {
3693 GHashTableIter iter;
3694 gpointer value;
3695
3696 g_hash_table_iter_init (&iter, graphs);
3697
3698 while (g_hash_table_iter_next (&iter, &value, NULL)) {
3699 if (!tracker_db_manager_attach_database (data_manager->db_manager,
3700 iface, value, FALSE,
3701 error)) {
3702 return FALSE;
3703 }
3704
3705 if (!tracker_data_manager_init_fts (data_manager, iface,
3706 value, FALSE, error))
3707 return FALSE;
3708 }
3709 }
3710
3711 if (!tracker_data_manager_init_fts (data_manager, iface, "main", FALSE, error))
3712 return FALSE;
3713
3714 return TRUE;
3715 }
3716
3717 static void
setup_interface_cb(TrackerDBManager * db_manager,TrackerDBInterface * iface,TrackerDataManager * data_manager)3718 setup_interface_cb (TrackerDBManager *db_manager,
3719 TrackerDBInterface *iface,
3720 TrackerDataManager *data_manager)
3721 {
3722 GError *error = NULL;
3723 guint flags;
3724
3725 if (!tracker_data_manager_initialize_iface (data_manager, iface, &error)) {
3726 g_critical ("Could not set up interface %p: %s",
3727 iface, error->message);
3728 g_error_free (error);
3729 }
3730
3731 g_object_get (iface, "flags", &flags, NULL);
3732 }
3733
3734 static gboolean
update_attached_databases(TrackerDBInterface * iface,TrackerDataManager * data_manager,gboolean * changed,GError ** error)3735 update_attached_databases (TrackerDBInterface *iface,
3736 TrackerDataManager *data_manager,
3737 gboolean *changed,
3738 GError **error)
3739 {
3740 TrackerDBStatement *stmt;
3741 TrackerDBCursor *cursor;
3742 gboolean retval = TRUE;
3743
3744 *changed = FALSE;
3745 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, error,
3746 "SELECT name, 1, 0, 0 FROM pragma_database_list "
3747 "WHERE name NOT IN (SELECT Uri FROM RESOURCE WHERE ID IN (SELECT ID FROM Graph))"
3748 "UNION "
3749 "SELECT Uri, 0, 1, ID FROM Resource "
3750 "WHERE ID IN (SELECT ID FROM Graph) "
3751 "AND Uri NOT IN (SELECT name FROM pragma_database_list)");
3752
3753 if (!stmt)
3754 return FALSE;
3755
3756 cursor = tracker_db_statement_start_cursor (stmt, error);
3757 g_object_unref (stmt);
3758
3759 if (!cursor)
3760 return FALSE;
3761
3762 while (retval && tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
3763 const gchar *name;
3764
3765 name = tracker_db_cursor_get_string (cursor, 0, NULL);
3766
3767 /* Ignore the main and temp databases, they are always attached */
3768 if (strcmp (name, "main") == 0 || strcmp (name, "temp") == 0)
3769 continue;
3770
3771 if (tracker_db_cursor_get_int (cursor, 1)) {
3772 if (!tracker_db_manager_detach_database (data_manager->db_manager,
3773 iface, name, error)) {
3774 retval = FALSE;
3775 break;
3776 }
3777
3778 g_hash_table_remove (data_manager->graphs, name);
3779 *changed = TRUE;
3780 } else if (tracker_db_cursor_get_int (cursor, 2)) {
3781 if (!tracker_db_manager_attach_database (data_manager->db_manager,
3782 iface, name, FALSE, error)) {
3783 retval = FALSE;
3784 break;
3785 }
3786
3787 g_hash_table_insert (data_manager->graphs, g_strdup (name),
3788 GINT_TO_POINTER (tracker_db_cursor_get_int (cursor, 3)));
3789 *changed = TRUE;
3790 }
3791 }
3792
3793 g_object_unref (cursor);
3794
3795 return retval;
3796 }
3797
3798 static void
update_interface_cb(TrackerDBManager * db_manager,TrackerDBInterface * iface,TrackerDataManager * data_manager)3799 update_interface_cb (TrackerDBManager *db_manager,
3800 TrackerDBInterface *iface,
3801 TrackerDataManager *data_manager)
3802 {
3803 GError *error = NULL;
3804 guint iface_generation;
3805 gboolean update = FALSE, changed, readonly;
3806
3807 readonly = (tracker_db_manager_get_flags (db_manager, NULL, NULL) & TRACKER_DB_MANAGER_READONLY) != 0;
3808
3809 if (readonly) {
3810 update = TRUE;
3811 } else {
3812 iface_generation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (iface),
3813 "tracker-data-iface-generation"));
3814 update = (iface_generation != data_manager->generation);
3815 }
3816
3817 if (update) {
3818 if (update_attached_databases (iface, data_manager, &changed, &error)) {
3819 /* This is where we bump the generation for the readonly case, in response to
3820 * tables being attached or detached due to graph changes.
3821 */
3822 if (readonly && changed)
3823 data_manager->generation++;
3824 } else {
3825 g_critical ("Could not update attached databases: %s\n",
3826 error->message);
3827 g_error_free (error);
3828 }
3829
3830 g_object_set_data (G_OBJECT (iface), "tracker-data-iface-generation",
3831 GUINT_TO_POINTER (data_manager->generation));
3832 }
3833 }
3834
3835 static gboolean
tracker_data_manager_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)3836 tracker_data_manager_initable_init (GInitable *initable,
3837 GCancellable *cancellable,
3838 GError **error)
3839 {
3840 TrackerDataManager *manager = TRACKER_DATA_MANAGER (initable);
3841 TrackerDBInterface *iface;
3842 gboolean is_create, has_graph_table = FALSE;
3843 TrackerDBCursor *cursor;
3844 TrackerDBStatement *stmt;
3845 GHashTable *ontos_table;
3846 GHashTable *graphs = NULL;
3847 GList *sorted = NULL, *l;
3848 gboolean read_only;
3849 GError *internal_error = NULL;
3850
3851 if (manager->initialized) {
3852 return TRUE;
3853 }
3854
3855 if (manager->cache_location && !g_file_is_native (manager->cache_location)) {
3856 g_set_error (error,
3857 TRACKER_DATA_ONTOLOGY_ERROR,
3858 TRACKER_DATA_UNSUPPORTED_LOCATION,
3859 "Cache and data locations must be local");
3860 return FALSE;
3861 }
3862
3863 read_only = (manager->flags & TRACKER_DB_MANAGER_READONLY) ? TRUE : FALSE;
3864
3865 /* Make sure we initialize all other modules we depend on */
3866 manager->data_update = tracker_data_new (manager);
3867 manager->ontologies = tracker_ontologies_new ();
3868
3869 manager->db_manager = tracker_db_manager_new (manager->flags,
3870 manager->cache_location,
3871 &is_create,
3872 FALSE,
3873 manager->select_cache_size,
3874 manager->update_cache_size,
3875 busy_callback, manager,
3876 G_OBJECT (manager),
3877 manager,
3878 &internal_error);
3879 if (!manager->db_manager) {
3880 g_propagate_error (error, internal_error);
3881 return FALSE;
3882 }
3883
3884 g_signal_connect (manager->db_manager, "setup-interface",
3885 G_CALLBACK (setup_interface_cb), manager);
3886 g_signal_connect (manager->db_manager, "update-interface",
3887 G_CALLBACK (update_interface_cb), manager);
3888
3889 tracker_data_manager_update_status (manager, "Initializing data manager");
3890
3891 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
3892
3893 if (manager->ontology_location &&
3894 g_file_query_file_type (manager->ontology_location, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) {
3895 gchar *uri;
3896
3897 uri = g_file_get_uri (manager->ontology_location);
3898 g_set_error (error, TRACKER_DATA_ONTOLOGY_ERROR,
3899 TRACKER_DATA_ONTOLOGY_NOT_FOUND,
3900 "'%s' is not a ontology location", uri);
3901 g_free (uri);
3902 return FALSE;
3903 }
3904
3905 if (is_create) {
3906 TrackerOntology **ontologies;
3907 guint n_ontologies, i;
3908
3909 if (!manager->ontology_location) {
3910 g_set_error (error,
3911 TRACKER_DATA_ONTOLOGY_ERROR,
3912 TRACKER_DATA_ONTOLOGY_NOT_FOUND,
3913 "You must pass an ontology location. "
3914 "Use tracker_sparql_get_ontology_nepomuk() to find the default ontologies.");
3915 return FALSE;
3916 }
3917
3918 g_debug ("Applying ontologies from %s", g_file_peek_path (manager->ontology_location));
3919 sorted = get_ontologies (manager, manager->ontology_location, &internal_error);
3920
3921 if (internal_error) {
3922 g_propagate_error (error, internal_error);
3923 return FALSE;
3924 }
3925
3926 tracker_data_begin_ontology_transaction (manager->data_update, &internal_error);
3927 if (internal_error) {
3928 g_propagate_error (error, internal_error);
3929 return FALSE;
3930 }
3931
3932 if (!create_base_tables (manager, iface, error)) {
3933 return FALSE;
3934 }
3935
3936 for (l = sorted; l; l = l->next) {
3937 GError *ontology_error = NULL;
3938 GFile *ontology_file = l->data;
3939 gchar *uri = g_file_get_uri (ontology_file);
3940
3941 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Loading ontology %s", uri));
3942
3943 load_ontology_file (manager, ontology_file,
3944 FALSE,
3945 NULL,
3946 NULL,
3947 &ontology_error);
3948 if (ontology_error) {
3949 g_error ("Error loading ontology (%s): %s",
3950 uri, ontology_error->message);
3951 }
3952
3953 g_free (uri);
3954 }
3955
3956 tracker_data_ontology_setup_db (manager, iface, "main", FALSE,
3957 &internal_error);
3958
3959 if (!internal_error) {
3960 tracker_data_ontology_import_into_db (manager, iface,
3961 FALSE,
3962 &internal_error);
3963 }
3964
3965 if (internal_error) {
3966 g_propagate_error (error, internal_error);
3967 return FALSE;
3968 }
3969
3970 if (!tracker_data_manager_init_fts (manager, iface, "main", TRUE, &internal_error)) {
3971 g_propagate_error (error, internal_error);
3972 return FALSE;
3973 }
3974
3975 tracker_data_manager_initialize_iface (manager, iface, &internal_error);
3976 if (internal_error) {
3977 g_propagate_error (error, internal_error);
3978 return FALSE;
3979 }
3980
3981 /* store ontology in database */
3982 for (l = sorted; l; l = l->next) {
3983 import_ontology_file (manager, l->data, FALSE);
3984 }
3985
3986 tracker_data_commit_transaction (manager->data_update, &internal_error);
3987
3988 if (internal_error) {
3989 g_propagate_error (error, internal_error);
3990 return FALSE;
3991 }
3992
3993 write_ontologies_gvdb (manager, TRUE /* overwrite */, NULL);
3994
3995 ontologies = tracker_ontologies_get_ontologies (manager->ontologies, &n_ontologies);
3996
3997 for (i = 0; i < n_ontologies; i++) {
3998 GError *n_error = NULL;
3999
4000 update_ontology_last_modified (manager, iface, ontologies[i], &n_error);
4001
4002 if (n_error) {
4003 g_critical ("%s", n_error->message);
4004 g_clear_error (&n_error);
4005 }
4006 }
4007
4008 g_list_free_full (sorted, g_object_unref);
4009 sorted = NULL;
4010 } else {
4011 GError *gvdb_error = NULL;
4012 gboolean load_from_db = TRUE;
4013
4014 if (read_only) {
4015 /* Not all ontology information is saved in the gvdb cache, so
4016 * it can only be used for read-only connections. */
4017 g_debug ("Loading cached ontologies from gvdb cache");
4018 load_ontologies_gvdb (manager, &gvdb_error);
4019
4020 if (gvdb_error) {
4021 g_debug ("Error loading ontology cache: %s. ", gvdb_error->message);
4022 g_clear_error (&gvdb_error);
4023 } else {
4024 load_from_db = FALSE;
4025 }
4026 }
4027
4028 if (load_from_db) {
4029 g_debug ("Loading ontologies from database.");
4030
4031 db_get_static_data (iface, manager, &internal_error);
4032 if (internal_error) {
4033 g_propagate_error (error, internal_error);
4034 return FALSE;
4035 }
4036
4037 if (!read_only) {
4038 write_ontologies_gvdb (manager, FALSE /* overwrite */, NULL);
4039 }
4040 }
4041
4042 tracker_data_manager_initialize_iface (manager, iface, &internal_error);
4043 if (internal_error) {
4044 g_propagate_error (error, internal_error);
4045 return FALSE;
4046 }
4047 }
4048
4049 if (!is_create && manager->ontology_location) {
4050 GList *to_reload = NULL;
4051 GList *ontos = NULL;
4052 GPtrArray *seen_classes;
4053 GPtrArray *seen_properties;
4054 GError *n_error = NULL;
4055 gboolean transaction_started = FALSE;
4056
4057 seen_classes = g_ptr_array_new ();
4058 seen_properties = g_ptr_array_new ();
4059
4060 g_debug ("Applying ontologies from %s to existing database", g_file_peek_path (manager->ontology_location));
4061
4062 /* Get all the ontology files from ontology_location */
4063 ontos = get_ontologies (manager, manager->ontology_location, &internal_error);
4064
4065 if (internal_error) {
4066 g_propagate_error (error, internal_error);
4067 return FALSE;
4068 }
4069
4070 /* check ontology against database */
4071
4072 /* Get a map of nrl:Ontology v. nrl:lastModified so that we can test
4073 * for all the ontology files in ontology_location whether the last-modified
4074 * has changed since we dealt with the file last time. */
4075
4076 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &n_error,
4077 "SELECT Resource.Uri, \"nrl:Ontology\".\"nrl:lastModified\" FROM \"nrl:Ontology\" "
4078 "INNER JOIN Resource ON Resource.ID = \"nrl:Ontology\".ID ");
4079
4080 if (stmt) {
4081 cursor = tracker_db_statement_start_cursor (stmt, &n_error);
4082 g_object_unref (stmt);
4083 } else {
4084 cursor = NULL;
4085 }
4086
4087 ontos_table = g_hash_table_new_full (g_str_hash,
4088 g_str_equal,
4089 g_free,
4090 NULL);
4091
4092 if (cursor) {
4093 while (tracker_db_cursor_iter_next (cursor, NULL, &n_error)) {
4094 const gchar *onto_uri = tracker_db_cursor_get_string (cursor, 0, NULL);
4095 /* It's stored as an int in the db anyway. This is caused by
4096 * string_to_gvalue in tracker-data-update.c */
4097 gint value = tracker_db_cursor_get_int (cursor, 1);
4098
4099 g_hash_table_insert (ontos_table, g_strdup (onto_uri),
4100 GINT_TO_POINTER (value));
4101 }
4102
4103 g_object_unref (cursor);
4104 }
4105
4106 if (n_error) {
4107 g_warning ("%s", n_error->message);
4108 g_clear_error (&n_error);
4109 }
4110
4111 for (l = ontos; l; l = l->next) {
4112 TrackerOntology *ontology;
4113 GFile *ontology_file = l->data;
4114 const gchar *ontology_uri;
4115 gboolean found, update_nao = FALSE;
4116 gpointer value;
4117 gint last_mod;
4118
4119 /* Parse a TrackerOntology from ontology_file */
4120 ontology = get_ontology_from_file (manager, ontology_file);
4121
4122 if (!ontology) {
4123 /* TODO: cope with full custom .ontology files: deal with this
4124 * error gracefully. App devs might install wrong ontology files
4125 * and we shouldn't critical() due to this. */
4126 gchar *uri = g_file_get_uri (ontology_file);
4127 g_critical ("Can't get ontology from file: %s", uri);
4128 g_free (uri);
4129 continue;
4130 }
4131
4132 ontology_uri = tracker_ontology_get_uri (ontology);
4133 /* We can't do better than this cast, it's stored as an int in the
4134 * db. See above comment for more info. */
4135 last_mod = (gint) tracker_ontology_get_last_modified (ontology);
4136
4137 found = g_hash_table_lookup_extended (ontos_table,
4138 ontology_uri,
4139 NULL, &value);
4140
4141 if (found) {
4142 GError *ontology_error = NULL;
4143 gint val = GPOINTER_TO_INT (value);
4144
4145 has_graph_table = query_table_exists (iface, "Graph", &internal_error);
4146 if (!has_graph_table) {
4147 /* No graph table and no resource triggers,
4148 * the table must be recreated.
4149 */
4150 if (!transaction_started) {
4151 tracker_data_begin_ontology_transaction (manager->data_update, &internal_error);
4152 if (internal_error) {
4153 g_propagate_error (error, internal_error);
4154 return FALSE;
4155 }
4156 transaction_started = TRUE;
4157 }
4158
4159 to_reload = g_list_prepend (to_reload, ontology_file);
4160 continue;
4161 }
4162
4163 /* When the last-modified in our database isn't the same as the last
4164 * modified in the latest version of the file, deal with changes. */
4165 if (val != last_mod) {
4166 gchar *uri = g_file_get_uri (ontology_file);
4167
4168 TRACKER_NOTE (ONTOLOGY_CHANGES, g_message ("Ontology file '%s' needs update", uri));
4169 g_free (uri);
4170
4171 if (!transaction_started) {
4172 tracker_data_begin_ontology_transaction (manager->data_update, &internal_error);
4173 if (internal_error) {
4174 g_propagate_error (error, internal_error);
4175 return FALSE;
4176 }
4177 transaction_started = TRUE;
4178 }
4179
4180 /* load ontology from files into memory, set all new's
4181 * is_new to TRUE */
4182 load_ontology_file (manager, ontology_file,
4183 TRUE,
4184 seen_classes,
4185 seen_properties,
4186 &ontology_error);
4187
4188 if (g_error_matches (ontology_error,
4189 TRACKER_DATA_ONTOLOGY_ERROR,
4190 TRACKER_DATA_UNSUPPORTED_ONTOLOGY_CHANGE)) {
4191 g_warning ("%s", ontology_error->message);
4192 g_error_free (ontology_error);
4193
4194 tracker_data_ontology_free_seen (seen_classes);
4195 tracker_data_ontology_free_seen (seen_properties);
4196 tracker_data_ontology_import_finished (manager);
4197
4198 /* as we're processing an ontology change,
4199 transaction is guaranteed to be started */
4200 tracker_data_rollback_transaction (manager->data_update);
4201
4202 if (ontos_table) {
4203 g_hash_table_unref (ontos_table);
4204 }
4205 if (ontos) {
4206 g_list_free_full (ontos, g_object_unref);
4207 }
4208 g_object_unref (manager->ontology_location);
4209
4210 goto skip_ontology_check;
4211 }
4212
4213 if (ontology_error) {
4214 g_critical ("Fatal error dealing with ontology changes: %s", ontology_error->message);
4215 g_error_free (ontology_error);
4216 }
4217
4218 to_reload = g_list_prepend (to_reload, l->data);
4219 update_nao = TRUE;
4220 }
4221 } else {
4222 GError *ontology_error = NULL;
4223 gchar *uri = g_file_get_uri (ontology_file);
4224
4225 g_debug ("Ontology file '%s' got added", uri);
4226 g_free (uri);
4227
4228 if (!transaction_started) {
4229 tracker_data_begin_ontology_transaction (manager->data_update, &internal_error);
4230 if (internal_error) {
4231 g_propagate_error (error, internal_error);
4232 return FALSE;
4233 }
4234 transaction_started = TRUE;
4235 }
4236
4237 /* load ontology from files into memory, set all new's
4238 * is_new to TRUE */
4239 load_ontology_file (manager, ontology_file,
4240 TRUE,
4241 seen_classes,
4242 seen_properties,
4243 &ontology_error);
4244
4245 if (g_error_matches (ontology_error,
4246 TRACKER_DATA_ONTOLOGY_ERROR,
4247 TRACKER_DATA_UNSUPPORTED_ONTOLOGY_CHANGE)) {
4248 g_warning ("%s", ontology_error->message);
4249 g_error_free (ontology_error);
4250
4251 tracker_data_ontology_free_seen (seen_classes);
4252 tracker_data_ontology_free_seen (seen_properties);
4253 tracker_data_ontology_import_finished (manager);
4254
4255 /* as we're processing an ontology change,
4256 transaction is guaranteed to be started */
4257 tracker_data_rollback_transaction (manager->data_update);
4258
4259 if (ontos_table) {
4260 g_hash_table_unref (ontos_table);
4261 }
4262 if (ontos) {
4263 g_list_free_full (ontos, g_object_unref);
4264 }
4265
4266 goto skip_ontology_check;
4267 }
4268
4269 if (ontology_error) {
4270 g_critical ("Fatal error dealing with ontology changes: %s", ontology_error->message);
4271 g_error_free (ontology_error);
4272 }
4273
4274 to_reload = g_list_prepend (to_reload, l->data);
4275 update_nao = TRUE;
4276 }
4277
4278 if (update_nao) {
4279 update_ontology_last_modified (manager, iface, ontology, &n_error);
4280
4281 if (n_error) {
4282 g_critical ("%s", n_error->message);
4283 g_clear_error (&n_error);
4284 }
4285 }
4286
4287 g_object_unref (ontology);
4288 }
4289
4290 if (to_reload) {
4291 GError *ontology_error = NULL;
4292
4293 tracker_data_ontology_process_changes_pre_db (manager,
4294 seen_classes,
4295 seen_properties,
4296 &ontology_error);
4297
4298 if (!ontology_error) {
4299 /* Perform ALTER-TABLE and CREATE-TABLE calls for all that are is_new */
4300 gboolean update_fts;
4301
4302 update_fts = tracker_data_manager_fts_changed (manager);
4303
4304 if (update_fts)
4305 tracker_db_interface_sqlite_fts_delete_table (iface, "main", &ontology_error);
4306
4307 if (!ontology_error)
4308 tracker_data_ontology_setup_db (manager, iface, "main", TRUE, &ontology_error);
4309
4310 if (!ontology_error)
4311 graphs = tracker_data_manager_ensure_graphs (manager, iface, &ontology_error);
4312
4313 if (graphs) {
4314 GHashTableIter iter;
4315 gpointer value;
4316
4317 g_hash_table_iter_init (&iter, graphs);
4318
4319 while (g_hash_table_iter_next (&iter, &value, NULL)) {
4320 if (update_fts)
4321 tracker_db_interface_sqlite_fts_delete_table (iface, value, &ontology_error);
4322
4323 if (ontology_error)
4324 break;
4325
4326 tracker_data_ontology_setup_db (manager, iface, value, TRUE,
4327 &ontology_error);
4328 if (ontology_error)
4329 break;
4330
4331 if (update_fts) {
4332 tracker_data_manager_update_fts (manager, iface, value, &ontology_error);
4333 } else {
4334 tracker_data_manager_init_fts (manager, iface, value, FALSE, &ontology_error);
4335 }
4336
4337 if (ontology_error)
4338 break;
4339 }
4340 }
4341
4342 if (!ontology_error) {
4343 if (update_fts) {
4344 tracker_data_manager_update_fts (manager, iface, "main", &ontology_error);
4345 } else {
4346 tracker_data_manager_init_fts (manager, iface, "main", FALSE, &ontology_error);
4347 }
4348 }
4349
4350 if (!ontology_error) {
4351 tracker_data_ontology_import_into_db (manager, iface, TRUE,
4352 &ontology_error);
4353 }
4354
4355 if (!ontology_error) {
4356 tracker_data_ontology_process_changes_post_db (manager,
4357 seen_classes,
4358 seen_properties,
4359 &ontology_error);
4360 }
4361 }
4362
4363 if (g_error_matches (ontology_error,
4364 TRACKER_DATA_ONTOLOGY_ERROR,
4365 TRACKER_DATA_UNSUPPORTED_ONTOLOGY_CHANGE)) {
4366 g_warning ("%s", ontology_error->message);
4367 g_error_free (ontology_error);
4368
4369 tracker_data_ontology_free_seen (seen_classes);
4370 tracker_data_ontology_free_seen (seen_properties);
4371 tracker_data_ontology_import_finished (manager);
4372
4373 /* as we're processing an ontology change,
4374 transaction is guaranteed to be started */
4375 tracker_data_rollback_transaction (manager->data_update);
4376
4377 if (ontos_table) {
4378 g_hash_table_unref (ontos_table);
4379 }
4380 if (ontos) {
4381 g_list_free_full (ontos, g_object_unref);
4382 }
4383
4384 goto skip_ontology_check;
4385 }
4386
4387 if (ontology_error) {
4388 g_propagate_error (error, ontology_error);
4389 return FALSE;
4390 }
4391
4392 for (l = to_reload; l; l = l->next) {
4393 GFile *ontology_file = l->data;
4394 /* store ontology in database */
4395 import_ontology_file (manager, ontology_file, TRUE);
4396 }
4397 g_list_free (to_reload);
4398
4399 tracker_data_ontology_process_changes_post_import (seen_classes, seen_properties);
4400
4401 write_ontologies_gvdb (manager, TRUE /* overwrite */, NULL);
4402 }
4403
4404 tracker_data_ontology_free_seen (seen_classes);
4405 tracker_data_ontology_free_seen (seen_properties);
4406
4407 /* Reset the is_new flag for all classes and properties */
4408 tracker_data_ontology_import_finished (manager);
4409
4410 if (transaction_started) {
4411 tracker_data_commit_transaction (manager->data_update, &internal_error);
4412 if (internal_error) {
4413 g_propagate_error (error, internal_error);
4414 return FALSE;
4415 }
4416 }
4417
4418 g_hash_table_unref (ontos_table);
4419 g_list_free_full (ontos, g_object_unref);
4420
4421 }
4422
4423
4424 skip_ontology_check:
4425 if (!read_only && is_create) {
4426 tracker_db_manager_set_current_locale (manager->db_manager);
4427 tracker_db_manager_tokenizer_update (manager->db_manager);
4428 } else if (!read_only && tracker_db_manager_locale_changed (manager->db_manager, NULL)) {
4429 /* If locale changed, re-create indexes.
4430 * No need to reset the collator in the db interface,
4431 * as this is only executed during startup, which should
4432 * already have the proper locale set in the collator */
4433 tracker_data_manager_recreate_indexes (manager, &internal_error);
4434
4435 if (internal_error) {
4436 g_propagate_error (error, internal_error);
4437 return FALSE;
4438 }
4439
4440 tracker_db_manager_set_current_locale (manager->db_manager);
4441
4442 if (!rebuild_fts_tokens (manager, iface, error))
4443 return FALSE;
4444 } else if (!read_only && tracker_db_manager_get_tokenizer_changed (manager->db_manager)) {
4445 if (!rebuild_fts_tokens (manager, iface, error))
4446 return FALSE;
4447 }
4448
4449 if (!read_only) {
4450 tracker_ontologies_sort (manager->ontologies);
4451 }
4452
4453 manager->initialized = TRUE;
4454
4455 /* This is the only one which doesn't show the 'OPERATION' part */
4456 tracker_data_manager_update_status (manager, "Idle");
4457
4458 return TRUE;
4459 }
4460
4461 static gboolean
data_manager_perform_cleanup(TrackerDataManager * manager,TrackerDBInterface * iface,GError ** error)4462 data_manager_perform_cleanup (TrackerDataManager *manager,
4463 TrackerDBInterface *iface,
4464 GError **error)
4465 {
4466 TrackerDBStatement *stmt;
4467 GError *internal_error = NULL;
4468 GHashTable *graphs;
4469 GHashTableIter iter;
4470 const gchar *graph;
4471 GString *str;
4472
4473 graphs = tracker_data_manager_ensure_graphs (manager, iface, &internal_error);
4474 if (!graphs)
4475 goto fail;
4476
4477 str = g_string_new ("WITH referencedElements(ID) AS ("
4478 "SELECT ID FROM \"main\".Refcount ");
4479
4480 g_hash_table_iter_init (&iter, graphs);
4481
4482 while (g_hash_table_iter_next (&iter, (gpointer*) &graph, NULL)) {
4483 g_string_append_printf (str,
4484 "UNION ALL SELECT ID FROM \"%s\".Refcount ",
4485 graph);
4486 }
4487
4488 g_string_append (str, ") ");
4489 g_string_append_printf (str,
4490 "DELETE FROM Resource "
4491 "WHERE Resource.ID NOT IN (SELECT ID FROM referencedElements) "
4492 "AND Resource.ID NOT IN (SELECT ID FROM Graph)");
4493
4494 stmt = tracker_db_interface_create_statement (iface,
4495 TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
4496 &internal_error,
4497 str->str);
4498 g_string_free (str, TRUE);
4499
4500 if (!stmt)
4501 goto fail;
4502
4503 tracker_db_statement_execute (stmt, &internal_error);
4504 g_object_unref (stmt);
4505
4506 fail:
4507 if (internal_error) {
4508 g_propagate_error (error, internal_error);
4509 return FALSE;
4510 }
4511
4512 return TRUE;
4513 }
4514
4515 void
tracker_data_manager_dispose(GObject * object)4516 tracker_data_manager_dispose (GObject *object)
4517 {
4518 TrackerDataManager *manager = TRACKER_DATA_MANAGER (object);
4519 TrackerDBInterface *iface;
4520 GError *error = NULL;
4521 gboolean readonly = TRUE;
4522
4523 if (manager->db_manager) {
4524 readonly = (tracker_db_manager_get_flags (manager->db_manager, NULL, NULL) & TRACKER_DB_MANAGER_READONLY) != 0;
4525
4526 if (!readonly) {
4527 /* Delete stale URIs in the Resource table */
4528 g_debug ("Cleaning up stale resource URIs");
4529
4530 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4531
4532 if (!data_manager_perform_cleanup (manager, iface, &error)) {
4533 g_warning ("Could not clean up stale resource URIs: %s\n",
4534 error->message);
4535 g_clear_error (&error);
4536 }
4537
4538 tracker_db_manager_check_perform_vacuum (manager->db_manager);
4539 }
4540
4541 g_clear_object (&manager->db_manager);
4542 }
4543
4544 g_clear_pointer (&manager->cached_connections, g_hash_table_unref);
4545
4546 G_OBJECT_CLASS (tracker_data_manager_parent_class)->dispose (object);
4547 }
4548
4549 void
tracker_data_manager_finalize(GObject * object)4550 tracker_data_manager_finalize (GObject *object)
4551 {
4552 TrackerDataManager *manager = TRACKER_DATA_MANAGER (object);
4553
4554 g_clear_object (&manager->ontologies);
4555 g_clear_object (&manager->data_update);
4556 g_clear_pointer (&manager->graphs, g_hash_table_unref);
4557 g_free (manager->status);
4558 g_mutex_clear (&manager->connections_lock);
4559
4560 G_OBJECT_CLASS (tracker_data_manager_parent_class)->finalize (object);
4561 }
4562
4563 static void
tracker_data_manager_initable_iface_init(GInitableIface * iface)4564 tracker_data_manager_initable_iface_init (GInitableIface *iface)
4565 {
4566 iface->init = tracker_data_manager_initable_init;
4567 }
4568
4569 static void
tracker_data_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4570 tracker_data_manager_get_property (GObject *object,
4571 guint prop_id,
4572 GValue *value,
4573 GParamSpec *pspec)
4574 {
4575 TrackerDataManager *manager = TRACKER_DATA_MANAGER (object);
4576
4577 switch (prop_id) {
4578 case PROP_STATUS:
4579 g_value_set_string (value, manager->status);
4580 break;
4581 default:
4582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4583 break;
4584 }
4585 }
4586
4587 static void
tracker_data_manager_class_init(TrackerDataManagerClass * klass)4588 tracker_data_manager_class_init (TrackerDataManagerClass *klass)
4589 {
4590 GObjectClass *object_class = G_OBJECT_CLASS (klass);
4591
4592 object_class->get_property = tracker_data_manager_get_property;
4593 object_class->dispose = tracker_data_manager_dispose;
4594 object_class->finalize = tracker_data_manager_finalize;
4595
4596 g_object_class_install_property (object_class,
4597 PROP_STATUS,
4598 g_param_spec_string ("status",
4599 "Status",
4600 "Status",
4601 NULL,
4602 G_PARAM_READABLE));
4603 }
4604
4605 TrackerOntologies *
tracker_data_manager_get_ontologies(TrackerDataManager * manager)4606 tracker_data_manager_get_ontologies (TrackerDataManager *manager)
4607 {
4608 return manager->ontologies;
4609 }
4610
4611 TrackerDBManager *
tracker_data_manager_get_db_manager(TrackerDataManager * manager)4612 tracker_data_manager_get_db_manager (TrackerDataManager *manager)
4613 {
4614 return manager->db_manager;
4615 }
4616
4617 TrackerDBInterface *
tracker_data_manager_get_db_interface(TrackerDataManager * manager)4618 tracker_data_manager_get_db_interface (TrackerDataManager *manager)
4619 {
4620 return tracker_db_manager_get_db_interface (manager->db_manager);
4621 }
4622
4623 TrackerDBInterface *
tracker_data_manager_get_writable_db_interface(TrackerDataManager * manager)4624 tracker_data_manager_get_writable_db_interface (TrackerDataManager *manager)
4625 {
4626 return tracker_db_manager_get_writable_db_interface (manager->db_manager);
4627 }
4628
4629 TrackerData *
tracker_data_manager_get_data(TrackerDataManager * manager)4630 tracker_data_manager_get_data (TrackerDataManager *manager)
4631 {
4632 return manager->data_update;
4633 }
4634
4635 void
tracker_data_manager_shutdown(TrackerDataManager * manager)4636 tracker_data_manager_shutdown (TrackerDataManager *manager)
4637 {
4638 g_object_run_dispose (G_OBJECT (manager));
4639 }
4640
4641 GHashTable *
tracker_data_manager_get_namespaces(TrackerDataManager * manager)4642 tracker_data_manager_get_namespaces (TrackerDataManager *manager)
4643 {
4644 TrackerNamespace **namespaces;
4645 guint i, n_namespaces;
4646 GHashTable *ht;
4647
4648 ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
4649 namespaces = tracker_ontologies_get_namespaces (manager->ontologies,
4650 &n_namespaces);
4651 for (i = 0; i < n_namespaces; i++) {
4652 g_hash_table_insert (ht,
4653 g_strdup (tracker_namespace_get_prefix (namespaces[i])),
4654 g_strdup (tracker_namespace_get_uri (namespaces[i])));
4655 }
4656
4657 return ht;
4658 }
4659
4660 static GHashTable *
copy_graphs(GHashTable * graphs)4661 copy_graphs (GHashTable *graphs)
4662 {
4663 GHashTable *copy;
4664 GHashTableIter iter;
4665 gpointer key, value;
4666
4667 copy = g_hash_table_new_full (g_str_hash,
4668 g_str_equal,
4669 g_free,
4670 NULL);
4671 g_hash_table_iter_init (&iter, graphs);
4672
4673 while (g_hash_table_iter_next (&iter, &key, &value))
4674 g_hash_table_insert (copy, g_strdup (key), value);
4675
4676 return copy;
4677 }
4678
4679 gboolean
tracker_data_manager_create_graph(TrackerDataManager * manager,const gchar * name,GError ** error)4680 tracker_data_manager_create_graph (TrackerDataManager *manager,
4681 const gchar *name,
4682 GError **error)
4683 {
4684 TrackerDBInterface *iface;
4685 gint id;
4686
4687
4688 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4689
4690 if (!tracker_db_manager_attach_database (manager->db_manager, iface,
4691 name, TRUE, error))
4692 return FALSE;
4693
4694 if (!tracker_data_ontology_setup_db (manager, iface, name,
4695 FALSE, error))
4696 goto detach;
4697
4698 if (!tracker_data_manager_init_fts (manager, iface, name, TRUE, error))
4699 goto detach;
4700
4701 id = tracker_data_ensure_graph (manager->data_update, name, error);
4702 if (id == 0)
4703 goto detach;
4704
4705 if (!manager->transaction_graphs)
4706 manager->transaction_graphs = copy_graphs (manager->graphs);
4707
4708 g_hash_table_insert (manager->transaction_graphs, g_strdup (name), GINT_TO_POINTER (id));
4709
4710 return TRUE;
4711
4712 detach:
4713 tracker_db_manager_detach_database (manager->db_manager, iface, name, NULL);
4714 return FALSE;
4715 }
4716
4717 gboolean
tracker_data_manager_drop_graph(TrackerDataManager * manager,const gchar * name,GError ** error)4718 tracker_data_manager_drop_graph (TrackerDataManager *manager,
4719 const gchar *name,
4720 GError **error)
4721 {
4722 TrackerDBInterface *iface;
4723
4724 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4725
4726 /* Silently refuse to drop the main graph, clear it instead */
4727 if (!name)
4728 return tracker_data_manager_clear_graph (manager, name, error);
4729
4730 /* Ensure the current transaction doesn't keep tables in this database locked */
4731 tracker_data_commit_transaction (manager->data_update, NULL);
4732 tracker_data_begin_transaction (manager->data_update, NULL);
4733
4734 if (!tracker_db_manager_detach_database (manager->db_manager, iface,
4735 name, error))
4736 return FALSE;
4737
4738 if (!tracker_data_delete_graph (manager->data_update, name, error))
4739 return FALSE;
4740
4741 if (!manager->transaction_graphs)
4742 manager->transaction_graphs = copy_graphs (manager->graphs);
4743
4744 g_hash_table_remove (manager->transaction_graphs, name);
4745
4746 return TRUE;
4747 }
4748
4749 gint
tracker_data_manager_find_graph(TrackerDataManager * manager,const gchar * name,gboolean in_transaction)4750 tracker_data_manager_find_graph (TrackerDataManager *manager,
4751 const gchar *name,
4752 gboolean in_transaction)
4753 {
4754 TrackerDBInterface *iface;
4755 GHashTable *graphs;
4756
4757 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4758
4759 if (in_transaction && manager->transaction_graphs) {
4760 return GPOINTER_TO_INT (g_hash_table_lookup (manager->transaction_graphs,
4761 name));
4762 }
4763
4764 graphs = tracker_data_manager_ensure_graphs (manager, iface, NULL);
4765
4766 if (!graphs)
4767 return 0;
4768
4769 return GPOINTER_TO_UINT (g_hash_table_lookup (graphs, name));
4770 }
4771
4772 gboolean
tracker_data_manager_clear_graph(TrackerDataManager * manager,const gchar * graph,GError ** error)4773 tracker_data_manager_clear_graph (TrackerDataManager *manager,
4774 const gchar *graph,
4775 GError **error)
4776 {
4777 TrackerOntologies *ontologies = manager->ontologies;
4778 TrackerClass **classes;
4779 TrackerProperty **properties;
4780 TrackerDBStatement *stmt;
4781 guint i, n_classes, n_properties;
4782 GError *inner_error = NULL;
4783 TrackerDBInterface *iface;
4784
4785 if (!graph)
4786 graph = "main";
4787
4788 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4789 classes = tracker_ontologies_get_classes (ontologies, &n_classes);
4790 properties = tracker_ontologies_get_properties (ontologies, &n_properties);
4791
4792 for (i = 0; !inner_error && i < n_classes; i++) {
4793 if (g_str_has_prefix (tracker_class_get_name (classes[i]), "xsd:"))
4794 continue;
4795
4796 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
4797 "DELETE FROM \"%s\".\"%s\"",
4798 graph,
4799 tracker_class_get_name (classes[i]));
4800 if (!stmt)
4801 goto out;
4802
4803 tracker_db_statement_execute (stmt, &inner_error);
4804 g_object_unref (stmt);
4805 }
4806
4807 for (i = 0; !inner_error && i < n_properties; i++) {
4808 TrackerClass *service;
4809
4810 if (!tracker_property_get_multiple_values (properties[i]))
4811 continue;
4812
4813 service = tracker_property_get_domain (properties[i]);
4814 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
4815 "DELETE FROM \"%s\".\"%s_%s\"",
4816 graph,
4817 tracker_class_get_name (service),
4818 tracker_property_get_name (properties[i]));
4819 if (!stmt)
4820 goto out;
4821
4822 tracker_db_statement_execute (stmt, &inner_error);
4823 g_object_unref (stmt);
4824 }
4825
4826 tracker_db_interface_execute_query (iface,
4827 &inner_error,
4828 "DELETE FROM \"%s\".Refcount",
4829 graph);
4830 out:
4831
4832 if (inner_error) {
4833 g_propagate_error (error, inner_error);
4834 return FALSE;
4835 }
4836
4837 return TRUE;
4838 }
4839
4840 gboolean
tracker_data_manager_copy_graph(TrackerDataManager * manager,const gchar * source,const gchar * destination,GError ** error)4841 tracker_data_manager_copy_graph (TrackerDataManager *manager,
4842 const gchar *source,
4843 const gchar *destination,
4844 GError **error)
4845 {
4846 TrackerOntologies *ontologies = manager->ontologies;
4847 TrackerClass **classes;
4848 TrackerProperty **properties;
4849 TrackerDBStatement *stmt;
4850 guint i, n_classes, n_properties;
4851 GError *inner_error = NULL;
4852 TrackerDBInterface *iface;
4853
4854 if (!source)
4855 source = "main";
4856 if (!destination)
4857 destination = "main";
4858
4859 if (strcmp (source, destination) == 0)
4860 return TRUE;
4861
4862 iface = tracker_db_manager_get_writable_db_interface (manager->db_manager);
4863 classes = tracker_ontologies_get_classes (ontologies, &n_classes);
4864 properties = tracker_ontologies_get_properties (ontologies, &n_properties);
4865
4866 for (i = 0; !inner_error && i < n_classes; i++) {
4867 if (g_str_has_prefix (tracker_class_get_name (classes[i]), "xsd:"))
4868 continue;
4869
4870 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
4871 "INSERT OR REPLACE INTO \"%s\".\"%s\" "
4872 "SELECT * from \"%s\".\"%s\"",
4873 destination,
4874 tracker_class_get_name (classes[i]),
4875 source,
4876 tracker_class_get_name (classes[i]));
4877 if (!stmt)
4878 break;
4879
4880 tracker_db_statement_execute (stmt, &inner_error);
4881 g_object_unref (stmt);
4882 }
4883
4884 for (i = 0; !inner_error && i < n_properties; i++) {
4885 TrackerClass *service;
4886
4887 if (!tracker_property_get_multiple_values (properties[i]))
4888 continue;
4889
4890 service = tracker_property_get_domain (properties[i]);
4891 stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE, &inner_error,
4892 "INSERT OR REPLACE INTO \"%s\".\"%s_%s\" "
4893 "SELECT * from \"%s\".\"%s_%s\"",
4894 destination,
4895 tracker_class_get_name (service),
4896 tracker_property_get_name (properties[i]),
4897 source,
4898 tracker_class_get_name (service),
4899 tracker_property_get_name (properties[i]));
4900 if (!stmt)
4901 goto out;
4902
4903 tracker_db_statement_execute (stmt, &inner_error);
4904 g_object_unref (stmt);
4905 }
4906
4907 /* Transfer refcounts */
4908 tracker_db_interface_execute_query (iface,
4909 &inner_error,
4910 "INSERT OR IGNORE INTO \"%s\".Refcount "
4911 "SELECT ID, 0 from \"%s\".Refcount",
4912 destination,
4913 source);
4914 if (inner_error)
4915 goto out;
4916
4917 tracker_db_interface_execute_query (iface,
4918 &inner_error,
4919 "UPDATE \"%s\".Refcount AS B "
4920 "SET Refcount = B.Refcount + A.Refcount "
4921 "FROM (SELECT ID, Refcount FROM \"%s\".Refcount) AS A "
4922 "WHERE B.ID = A.ID",
4923 destination, source);
4924 out:
4925 if (inner_error) {
4926 g_propagate_error (error, inner_error);
4927 return FALSE;
4928 }
4929
4930 return TRUE;
4931 }
4932
4933 guint
tracker_data_manager_get_generation(TrackerDataManager * manager)4934 tracker_data_manager_get_generation (TrackerDataManager *manager)
4935 {
4936 return manager->generation;
4937 }
4938
4939 void
tracker_data_manager_commit_graphs(TrackerDataManager * manager)4940 tracker_data_manager_commit_graphs (TrackerDataManager *manager)
4941 {
4942 if (manager->transaction_graphs) {
4943 g_clear_pointer (&manager->graphs, g_hash_table_unref);
4944 manager->graphs = manager->transaction_graphs;
4945 manager->transaction_graphs = NULL;
4946 manager->generation++;
4947 }
4948 }
4949
4950 void
tracker_data_manager_rollback_graphs(TrackerDataManager * manager)4951 tracker_data_manager_rollback_graphs (TrackerDataManager *manager)
4952 {
4953 g_clear_pointer (&manager->transaction_graphs, g_hash_table_unref);
4954 }
4955
4956 void
tracker_data_manager_release_memory(TrackerDataManager * manager)4957 tracker_data_manager_release_memory (TrackerDataManager *manager)
4958 {
4959 tracker_db_manager_release_memory (manager->db_manager);
4960 }
4961
4962 gboolean
tracker_data_manager_expand_prefix(TrackerDataManager * manager,const gchar * term,GHashTable * prefix_map,gchar ** prefix,gchar ** expanded)4963 tracker_data_manager_expand_prefix (TrackerDataManager *manager,
4964 const gchar *term,
4965 GHashTable *prefix_map,
4966 gchar **prefix,
4967 gchar **expanded)
4968 {
4969 const gchar *sep, *expanded_ns = NULL;
4970 TrackerOntologies *ontologies;
4971 TrackerNamespace **namespaces;
4972 guint n_namespaces, i;
4973 gchar *ns;
4974
4975 sep = strchr (term, ':');
4976
4977 if (sep) {
4978 ns = g_strndup (term, sep - term);
4979 sep++;
4980 } else {
4981 ns = g_strdup (term);
4982 }
4983
4984 if (prefix_map)
4985 expanded_ns = g_hash_table_lookup (prefix_map, ns);
4986
4987 if (!expanded_ns) {
4988 ontologies = tracker_data_manager_get_ontologies (manager);
4989 namespaces = tracker_ontologies_get_namespaces (ontologies, &n_namespaces);
4990
4991 for (i = 0; i < n_namespaces; i++) {
4992 if (!g_str_equal (ns, tracker_namespace_get_prefix (namespaces[i])))
4993 continue;
4994
4995 expanded_ns = tracker_namespace_get_uri (namespaces[i]);
4996
4997 if (prefix_map)
4998 g_hash_table_insert (prefix_map, g_strdup (ns), g_strdup (expanded_ns));
4999 break;
5000 }
5001 }
5002
5003 g_free (ns);
5004
5005 if (!expanded_ns) {
5006 if (prefix)
5007 *prefix = NULL;
5008 if (expanded)
5009 *expanded = g_strdup (term);
5010
5011 return FALSE;
5012 }
5013
5014 if (prefix)
5015 *prefix = g_strdup (expanded_ns);
5016
5017 if (expanded) {
5018 if (sep) {
5019 *expanded = g_strdup_printf ("%s%s", expanded_ns, sep);
5020 } else {
5021 *expanded = g_strdup (expanded_ns);
5022 }
5023 }
5024
5025 return TRUE;
5026 }
5027
5028 TrackerSparqlConnection *
tracker_data_manager_get_remote_connection(TrackerDataManager * data_manager,const gchar * uri,GError ** error)5029 tracker_data_manager_get_remote_connection (TrackerDataManager *data_manager,
5030 const gchar *uri,
5031 GError **error)
5032 {
5033 TrackerSparqlConnection *connection = NULL;
5034 GError *inner_error = NULL;
5035 gchar *uri_scheme = NULL;
5036
5037 g_mutex_lock (&data_manager->connections_lock);
5038
5039 if (!data_manager->cached_connections) {
5040 data_manager->cached_connections =
5041 g_hash_table_new_full (g_str_hash, g_str_equal,
5042 g_free, g_object_unref);
5043 }
5044
5045 connection = g_hash_table_lookup (data_manager->cached_connections, uri);
5046
5047 if (!connection) {
5048 uri_scheme = g_uri_parse_scheme (uri);
5049 if (g_strcmp0 (uri_scheme, "dbus") == 0) {
5050 gchar *bus_name, *object_path;
5051 GDBusConnection *dbus_connection;
5052 GBusType bus_type;
5053
5054 if (!tracker_util_parse_dbus_uri (uri,
5055 &bus_type,
5056 &bus_name, &object_path)) {
5057 g_set_error (&inner_error,
5058 TRACKER_SPARQL_ERROR,
5059 TRACKER_SPARQL_ERROR_PARSE,
5060 "Failed to parse uri '%s'",
5061 uri);
5062 goto fail;
5063 }
5064
5065 if (!g_dbus_is_name (bus_name)) {
5066 g_set_error (&inner_error,
5067 TRACKER_SPARQL_ERROR,
5068 TRACKER_SPARQL_ERROR_PARSE,
5069 "Invalid bus name '%s'",
5070 bus_name);
5071 goto fail;
5072 }
5073
5074 dbus_connection = g_bus_get_sync (bus_type, NULL, &inner_error);
5075 if (!dbus_connection)
5076 goto fail;
5077
5078 connection = tracker_sparql_connection_bus_new (bus_name, object_path,
5079 dbus_connection, &inner_error);
5080 g_free (bus_name);
5081 g_free (object_path);
5082
5083 if (!connection)
5084 goto fail;
5085 } else if (g_strcmp0 (uri_scheme, "http") == 0) {
5086 connection = tracker_sparql_connection_remote_new (uri);
5087 }
5088
5089 if (!connection) {
5090 g_set_error (&inner_error,
5091 TRACKER_SPARQL_ERROR,
5092 TRACKER_SPARQL_ERROR_UNSUPPORTED,
5093 "Unsupported uri '%s'",
5094 uri);
5095 goto fail;
5096 }
5097
5098 g_hash_table_insert (data_manager->cached_connections,
5099 g_strdup (uri),
5100 connection);
5101 }
5102
5103 fail:
5104 g_mutex_unlock (&data_manager->connections_lock);
5105 g_free (uri_scheme);
5106
5107 if (inner_error)
5108 g_propagate_error (error, inner_error);
5109
5110 return connection;
5111 }
5112