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