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