tracker-0.16.2/src/libtracker-data/tracker-data-update.c

Location Tool Test ID Function Issue
tracker-data-update.c:975:6 gcc deprecated-declarations tracker_data_resource_buffer_flush 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:1314:3 gcc deprecated-declarations value_set_add_value 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:1334:3 gcc deprecated-declarations value_set_remove_value 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:1368:2 gcc deprecated-declarations get_property_values 'g_value_array_new' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:69): Use 'GArray' instead
tracker-data-update.c:1696:3 gcc deprecated-declarations cache_insert_metadata_decomposed 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:1754:3 gcc deprecated-declarations delete_first_object 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:2166:4 gcc deprecated-declarations cache_delete_resource_type_full 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:2276:34 clang-analyzer Null pointer passed as an argument to a 'nonnull' parameter
tracker-data-update.c:2309:3 gcc deprecated-declarations resource_buffer_switch 'g_value_array_free' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:72): Use 'GArray' instead
tracker-data-update.c:2318:5 clang-analyzer Value stored to 'graph_id' is never read
tracker-data-update.c:2705:4 clang-analyzer Value stored to 'graph_id' is never read*
tracker-data-update.c:2925:4 clang-analyzer Value stored to 'graph_id' is never read
tracker-data-update.c:2948:5 gcc deprecated-declarations tracker_data_update_statement_with_uri 'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
tracker-data-update.c:3304:2 clang-analyzer Value stored to 'iface' is never read
   1 /*
   2  * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
   3  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
   4  *
   5  * This library is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU Lesser General Public
   7  * License as published by the Free Software Foundation; either
   8  * version 2.1 of the License, or (at your option) any later version.
   9  *
  10  * This library is distributed in the hope that it will be useful,
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * Lesser General Public License for more details.
  14  *
  15  * You should have received a copy of the GNU Lesser General Public
  16  * License along with this library; if not, write to the
  17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18  * Boston, MA  02110-1301, USA.
  19  */
  20 
  21 #include "config.h"
  22 
  23 #include <string.h>
  24 #include <stdlib.h>
  25 #include <math.h>
  26 #include <time.h>
  27 
  28 #include <libtracker-common/tracker-date-time.h>
  29 #include <libtracker-common/tracker-file-utils.h>
  30 #include <libtracker-common/tracker-ontologies.h>
  31 
  32 #include <libtracker-miner/tracker-miner-common.h>
  33 
  34 #include "tracker-class.h"
  35 #include "tracker-data-manager.h"
  36 #include "tracker-data-update.h"
  37 #include "tracker-data-query.h"
  38 #include "tracker-db-interface-sqlite.h"
  39 #include "tracker-db-manager.h"
  40 #include "tracker-db-journal.h"
  41 #include "tracker-ontologies.h"
  42 #include "tracker-property.h"
  43 #include "tracker-sparql-query.h"
  44 
  45 #define RDF_PREFIX TRACKER_RDF_PREFIX
  46 #define RDFS_PREFIX TRACKER_RDFS_PREFIX
  47 #define TRACKER_PREFIX TRACKER_TRACKER_PREFIX
  48 #define NAO_PREFIX TRACKER_NAO_PREFIX
  49 #define NAO_LAST_MODIFIED NAO_PREFIX "lastModified"
  50 #define RDF_PREFIX TRACKER_RDF_PREFIX
  51 #define RDF_PROPERTY RDF_PREFIX "Property"
  52 #define RDF_TYPE RDF_PREFIX "type"
  53 
  54 typedef struct _TrackerDataUpdateBuffer TrackerDataUpdateBuffer;
  55 typedef struct _TrackerDataUpdateBufferResource TrackerDataUpdateBufferResource;
  56 typedef struct _TrackerDataUpdateBufferPredicate TrackerDataUpdateBufferPredicate;
  57 typedef struct _TrackerDataUpdateBufferProperty TrackerDataUpdateBufferProperty;
  58 typedef struct _TrackerDataUpdateBufferTable TrackerDataUpdateBufferTable;
  59 typedef struct _TrackerDataBlankBuffer TrackerDataBlankBuffer;
  60 typedef struct _TrackerStatementDelegate TrackerStatementDelegate;
  61 typedef struct _TrackerCommitDelegate TrackerCommitDelegate;
  62 
  63 struct _TrackerDataUpdateBuffer {
  64 	/* string -> integer */
  65 	GHashTable *resource_cache;
  66 	/* string -> TrackerDataUpdateBufferResource */
  67 	GHashTable *resources;
  68 	/* integer -> TrackerDataUpdateBufferResource */
  69 	GHashTable *resources_by_id;
  70 
  71 	/* the following two fields are valid per sqlite transaction, not just for same subject */
  72 	/* TrackerClass -> integer */
  73 	GHashTable *class_counts;
  74 
  75 #if HAVE_TRACKER_FTS
  76 	gboolean fts_ever_updated;
  77 #endif
  78 };
  79 
  80 struct _TrackerDataUpdateBufferResource {
  81 	const gchar *subject;
  82 	gint id;
  83 	gboolean create;
  84 	gboolean modified;
  85 	/* TrackerProperty -> GValueArray */
  86 	GHashTable *predicates;
  87 	/* string -> TrackerDataUpdateBufferTable */
  88 	GHashTable *tables;
  89 	/* TrackerClass */
  90 	GPtrArray *types;
  91 
  92 #if HAVE_TRACKER_FTS
  93 	gboolean fts_updated;
  94 #endif
  95 };
  96 
  97 struct _TrackerDataUpdateBufferProperty {
  98 	const gchar *name;
  99 	GValue value;
 100 	gint graph;
 101 	gboolean date_time : 1;
 102 
 103 #if HAVE_TRACKER_FTS
 104 	gboolean fts : 1;
 105 #endif
 106 };
 107 
 108 struct _TrackerDataUpdateBufferTable {
 109 	gboolean insert;
 110 	gboolean delete_row;
 111 	gboolean delete_value;
 112 	gboolean multiple_values;
 113 	TrackerClass *class;
 114 	/* TrackerDataUpdateBufferProperty */
 115 	GArray *properties;
 116 };
 117 
 118 /* buffer for anonymous blank nodes
 119  * that are not yet in the database */
 120 struct _TrackerDataBlankBuffer {
 121 	GHashTable *table;
 122 	gchar *subject;
 123 	/* string */
 124 	GArray *predicates;
 125 	/* string */
 126 	GArray *objects;
 127 	/* string */
 128 	GArray *graphs;
 129 };
 130 
 131 struct _TrackerStatementDelegate {
 132 	TrackerStatementCallback callback;
 133 	gpointer user_data;
 134 };
 135 
 136 struct _TrackerCommitDelegate {
 137 	TrackerCommitCallback callback;
 138 	gpointer user_data;
 139 };
 140 
 141 typedef struct {
 142 	gchar *graph;
 143 	gchar *subject;
 144 	gchar *predicate;
 145 	gchar *object;
 146 	gboolean is_uri;
 147 } QueuedStatement;
 148 
 149 static gboolean in_transaction = FALSE;
 150 static gboolean in_ontology_transaction = FALSE;
 151 static gboolean in_journal_replay = FALSE;
 152 static TrackerDataUpdateBuffer update_buffer;
 153 /* current resource */
 154 static TrackerDataUpdateBufferResource *resource_buffer;
 155 static TrackerDataBlankBuffer blank_buffer;
 156 static time_t resource_time = 0;
 157 static gint transaction_modseq = 0;
 158 static gboolean has_persistent = TRUE;
 159 
 160 static GPtrArray *insert_callbacks = NULL;
 161 static GPtrArray *delete_callbacks = NULL;
 162 static GPtrArray *commit_callbacks = NULL;
 163 static GPtrArray *rollback_callbacks = NULL;
 164 static gint max_service_id = 0;
 165 static gint max_ontology_id = 0;
 166 
 167 static gint         ensure_resource_id         (const gchar      *uri,
 168                                                 gboolean         *create);
 169 static void         cache_insert_value         (const gchar      *table_name,
 170                                                 const gchar      *field_name,
 171                                                 gboolean          transient,
 172                                                 GValue           *value,
 173                                                 gint              graph,
 174                                                 gboolean          multiple_values,
 175                                                 gboolean          fts,
 176                                                 gboolean          date_time);
 177 static GValueArray *get_old_property_values    (TrackerProperty  *property,
 178                                                 GError          **error);
 179 static gchar*       gvalue_to_string           (TrackerPropertyType  type,
 180                                                 GValue           *gvalue);
 181 static gboolean     delete_metadata_decomposed (TrackerProperty  *property,
 182                                                 const gchar      *value,
 183                                                 gint              value_id,
 184                                                 GError          **error);
 185 static void         resource_buffer_switch     (const gchar *graph,
 186                                                 gint         graph_id,
 187                                                 const gchar *subject,
 188                                                 gint         subject_id);
 189 
 190 void
 191 tracker_data_add_commit_statement_callback (TrackerCommitCallback    callback,
 192                                             gpointer                 user_data)
 193 {
 194 	TrackerCommitDelegate *delegate = g_new0 (TrackerCommitDelegate, 1);
 195 
 196 	if (!commit_callbacks) {
 197 		commit_callbacks = g_ptr_array_new ();
 198 	}
 199 
 200 	delegate->callback = callback;
 201 	delegate->user_data = user_data;
 202 
 203 	g_ptr_array_add (commit_callbacks, delegate);
 204 }
 205 
 206 void
 207 tracker_data_remove_commit_statement_callback (TrackerCommitCallback callback,
 208                                                gpointer              user_data)
 209 {
 210 	TrackerCommitDelegate *delegate;
 211 	guint i;
 212 	gboolean found = FALSE;
 213 
 214 	if (!commit_callbacks) {
 215 		return;
 216 	}
 217 
 218 	for (i = 0; i < commit_callbacks->len; i++) {
 219 		delegate = g_ptr_array_index (commit_callbacks, i);
 220 		if (delegate->callback == callback && delegate->user_data == user_data) {
 221 			found = TRUE;
 222 			break;
 223 		}
 224 	}
 225 
 226 	if (found) {
 227 		g_free (delegate);
 228 		g_ptr_array_remove_index (commit_callbacks, i);
 229 	}
 230 }
 231 
 232 void
 233 tracker_data_add_rollback_statement_callback (TrackerCommitCallback    callback,
 234                                               gpointer                 user_data)
 235 {
 236 	TrackerCommitDelegate *delegate = g_new0 (TrackerCommitDelegate, 1);
 237 
 238 	if (!rollback_callbacks) {
 239 		rollback_callbacks = g_ptr_array_new ();
 240 	}
 241 
 242 	delegate->callback = callback;
 243 	delegate->user_data = user_data;
 244 
 245 	g_ptr_array_add (rollback_callbacks, delegate);
 246 }
 247 
 248 
 249 void
 250 tracker_data_remove_rollback_statement_callback (TrackerCommitCallback callback,
 251                                                  gpointer              user_data)
 252 {
 253 	TrackerCommitDelegate *delegate;
 254 	guint i;
 255 	gboolean found = FALSE;
 256 
 257 	if (!rollback_callbacks) {
 258 		return;
 259 	}
 260 
 261 	for (i = 0; i < rollback_callbacks->len; i++) {
 262 		delegate = g_ptr_array_index (rollback_callbacks, i);
 263 		if (delegate->callback == callback && delegate->user_data == user_data) {
 264 			found = TRUE;
 265 			break;
 266 		}
 267 	}
 268 
 269 	if (found) {
 270 		g_free (delegate);
 271 		g_ptr_array_remove_index (rollback_callbacks, i);
 272 	}
 273 }
 274 
 275 void
 276 tracker_data_add_insert_statement_callback (TrackerStatementCallback callback,
 277                                             gpointer                 user_data)
 278 {
 279 	TrackerStatementDelegate *delegate = g_new0 (TrackerStatementDelegate, 1);
 280 
 281 	if (!insert_callbacks) {
 282 		insert_callbacks = g_ptr_array_new ();
 283 	}
 284 
 285 	delegate->callback = callback;
 286 	delegate->user_data = user_data;
 287 
 288 	g_ptr_array_add (insert_callbacks, delegate);
 289 }
 290 
 291 void
 292 tracker_data_remove_insert_statement_callback (TrackerStatementCallback callback,
 293                                                gpointer                 user_data)
 294 {
 295 	TrackerStatementDelegate *delegate;
 296 	guint i;
 297 	gboolean found = FALSE;
 298 
 299 	if (!insert_callbacks) {
 300 		return;
 301 	}
 302 
 303 	for (i = 0; i < insert_callbacks->len; i++) {
 304 		delegate = g_ptr_array_index (insert_callbacks, i);
 305 		if (delegate->callback == callback && delegate->user_data == user_data) {
 306 			found = TRUE;
 307 			break;
 308 		}
 309 	}
 310 
 311 	if (found) {
 312 		g_free (delegate);
 313 		g_ptr_array_remove_index (insert_callbacks, i);
 314 	}
 315 }
 316 
 317 void
 318 tracker_data_add_delete_statement_callback (TrackerStatementCallback callback,
 319                                             gpointer                 user_data)
 320 {
 321 	TrackerStatementDelegate *delegate = g_new0 (TrackerStatementDelegate, 1);
 322 
 323 	if (!delete_callbacks) {
 324 		delete_callbacks = g_ptr_array_new ();
 325 	}
 326 
 327 	delegate->callback = callback;
 328 	delegate->user_data = user_data;
 329 
 330 	g_ptr_array_add (delete_callbacks, delegate);
 331 }
 332 
 333 void
 334 tracker_data_remove_delete_statement_callback (TrackerStatementCallback callback,
 335                                                gpointer                 user_data)
 336 {
 337 	TrackerStatementDelegate *delegate;
 338 	guint i;
 339 	gboolean found = FALSE;
 340 
 341 	if (!delete_callbacks) {
 342 		return;
 343 	}
 344 
 345 	for (i = 0; i < delete_callbacks->len; i++) {
 346 		delegate = g_ptr_array_index (delete_callbacks, i);
 347 		if (delegate->callback == callback && delegate->user_data == user_data) {
 348 			found = TRUE;
 349 			break;
 350 		}
 351 	}
 352 
 353 	if (found) {
 354 		g_free (delegate);
 355 		g_ptr_array_remove_index (delete_callbacks, i);
 356 	}
 357 }
 358 
 359 static gint
 360 tracker_data_update_get_new_service_id (void)
 361 {
 362 	TrackerDBCursor    *cursor = NULL;
 363 	TrackerDBInterface *iface;
 364 	TrackerDBStatement *stmt;
 365 	GError *error = NULL;
 366 
 367 	if (in_ontology_transaction) {
 368 		if (G_LIKELY (max_ontology_id != 0)) {
 369 			return ++max_ontology_id;
 370 		}
 371 
 372 		iface = tracker_db_manager_get_db_interface ();
 373 
 374 		stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
 375 		                                              "SELECT MAX(ID) AS A FROM Resource WHERE ID <= %d", TRACKER_ONTOLOGIES_MAX_ID);
 376 
 377 		if (stmt) {
 378 			cursor = tracker_db_statement_start_cursor (stmt, &error);
 379 			g_object_unref (stmt);
 380 		}
 381 
 382 		if (cursor) {
 383 			if (tracker_db_cursor_iter_next (cursor, NULL, &error)) {
 384 				max_ontology_id = MAX (tracker_db_cursor_get_int (cursor, 0), max_ontology_id);
 385 			}
 386 
 387 			g_object_unref (cursor);
 388 		}
 389 
 390 		if (G_UNLIKELY (error)) {
 391 			g_warning ("Could not get new resource ID for ontology transaction: %s\n", error->message);
 392 			g_error_free (error);
 393 		}
 394 
 395 		return ++max_ontology_id;
 396 	} else {
 397 		if (G_LIKELY (max_service_id != 0)) {
 398 			return ++max_service_id;
 399 		}
 400 
 401 		max_service_id = TRACKER_ONTOLOGIES_MAX_ID;
 402 
 403 		iface = tracker_db_manager_get_db_interface ();
 404 
 405 		stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
 406 		                                              "SELECT MAX(ID) AS A FROM Resource");
 407 
 408 		if (stmt) {
 409 			cursor = tracker_db_statement_start_cursor (stmt, &error);
 410 			g_object_unref (stmt);
 411 		}
 412 
 413 		if (cursor) {
 414 			if (tracker_db_cursor_iter_next (cursor, NULL, &error)) {
 415 				max_service_id = MAX (tracker_db_cursor_get_int (cursor, 0), max_service_id);
 416 			}
 417 
 418 			g_object_unref (cursor);
 419 		}
 420 
 421 		if (G_UNLIKELY (error)) {
 422 			g_warning ("Could not get new resource ID: %s\n", error->message);
 423 			g_error_free (error);
 424 		}
 425 
 426 		return ++max_service_id;
 427 	}
 428 }
 429 
 430 static gint
 431 tracker_data_update_get_next_modseq (void)
 432 {
 433 	TrackerDBCursor    *cursor = NULL;
 434 	TrackerDBInterface *temp_iface;
 435 	TrackerDBStatement *stmt;
 436 	GError             *error = NULL;
 437 	gint                max_modseq = 0;
 438 
 439 	temp_iface = tracker_db_manager_get_db_interface ();
 440 
 441 	stmt = tracker_db_interface_create_statement (temp_iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error,
 442 	                                              "SELECT MAX(\"tracker:modified\") AS A FROM \"rdfs:Resource\"");
 443 
 444 	if (stmt) {
 445 		cursor = tracker_db_statement_start_cursor (stmt, &error);
 446 		g_object_unref (stmt);
 447 	}
 448 
 449 	if (cursor) {
 450 		if (tracker_db_cursor_iter_next (cursor, NULL, &error)) {
 451 			max_modseq = MAX (tracker_db_cursor_get_int (cursor, 0), max_modseq);
 452 		}
 453 
 454 		g_object_unref (cursor);
 455 	}
 456 
 457 	if (G_UNLIKELY (error)) {
 458 		g_warning ("Could not get new resource ID: %s\n", error->message);
 459 		g_error_free (error);
 460 	}
 461 
 462 	return ++max_modseq;
 463 }
 464 
 465 void
 466 tracker_data_update_shutdown (void)
 467 {
 468 	max_service_id = 0;
 469 	max_ontology_id = 0;
 470 	transaction_modseq = 0;
 471 }
 472 
 473 static gint
 474 get_transaction_modseq (void)
 475 {
 476 	if (G_UNLIKELY (transaction_modseq == 0)) {
 477 		transaction_modseq = tracker_data_update_get_next_modseq ();
 478 	}
 479 
 480 	/* Always use 1 for ontology transactions */
 481 	if (in_ontology_transaction) {
 482 		return 1;
 483 	}
 484 
 485 	return transaction_modseq;
 486 }
 487 
 488 static TrackerDataUpdateBufferTable *
 489 cache_table_new (gboolean multiple_values)
 490 {
 491 	TrackerDataUpdateBufferTable *table;
 492 
 493 	table = g_slice_new0 (TrackerDataUpdateBufferTable);
 494 	table->multiple_values = multiple_values;
 495 	table->properties = g_array_sized_new (FALSE, FALSE, sizeof (TrackerDataUpdateBufferProperty), 4);
 496 
 497 	return table;
 498 }
 499 
 500 static void
 501 cache_table_free (TrackerDataUpdateBufferTable *table)
 502 {
 503 	TrackerDataUpdateBufferProperty *property;
 504 	gint                            i;
 505 
 506 	for (i = 0; i < table->properties->len; i++) {
 507 		property = &g_array_index (table->properties, TrackerDataUpdateBufferProperty, i);
 508 		g_value_unset (&property->value);
 509 	}
 510 
 511 	g_array_free (table->properties, TRUE);
 512 	g_slice_free (TrackerDataUpdateBufferTable, table);
 513 }
 514 
 515 static TrackerDataUpdateBufferTable *
 516 cache_ensure_table (const gchar *table_name,
 517                     gboolean     multiple_values,
 518                     gboolean     transient)
 519 {
 520 	TrackerDataUpdateBufferTable *table;
 521 
 522 	if (!resource_buffer->modified && !transient) {
 523 		/* first modification of this particular resource, update tracker:modified */
 524 
 525 		GValue gvalue = { 0 };
 526 
 527 		resource_buffer->modified = TRUE;
 528 
 529 		g_value_init (&gvalue, G_TYPE_INT64);
 530 		g_value_set_int64 (&gvalue, get_transaction_modseq ());
 531 		cache_insert_value ("rdfs:Resource", "tracker:modified", TRUE, &gvalue,
 532 		                    0,
 533 		                    FALSE, FALSE, FALSE);
 534 	}
 535 
 536 	table = g_hash_table_lookup (resource_buffer->tables, table_name);
 537 	if (table == NULL) {
 538 		table = cache_table_new (multiple_values);
 539 		g_hash_table_insert (resource_buffer->tables, g_strdup (table_name), table);
 540 		table->insert = multiple_values;
 541 	}
 542 
 543 	return table;
 544 }
 545 
 546 static void
 547 cache_insert_row (TrackerClass *class)
 548 {
 549 	TrackerDataUpdateBufferTable *table;
 550 
 551 	table = cache_ensure_table (tracker_class_get_name (class), FALSE, FALSE);
 552 	table->class = class;
 553 	table->insert = TRUE;
 554 }
 555 
 556 static void
 557 cache_insert_value (const gchar            *table_name,
 558                     const gchar            *field_name,
 559                     gboolean                transient,
 560                     GValue                 *value,
 561                     gint                    graph,
 562                     gboolean                multiple_values,
 563                     gboolean                fts,
 564                     gboolean                date_time)
 565 {
 566 	TrackerDataUpdateBufferTable    *table;
 567 	TrackerDataUpdateBufferProperty  property;
 568 
 569 	/* No need to strdup here, the incoming string is either always static, or
 570 	 * long-standing as tracker_property_get_name return value. */
 571 	property.name = field_name;
 572 
 573 	property.value = *value;
 574 	property.graph = graph;
 575 #if HAVE_TRACKER_FTS
 576 	property.fts = fts;
 577 #endif
 578 	property.date_time = date_time;
 579 
 580 	table = cache_ensure_table (table_name, multiple_values, transient);
 581 	g_array_append_val (table->properties, property);
 582 }
 583 
 584 static void
 585 cache_delete_row (TrackerClass *class)
 586 {
 587 	TrackerDataUpdateBufferTable    *table;
 588 
 589 	table = cache_ensure_table (tracker_class_get_name (class), FALSE, FALSE);
 590 	table->class = class;
 591 	table->delete_row = TRUE;
 592 }
 593 
 594 static void
 595 cache_delete_value (const gchar            *table_name,
 596                     const gchar            *field_name,
 597                     gboolean                transient,
 598                     GValue                 *value,
 599                     gboolean                multiple_values,
 600                     gboolean                fts,
 601                     gboolean                date_time)
 602 {
 603 	TrackerDataUpdateBufferTable    *table;
 604 	TrackerDataUpdateBufferProperty  property;
 605 
 606 	property.name = field_name;
 607 	property.value = *value;
 608 	property.graph = 0;
 609 #if HAVE_TRACKER_FTS
 610 	property.fts = fts;
 611 #endif
 612 	property.date_time = date_time;
 613 
 614 	table = cache_ensure_table (table_name, multiple_values, transient);
 615 	table->delete_value = TRUE;
 616 	g_array_append_val (table->properties, property);
 617 }
 618 
 619 static gint
 620 query_resource_id (const gchar *uri)
 621 {
 622 	gint id;
 623 
 624 	id = GPOINTER_TO_INT (g_hash_table_lookup (update_buffer.resource_cache, uri));
 625 
 626 	if (id == 0) {
 627 		id = tracker_data_query_resource_id (uri);
 628 
 629 		if (id) {
 630 			g_hash_table_insert (update_buffer.resource_cache, g_strdup (uri), GINT_TO_POINTER (id));
 631 		}
 632 	}
 633 
 634 	return id;
 635 }
 636 
 637 static gint
 638 ensure_resource_id (const gchar *uri,
 639                     gboolean    *create)
 640 {
 641 	TrackerDBInterface *iface;
 642 	TrackerDBStatement *stmt;
 643 	GError *error = NULL;
 644 	gint id;
 645 
 646 	id = query_resource_id (uri);
 647 
 648 	if (create) {
 649 		*create = (id == 0);
 650 	}
 651 
 652 	if (id == 0) {
 653 		iface = tracker_db_manager_get_db_interface ();
 654 
 655 		id = tracker_data_update_get_new_service_id ();
 656 		stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error,
 657 		                                              "INSERT INTO Resource (ID, Uri) VALUES (?, ?)");
 658 
 659 		if (stmt) {
 660 			tracker_db_statement_bind_int (stmt, 0, id);
 661 			tracker_db_statement_bind_text (stmt, 1, uri);
 662 			tracker_db_statement_execute (stmt, &error);
 663 			g_object_unref (stmt);
 664 		}
 665 
 666 		if (error) {
 667 			g_critical ("Could not ensure resource existence: %s", error->message);
 668 			g_error_free (error);
 669 		}
 670 
 671 #ifndef DISABLE_JOURNAL
 672 		if (!in_journal_replay) {
 673 			tracker_db_journal_append_resource (id, uri);
 674 		}
 675 #endif /* DISABLE_JOURNAL */
 676 
 677 		g_hash_table_insert (update_buffer.resource_cache, g_strdup (uri), GINT_TO_POINTER (id));
 678 	}
 679 
 680 	return id;
 681 }
 682 
 683 static void
 684 statement_bind_gvalue (TrackerDBStatement *stmt,
 685                        gint               *idx,
 686                        const GValue       *value)
 687 {
 688 	GType type;
 689 
 690 	type = G_VALUE_TYPE (value);
 691 	switch (type) {
 692 	case G_TYPE_STRING:
 693 		tracker_db_statement_bind_text (stmt, (*idx)++, g_value_get_string (value));
 694 		break;
 695 	case G_TYPE_INT64:
 696 		tracker_db_statement_bind_int (stmt, (*idx)++, g_value_get_int64 (value));
 697 		break;
 698 	case G_TYPE_DOUBLE:
 699 		tracker_db_statement_bind_double (stmt, (*idx)++, g_value_get_double (value));
 700 		break;
 701 	default:
 702 		if (type == TRACKER_TYPE_DATE_TIME) {
 703 			tracker_db_statement_bind_double (stmt, (*idx)++, tracker_date_time_get_time (value));
 704 			tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_date (value));
 705 			tracker_db_statement_bind_int (stmt, (*idx)++, tracker_date_time_get_local_time (value));
 706 		} else {
 707 			g_warning ("Unknown type for binding: %s\n", G_VALUE_TYPE_NAME (value));
 708 		}
 709 		break;
 710 	}
 711 }
 712 
 713 static void
 714 add_class_count (TrackerClass *class,
 715                  gint          count)
 716 {
 717 	gint old_count_entry;
 718 
 719 	tracker_class_set_count (class, tracker_class_get_count (class) + count);
 720 
 721 	/* update class_counts table so that the count change can be reverted in case of rollback */
 722 	if (!update_buffer.class_counts) {
 723 		update_buffer.class_counts = g_hash_table_new (g_direct_hash, g_direct_equal);
 724 	}
 725 
 726 	old_count_entry = GPOINTER_TO_INT (g_hash_table_lookup (update_buffer.class_counts, class));
 727 	g_hash_table_insert (update_buffer.class_counts, class,
 728 	                     GINT_TO_POINTER (old_count_entry + count));
 729 }
 730 
 731 static void
 732 tracker_data_resource_buffer_flush (GError **error)
 733 {
 734 	TrackerDBInterface             *iface;
 735 	TrackerDBStatement             *stmt;
 736 	TrackerDataUpdateBufferTable    *table;
 737 	TrackerDataUpdateBufferProperty *property;
 738 	GHashTableIter                  iter;
 739 	const gchar                    *table_name;
 740 	gint                            i, param;
 741 	GError                         *actual_error = NULL;
 742 
 743 	iface = tracker_db_manager_get_db_interface ();
 744 
 745 	g_hash_table_iter_init (&iter, resource_buffer->tables);
 746 	while (g_hash_table_iter_next (&iter, (gpointer*) &table_name, (gpointer*) &table)) {
 747 		if (table->multiple_values) {
 748 			for (i = 0; i < table->properties->len; i++) {
 749 				property = &g_array_index (table->properties, TrackerDataUpdateBufferProperty, i);
 750 
 751 				if (table->delete_value) {
 752 					/* delete rows for multiple value properties */
 753 					stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 754 					                                              "DELETE FROM \"%s\" WHERE ID = ? AND \"%s\" = ?",
 755 					                                              table_name,
 756 					                                              property->name);
 757 				} else if (property->date_time) {
 758 					stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 759 					                                              "INSERT OR IGNORE INTO \"%s\" (ID, \"%s\", \"%s:localDate\", \"%s:localTime\", \"%s:graph\") VALUES (?, ?, ?, ?, ?)",
 760 					                                              table_name,
 761 					                                              property->name,
 762 					                                              property->name,
 763 					                                              property->name,
 764 					                                              property->name);
 765 				} else {
 766 					stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 767 					                                              "INSERT OR IGNORE INTO \"%s\" (ID, \"%s\", \"%s:graph\") VALUES (?, ?, ?)",
 768 					                                              table_name,
 769 					                                              property->name,
 770 					                                              property->name);
 771 				}
 772 
 773 				if (actual_error) {
 774 					g_propagate_error (error, actual_error);
 775 					return;
 776 				}
 777 
 778 				param = 0;
 779 
 780 				tracker_db_statement_bind_int (stmt, param++, resource_buffer->id);
 781 				statement_bind_gvalue (stmt, &param, &property->value);
 782 
 783 				if (property->graph != 0) {
 784 					tracker_db_statement_bind_int (stmt, param++, property->graph);
 785 				} else {
 786 					tracker_db_statement_bind_null (stmt, param++);
 787 				}
 788 
 789 				tracker_db_statement_execute (stmt, &actual_error);
 790 				g_object_unref (stmt);
 791 
 792 				if (actual_error) {
 793 					g_propagate_error (error, actual_error);
 794 					return;
 795 				}
 796 			}
 797 		} else {
 798 			GString *sql, *values_sql;
 799 
 800 			if (table->delete_row) {
 801 				/* remove entry from rdf:type table */
 802 				stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 803 				                                              "DELETE FROM \"rdfs:Resource_rdf:type\" WHERE ID = ? AND \"rdf:type\" = ?");
 804 
 805 				if (stmt) {
 806 					tracker_db_statement_bind_int (stmt, 0, resource_buffer->id);
 807 					tracker_db_statement_bind_int (stmt, 1, ensure_resource_id (tracker_class_get_uri (table->class), NULL));
 808 					tracker_db_statement_execute (stmt, &actual_error);
 809 					g_object_unref (stmt);
 810 				}
 811 
 812 				if (actual_error) {
 813 					g_propagate_error (error, actual_error);
 814 					return;
 815 				}
 816 
 817 				if (table->class) {
 818 					add_class_count (table->class, -1);
 819 				}
 820 
 821 				/* remove row from class table */
 822 				stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 823 				                                              "DELETE FROM \"%s\" WHERE ID = ?", table_name);
 824 
 825 				if (stmt) {
 826 					tracker_db_statement_bind_int (stmt, 0, resource_buffer->id);
 827 					tracker_db_statement_execute (stmt, &actual_error);
 828 					g_object_unref (stmt);
 829 				}
 830 
 831 				if (actual_error) {
 832 					g_propagate_error (error, actual_error);
 833 					return;
 834 				}
 835 
 836 				continue;
 837 			}
 838 
 839 			if (table->insert) {
 840 				sql = g_string_new ("INSERT INTO \"");
 841 				values_sql = g_string_new ("VALUES (?");
 842 			} else {
 843 				sql = g_string_new ("UPDATE \"");
 844 				values_sql = NULL;
 845 			}
 846 
 847 			g_string_append (sql, table_name);
 848 
 849 			if (table->insert) {
 850 				g_string_append (sql, "\" (ID");
 851 
 852 				if (strcmp (table_name, "rdfs:Resource") == 0) {
 853 					g_string_append (sql, ", \"tracker:added\", \"tracker:modified\", Available");
 854 					g_string_append (values_sql, ", ?, ?, 1");
 855 				} else {
 856 				}
 857 			} else {
 858 				g_string_append (sql, "\" SET ");
 859 			}
 860 
 861 			for (i = 0; i < table->properties->len; i++) {
 862 				property = &g_array_index (table->properties, TrackerDataUpdateBufferProperty, i);
 863 				if (table->insert) {
 864 					g_string_append_printf (sql, ", \"%s\"", property->name);
 865 					g_string_append (values_sql, ", ?");
 866 
 867 					if (property->date_time) {
 868 						g_string_append_printf (sql, ", \"%s:localDate\"", property->name);
 869 						g_string_append_printf (sql, ", \"%s:localTime\"", property->name);
 870 						g_string_append (values_sql, ", ?, ?");
 871 					}
 872 
 873 					g_string_append_printf (sql, ", \"%s:graph\"", property->name);
 874 					g_string_append (values_sql, ", ?");
 875 				} else {
 876 					if (i > 0) {
 877 						g_string_append (sql, ", ");
 878 					}
 879 					g_string_append_printf (sql, "\"%s\" = ?", property->name);
 880 
 881 					if (property->date_time) {
 882 						g_string_append_printf (sql, ", \"%s:localDate\" = ?", property->name);
 883 						g_string_append_printf (sql, ", \"%s:localTime\" = ?", property->name);
 884 					}
 885 
 886 					g_string_append_printf (sql, ", \"%s:graph\" = ?", property->name);
 887 				}
 888 			}
 889 
 890 			if (table->insert) {
 891 				g_string_append (sql, ")");
 892 				g_string_append (values_sql, ")");
 893 
 894 				stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 895 				                                              "%s %s", sql->str, values_sql->str);
 896 				g_string_free (sql, TRUE);
 897 				g_string_free (values_sql, TRUE);
 898 			} else {
 899 				g_string_append (sql, " WHERE ID = ?");
 900 
 901 				stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &actual_error,
 902 				                                              "%s", sql->str);
 903 				g_string_free (sql, TRUE);
 904 			}
 905 
 906 			if (actual_error) {
 907 				g_propagate_error (error, actual_error);
 908 				return;
 909 			}
 910 
 911 			if (table->insert) {
 912 				tracker_db_statement_bind_int (stmt, 0, resource_buffer->id);
 913 
 914 				if (strcmp (table_name, "rdfs:Resource") == 0) {
 915 					g_warn_if_fail	(resource_time != 0);
 916 					tracker_db_statement_bind_int (stmt, 1, (gint64) resource_time);
 917 					tracker_db_statement_bind_int (stmt, 2, get_transaction_modseq ());
 918 					param = 3;
 919 				} else {
 920 					param = 1;
 921 				}
 922 			} else {
 923 				param = 0;
 924 			}
 925 
 926 			for (i = 0; i < table->properties->len; i++) {
 927 				property = &g_array_index (table->properties, TrackerDataUpdateBufferProperty, i);
 928 				if (table->delete_value) {
 929 					/* just set value to NULL for single value properties */
 930 					tracker_db_statement_bind_null (stmt, param++);
 931 					if (property->date_time) {
 932 						/* also set localDate and localTime to NULL */
 933 						tracker_db_statement_bind_null (stmt, param++);
 934 						tracker_db_statement_bind_null (stmt, param++);
 935 					}
 936 				} else {
 937 					statement_bind_gvalue (stmt, &param, &property->value);
 938 				}
 939 				if (property->graph != 0) {
 940 					tracker_db_statement_bind_int (stmt, param++, property->graph);
 941 				} else {
 942 					tracker_db_statement_bind_null (stmt, param++);
 943 				}
 944 			}
 945 
 946 			if (!table->insert) {
 947 				tracker_db_statement_bind_int (stmt, param++, resource_buffer->id);
 948 			}
 949 
 950 			tracker_db_statement_execute (stmt, &actual_error);
 951 			g_object_unref (stmt);
 952 
 953 			if (actual_error) {
 954 				g_propagate_error (error, actual_error);
 955 				return;
 956 			}
 957 		}
 958 	}
 959 
 960 #if HAVE_TRACKER_FTS
 961 	if (resource_buffer->fts_updated) {
 962 		TrackerProperty *prop;
 963 		GValueArray *values;
 964 		gboolean create = resource_buffer->create;
 965 		GPtrArray *properties, *text;
 966 
 967 		properties = text = NULL;
 968 		g_hash_table_iter_init (&iter, resource_buffer->predicates);
 969 		while (g_hash_table_iter_next (&iter, (gpointer*) &prop, (gpointer*) &values)) {
 970 			if (tracker_property_get_fulltext_indexed (prop)) {
 971 				GString *fts;
 972 
 973 				fts = g_string_new ("");
 974 				for (i = 0; i < values->n_values; i++) {
 975 					g_string_append (fts, g_value_get_string (g_value_array_get_nth (values, i)));
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
976 g_string_append_c (fts, ' '); 977 } 978 979 if (!properties && !text) { 980 properties = g_ptr_array_new (); 981 text = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); 982 } 983 984 g_ptr_array_add (properties, (gpointer) tracker_property_get_name (prop)); 985 g_ptr_array_add (text, g_string_free (fts, FALSE)); 986 } 987 } 988 989 if (properties && text) { 990 g_ptr_array_add (properties, NULL); 991 g_ptr_array_add (text, NULL); 992 993 tracker_db_interface_sqlite_fts_update_text (iface, 994 resource_buffer->id, 995 (gchar **) properties->pdata, 996 (gchar **) text->pdata, 997 create); 998 update_buffer.fts_ever_updated = TRUE; 999 g_ptr_array_free (properties, TRUE); 1000 g_ptr_array_free (text, TRUE); 1001 } 1002 } 1003 #endif 1004 } 1005 1006 static void resource_buffer_free (TrackerDataUpdateBufferResource *resource) 1007 { 1008 g_hash_table_unref (resource->predicates); 1009 g_hash_table_unref (resource->tables); 1010 resource->subject = NULL; 1011 1012 g_ptr_array_free (resource->types, TRUE); 1013 resource->types = NULL; 1014 1015 g_slice_free (TrackerDataUpdateBufferResource, resource); 1016 } 1017 1018 void 1019 tracker_data_update_buffer_flush (GError **error) 1020 { 1021 GHashTableIter iter; 1022 GError *actual_error = NULL; 1023 1024 if (in_journal_replay) { 1025 g_hash_table_iter_init (&iter, update_buffer.resources_by_id); 1026 while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &resource_buffer)) { 1027 tracker_data_resource_buffer_flush (&actual_error); 1028 if (actual_error) { 1029 g_propagate_error (error, actual_error); 1030 break; 1031 } 1032 } 1033 1034 g_hash_table_remove_all (update_buffer.resources_by_id); 1035 } else { 1036 g_hash_table_iter_init (&iter, update_buffer.resources); 1037 while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &resource_buffer)) { 1038 tracker_data_resource_buffer_flush (&actual_error); 1039 if (actual_error) { 1040 g_propagate_error (error, actual_error); 1041 break; 1042 } 1043 } 1044 1045 g_hash_table_remove_all (update_buffer.resources); 1046 } 1047 resource_buffer = NULL; 1048 } 1049 1050 void 1051 tracker_data_update_buffer_might_flush (GError **error) 1052 { 1053 /* avoid high memory usage by update buffer */ 1054 if (g_hash_table_size (update_buffer.resources) + 1055 g_hash_table_size (update_buffer.resources_by_id) >= 1000) { 1056 tracker_data_update_buffer_flush (error); 1057 } 1058 } 1059 1060 static void 1061 tracker_data_update_buffer_clear (void) 1062 { 1063 g_hash_table_remove_all (update_buffer.resources); 1064 g_hash_table_remove_all (update_buffer.resources_by_id); 1065 g_hash_table_remove_all (update_buffer.resource_cache); 1066 resource_buffer = NULL; 1067 1068 #if HAVE_TRACKER_FTS 1069 update_buffer.fts_ever_updated = FALSE; 1070 #endif 1071 1072 if (update_buffer.class_counts) { 1073 /* revert class count changes */ 1074 1075 GHashTableIter iter; 1076 TrackerClass *class; 1077 gpointer count_ptr; 1078 1079 g_hash_table_iter_init (&iter, update_buffer.class_counts); 1080 while (g_hash_table_iter_next (&iter, (gpointer*) &class, &count_ptr)) { 1081 gint count; 1082 1083 count = GPOINTER_TO_INT (count_ptr); 1084 tracker_class_set_count (class, tracker_class_get_count (class) - count); 1085 } 1086 1087 g_hash_table_remove_all (update_buffer.class_counts); 1088 } 1089 } 1090 1091 static void 1092 tracker_data_blank_buffer_flush (GError **error) 1093 { 1094 /* end of blank node */ 1095 gint i; 1096 gint id; 1097 gchar *subject; 1098 gchar *blank_uri; 1099 const gchar *sha1; 1100 GChecksum *checksum; 1101 GError *actual_error = NULL; 1102 1103 subject = blank_buffer.subject; 1104 blank_buffer.subject = NULL; 1105 1106 /* we share anonymous blank nodes with identical properties 1107 to avoid blowing up the database with duplicates */ 1108 1109 checksum = g_checksum_new (G_CHECKSUM_SHA1); 1110 1111 /* generate hash uri from data to find resource 1112 assumes no collisions due to generally little contents of anonymous nodes */ 1113 for (i = 0; i < blank_buffer.predicates->len; i++) { 1114 if (g_array_index (blank_buffer.graphs, guchar *, i) != NULL) { 1115 g_checksum_update (checksum, g_array_index (blank_buffer.graphs, guchar *, i), -1); 1116 } 1117 1118 g_checksum_update (checksum, g_array_index (blank_buffer.predicates, guchar *, i), -1); 1119 g_checksum_update (checksum, g_array_index (blank_buffer.objects, guchar *, i), -1); 1120 } 1121 1122 sha1 = g_checksum_get_string (checksum); 1123 1124 /* generate name based uuid */ 1125 blank_uri = g_strdup_printf ("urn:uuid:%.8s-%.4s-%.4s-%.4s-%.12s", 1126 sha1, sha1 + 8, sha1 + 12, sha1 + 16, sha1 + 20); 1127 1128 id = tracker_data_query_resource_id (blank_uri); 1129 1130 if (id == 0) { 1131 /* uri not found 1132 replay piled up statements to create resource */ 1133 for (i = 0; i < blank_buffer.predicates->len; i++) { 1134 tracker_data_insert_statement (g_array_index (blank_buffer.graphs, gchar *, i), 1135 blank_uri, 1136 g_array_index (blank_buffer.predicates, gchar *, i), 1137 g_array_index (blank_buffer.objects, gchar *, i), 1138 &actual_error); 1139 if (actual_error) { 1140 break; 1141 } 1142 } 1143 } 1144 1145 /* free piled up statements */ 1146 for (i = 0; i < blank_buffer.predicates->len; i++) { 1147 g_free (g_array_index (blank_buffer.graphs, gchar *, i)); 1148 g_free (g_array_index (blank_buffer.predicates, gchar *, i)); 1149 g_free (g_array_index (blank_buffer.objects, gchar *, i)); 1150 } 1151 g_array_remove_range (blank_buffer.graphs, 0, blank_buffer.graphs->len); 1152 g_array_remove_range (blank_buffer.predicates, 0, blank_buffer.predicates->len); 1153 g_array_remove_range (blank_buffer.objects, 0, blank_buffer.objects->len); 1154 1155 g_hash_table_insert (blank_buffer.table, subject, blank_uri); 1156 g_checksum_free (checksum); 1157 1158 if (actual_error) { 1159 g_propagate_error (error, actual_error); 1160 } 1161 } 1162 1163 static void 1164 cache_create_service_decomposed (TrackerClass *cl, 1165 const gchar *graph, 1166 gint graph_id) 1167 { 1168 TrackerClass **super_classes; 1169 TrackerProperty **domain_indexes; 1170 GValue gvalue = { 0 }; 1171 gint i, final_graph_id, class_id; 1172 1173 /* also create instance of all super classes */ 1174 super_classes = tracker_class_get_super_classes (cl); 1175 while (*super_classes) { 1176 cache_create_service_decomposed (*super_classes, graph, graph_id); 1177 super_classes++; 1178 } 1179 1180 for (i = 0; i < resource_buffer->types->len; i++) { 1181 if (g_ptr_array_index (resource_buffer->types, i) == cl) { 1182 /* ignore duplicate statement */ 1183 return; 1184 } 1185 } 1186 1187 g_ptr_array_add (resource_buffer->types, cl); 1188 1189 g_value_init (&gvalue, G_TYPE_INT64); 1190 1191 cache_insert_row (cl); 1192 1193 final_graph_id = (graph != NULL ? ensure_resource_id (graph, NULL) : graph_id); 1194 1195 /* This is the original, no idea why tracker_class_get_id wasn't used here: 1196 * class_id = ensure_resource_id (tracker_class_get_uri (cl), NULL); */ 1197 1198 class_id = tracker_class_get_id (cl); 1199 1200 g_value_set_int64 (&gvalue, class_id); 1201 cache_insert_value ("rdfs:Resource_rdf:type", "rdf:type", FALSE, &gvalue, 1202 final_graph_id, 1203 TRUE, FALSE, FALSE); 1204 1205 add_class_count (cl, 1); 1206 1207 if (!in_journal_replay && insert_callbacks) { 1208 guint n; 1209 1210 for (n = 0; n < insert_callbacks->len; n++) { 1211 TrackerStatementDelegate *delegate; 1212 1213 delegate = g_ptr_array_index (insert_callbacks, n); 1214 delegate->callback (final_graph_id, graph, resource_buffer->id, resource_buffer->subject, 1215 tracker_property_get_id (tracker_ontologies_get_rdf_type ()), 1216 class_id, 1217 tracker_class_get_uri (cl), 1218 resource_buffer->types, 1219 delegate->user_data); 1220 } 1221 } 1222 1223 /* When a new class created, make sure we propagate to the domain indexes 1224 * the property values already set, if any. */ 1225 domain_indexes = tracker_class_get_domain_indexes (cl); 1226 if (!domain_indexes) { 1227 /* Nothing else to do, return */ 1228 return; 1229 } 1230 1231 while (*domain_indexes) { 1232 GError *error = NULL; 1233 GValueArray *old_values; 1234 1235 /* read existing property values */ 1236 old_values = get_old_property_values (*domain_indexes, &error); 1237 if (error) { 1238 g_critical ("Couldn't get old values for property '%s': '%s'", 1239 tracker_property_get_name (*domain_indexes), 1240 error->message); 1241 g_clear_error (&error); 1242 domain_indexes++; 1243 continue; 1244 } 1245 1246 if (old_values && 1247 old_values->n_values > 0) { 1248 GValue gvalue_copy = { 0 }; 1249 1250 /* Don't expect several values for property which is a domain index */ 1251 g_assert_cmpint (old_values->n_values, ==, 1); 1252 1253 g_debug ("Propagating '%s' property value from '%s' to domain index in '%s'", 1254 tracker_property_get_name (*domain_indexes), 1255 tracker_property_get_table_name (*domain_indexes), 1256 tracker_class_get_name (cl)); 1257 1258 g_value_init (&gvalue_copy, G_VALUE_TYPE (old_values->values)); 1259 g_value_copy (old_values->values, &gvalue_copy); 1260 1261 cache_insert_value (tracker_class_get_name (cl), 1262 tracker_property_get_name (*domain_indexes), 1263 tracker_property_get_transient (*domain_indexes), 1264 &gvalue_copy, 1265 graph != NULL ? ensure_resource_id (graph, NULL) : graph_id, 1266 tracker_property_get_multiple_values (*domain_indexes), 1267 tracker_property_get_fulltext_indexed (*domain_indexes), 1268 tracker_property_get_data_type (*domain_indexes) == TRACKER_PROPERTY_TYPE_DATETIME); 1269 } 1270 1271 domain_indexes++; 1272 } 1273 } 1274 1275 static gboolean 1276 value_equal (GValue *value1, 1277 GValue *value2) 1278 { 1279 GType type = G_VALUE_TYPE (value1); 1280 1281 if (type != G_VALUE_TYPE (value2)) { 1282 return FALSE; 1283 } 1284 1285 switch (type) { 1286 case G_TYPE_STRING: 1287 return (strcmp (g_value_get_string (value1), g_value_get_string (value2)) == 0); 1288 case G_TYPE_INT64: 1289 return g_value_get_int64 (value1) == g_value_get_int64 (value2); 1290 case G_TYPE_DOUBLE: 1291 /* does RDF define equality for floating point values? */ 1292 return g_value_get_double (value1) == g_value_get_double (value2); 1293 default: 1294 if (type == TRACKER_TYPE_DATE_TIME) { 1295 /* ignore UTC offset for comparison, irrelevant for comparison according to xsd:dateTime spec 1296 * http://www.w3.org/TR/xmlschema-2/#dateTime 1297 * also ignore sub-millisecond as this is a floating point comparison 1298 */ 1299 return fabs (tracker_date_time_get_time (value1) - tracker_date_time_get_time (value2)) < 0.001; 1300 } 1301 g_assert_not_reached (); 1302 } 1303 } 1304 1305 static gboolean 1306 value_set_add_value (GValueArray *value_set, 1307 GValue *value) 1308 { 1309 gint i; 1310 1311 g_return_val_if_fail (G_VALUE_TYPE (value), FALSE); 1312 1313 for (i = 0; i < value_set->n_values; i++) { 1314 if (value_equal (g_value_array_get_nth (value_set, i), value)) {
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
1315 /* no change, value already in set */ 1316 return FALSE; 1317 } 1318 } 1319 1320 g_value_array_append (value_set, value); 1321 1322 return TRUE; 1323 } 1324 1325 static gboolean 1326 value_set_remove_value (GValueArray *value_set, 1327 GValue *value) 1328 { 1329 gint i; 1330 1331 g_return_val_if_fail (G_VALUE_TYPE (value), FALSE); 1332 1333 for (i = 0; i < value_set->n_values; i++) { 1334 if (value_equal (g_value_array_get_nth (value_set, i), value)) {
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
1335 /* value found, remove from set */ 1336 1337 g_value_array_remove (value_set, i); 1338 1339 return TRUE; 1340 } 1341 } 1342 1343 /* no change, value not found */ 1344 return FALSE; 1345 } 1346 1347 static gboolean 1348 check_property_domain (TrackerProperty *property) 1349 { 1350 gint type_index; 1351 1352 for (type_index = 0; type_index < resource_buffer->types->len; type_index++) { 1353 if (tracker_property_get_domain (property) == g_ptr_array_index (resource_buffer->types, type_index)) { 1354 return TRUE; 1355 } 1356 } 1357 return FALSE; 1358 } 1359 1360 static GValueArray * 1361 get_property_values (TrackerProperty *property) 1362 { 1363 gboolean multiple_values; 1364 GValueArray *old_values; 1365 1366 multiple_values = tracker_property_get_multiple_values (property); 1367 1368 old_values = g_value_array_new (multiple_values ? 4 : 1);
'g_value_array_new' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:69): Use 'GArray' instead
(emitted by gcc)
1369 g_hash_table_insert (resource_buffer->predicates, g_object_ref (property), old_values); 1370 1371 if (!resource_buffer->create) { 1372 TrackerDBInterface *iface; 1373 TrackerDBStatement *stmt; 1374 TrackerDBCursor *cursor = NULL; 1375 const gchar *table_name; 1376 const gchar *field_name; 1377 GError *error = NULL; 1378 1379 table_name = tracker_property_get_table_name (property); 1380 field_name = tracker_property_get_name (property); 1381 1382 iface = tracker_db_manager_get_db_interface (); 1383 1384 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error, 1385 "SELECT \"%s\" FROM \"%s\" WHERE ID = ?", 1386 field_name, table_name); 1387 1388 if (stmt) { 1389 tracker_db_statement_bind_int (stmt, 0, resource_buffer->id); 1390 cursor = tracker_db_statement_start_cursor (stmt, &error); 1391 g_object_unref (stmt); 1392 } 1393 1394 if (error) { 1395 g_warning ("Could not get property values: %s\n", error->message); 1396 g_error_free (error); 1397 } 1398 1399 if (cursor) { 1400 while (tracker_db_cursor_iter_next (cursor, NULL, &error)) { 1401 GValue gvalue = { 0 }; 1402 tracker_db_cursor_get_value (cursor, 0, &gvalue); 1403 if (G_VALUE_TYPE (&gvalue)) { 1404 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME) { 1405 gdouble time; 1406 1407 if (G_VALUE_TYPE (&gvalue) == G_TYPE_INT64) { 1408 time = g_value_get_int64 (&gvalue); 1409 } else { 1410 time = g_value_get_double (&gvalue); 1411 } 1412 g_value_unset (&gvalue); 1413 g_value_init (&gvalue, TRACKER_TYPE_DATE_TIME); 1414 /* UTC offset is irrelevant for comparison */ 1415 tracker_date_time_set (&gvalue, time, 0); 1416 } 1417 g_value_array_append (old_values, &gvalue); 1418 g_value_unset (&gvalue); 1419 } 1420 } 1421 g_object_unref (cursor); 1422 } 1423 } 1424 1425 return old_values; 1426 } 1427 1428 static GValueArray * 1429 get_old_property_values (TrackerProperty *property, 1430 GError **error) 1431 { 1432 GValueArray *old_values; 1433 1434 /* read existing property values */ 1435 old_values = g_hash_table_lookup (resource_buffer->predicates, property); 1436 if (old_values == NULL) { 1437 if (!check_property_domain (property)) { 1438 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_CONSTRAINT, 1439 "Subject `%s' is not in domain `%s' of property `%s'", 1440 resource_buffer->subject, 1441 tracker_class_get_name (tracker_property_get_domain (property)), 1442 tracker_property_get_name (property)); 1443 return NULL; 1444 } 1445 1446 #if HAVE_TRACKER_FTS 1447 if (tracker_property_get_fulltext_indexed (property)) { 1448 TrackerDBInterface *iface; 1449 1450 iface = tracker_db_manager_get_db_interface (); 1451 1452 if (!resource_buffer->fts_updated && !resource_buffer->create) { 1453 guint i, n_props; 1454 TrackerProperty **properties, *prop; 1455 1456 /* first fulltext indexed property to be modified 1457 * retrieve values of all fulltext indexed properties 1458 */ 1459 properties = tracker_ontologies_get_properties (&n_props); 1460 1461 for (i = 0; i < n_props; i++) { 1462 prop = properties[i]; 1463 1464 if (tracker_property_get_fulltext_indexed (prop) 1465 && check_property_domain (prop)) { 1466 const gchar *property_name; 1467 gint i; 1468 1469 old_values = get_property_values (prop); 1470 property_name = tracker_property_get_name (prop); 1471 1472 /* delete old fts entries */ 1473 for (i = 0; i < old_values->n_values; i++) { 1474 tracker_db_interface_sqlite_fts_delete_text (iface, 1475 resource_buffer->id, 1476 property_name); 1477 } 1478 } 1479 } 1480 1481 update_buffer.fts_ever_updated = TRUE; 1482 1483 old_values = g_hash_table_lookup (resource_buffer->predicates, property); 1484 } else { 1485 old_values = get_property_values (property); 1486 } 1487 1488 resource_buffer->fts_updated = TRUE; 1489 } else { 1490 old_values = get_property_values (property); 1491 } 1492 #else 1493 old_values = get_property_values (property); 1494 #endif 1495 } 1496 1497 return old_values; 1498 } 1499 1500 static void 1501 string_to_gvalue (const gchar *value, 1502 TrackerPropertyType type, 1503 GValue *gvalue, 1504 GError **error) 1505 { 1506 gint object_id; 1507 gchar *datetime; 1508 1509 switch (type) { 1510 case TRACKER_PROPERTY_TYPE_STRING: 1511 g_value_init (gvalue, G_TYPE_STRING); 1512 g_value_set_string (gvalue, value); 1513 break; 1514 case TRACKER_PROPERTY_TYPE_INTEGER: 1515 g_value_init (gvalue, G_TYPE_INT64); 1516 g_value_set_int64 (gvalue, atoll (value)); 1517 break; 1518 case TRACKER_PROPERTY_TYPE_BOOLEAN: 1519 /* use G_TYPE_INT64 to be compatible with value stored in DB 1520 (important for value_equal function) */ 1521 g_value_init (gvalue, G_TYPE_INT64); 1522 g_value_set_int64 (gvalue, strcmp (value, "true") == 0); 1523 break; 1524 case TRACKER_PROPERTY_TYPE_DOUBLE: 1525 g_value_init (gvalue, G_TYPE_DOUBLE); 1526 g_value_set_double (gvalue, atof (value)); 1527 break; 1528 case TRACKER_PROPERTY_TYPE_DATE: 1529 g_value_init (gvalue, G_TYPE_INT64); 1530 datetime = g_strdup_printf ("%sT00:00:00Z", value); 1531 g_value_set_int64 (gvalue, tracker_string_to_date (datetime, NULL, error)); 1532 g_free (datetime); 1533 break; 1534 case TRACKER_PROPERTY_TYPE_DATETIME: 1535 g_value_init (gvalue, TRACKER_TYPE_DATE_TIME); 1536 tracker_date_time_set_from_string (gvalue, value, error); 1537 break; 1538 case TRACKER_PROPERTY_TYPE_RESOURCE: 1539 object_id = ensure_resource_id (value, NULL); 1540 g_value_init (gvalue, G_TYPE_INT64); 1541 g_value_set_int64 (gvalue, object_id); 1542 break; 1543 default: 1544 g_warn_if_reached (); 1545 break; 1546 } 1547 } 1548 1549 static gchar* 1550 gvalue_to_string (TrackerPropertyType type, 1551 GValue *gvalue) 1552 { 1553 gchar *retval = NULL; 1554 gint64 datet; 1555 1556 switch (type) { 1557 case TRACKER_PROPERTY_TYPE_STRING: 1558 retval = g_value_dup_string (gvalue); 1559 break; 1560 case TRACKER_PROPERTY_TYPE_INTEGER: 1561 retval = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (gvalue)); 1562 break; 1563 case TRACKER_PROPERTY_TYPE_BOOLEAN: 1564 retval = g_value_get_int64 (gvalue) == 0 ? g_strdup ("false") : g_strdup ("true"); 1565 break; 1566 case TRACKER_PROPERTY_TYPE_DOUBLE: 1567 retval = g_strdup_printf ("%f", g_value_get_double (gvalue)); 1568 break; 1569 case TRACKER_PROPERTY_TYPE_DATE: 1570 datet = g_value_get_int64 (gvalue); 1571 retval = tracker_date_to_string (datet); 1572 /* it's a date-only, cut off the time */ 1573 retval[10] = '\0'; 1574 break; 1575 case TRACKER_PROPERTY_TYPE_DATETIME: 1576 datet = tracker_date_time_get_time (gvalue); 1577 retval = tracker_date_to_string (datet); 1578 break; 1579 case TRACKER_PROPERTY_TYPE_RESOURCE: 1580 default: 1581 g_warn_if_reached (); 1582 break; 1583 } 1584 1585 return retval; 1586 } 1587 1588 static gboolean 1589 resource_in_domain_index_class (TrackerClass *domain_index_class) 1590 { 1591 guint i; 1592 for (i = 0; i < resource_buffer->types->len; i++) { 1593 if (g_ptr_array_index (resource_buffer->types, i) == domain_index_class) { 1594 return TRUE; 1595 } 1596 } 1597 return FALSE; 1598 } 1599 1600 static void 1601 process_domain_indexes (TrackerProperty *property, 1602 GValue *gvalue, 1603 const gchar *field_name, 1604 const gchar *graph, 1605 gint graph_id) 1606 { 1607 TrackerClass **domain_index_classes; 1608 1609 domain_index_classes = tracker_property_get_domain_indexes (property); 1610 while (*domain_index_classes) { 1611 if (resource_in_domain_index_class (*domain_index_classes)) { 1612 GValue gvalue_copy = { 0 }; 1613 1614 g_value_init (&gvalue_copy, G_VALUE_TYPE (gvalue)); 1615 g_value_copy (gvalue, &gvalue_copy); 1616 1617 cache_insert_value (tracker_class_get_name (*domain_index_classes), 1618 field_name, 1619 tracker_property_get_transient (property), 1620 &gvalue_copy, 1621 graph != NULL ? ensure_resource_id (graph, NULL) : graph_id, 1622 FALSE, 1623 tracker_property_get_fulltext_indexed (property), 1624 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME); 1625 } 1626 domain_index_classes++; 1627 } 1628 } 1629 1630 static gboolean 1631 cache_insert_metadata_decomposed (TrackerProperty *property, 1632 const gchar *value, 1633 gint value_id, 1634 const gchar *graph, 1635 gint graph_id, 1636 GError **error) 1637 { 1638 gboolean multiple_values; 1639 const gchar *table_name; 1640 const gchar *field_name; 1641 TrackerProperty **super_properties; 1642 GValue gvalue = { 0 }; 1643 GValueArray *old_values; 1644 GError *new_error = NULL; 1645 gboolean change = FALSE; 1646 1647 /* also insert super property values */ 1648 super_properties = tracker_property_get_super_properties (property); 1649 while (*super_properties) { 1650 change |= cache_insert_metadata_decomposed (*super_properties, value, value_id, 1651 graph, graph_id, &new_error); 1652 if (new_error) { 1653 g_propagate_error (error, new_error); 1654 return FALSE; 1655 } 1656 super_properties++; 1657 } 1658 1659 multiple_values = tracker_property_get_multiple_values (property); 1660 table_name = tracker_property_get_table_name (property); 1661 field_name = tracker_property_get_name (property); 1662 1663 /* read existing property values */ 1664 old_values = get_old_property_values (property, &new_error); 1665 if (new_error) { 1666 g_propagate_error (error, new_error); 1667 return FALSE; 1668 } 1669 1670 if (value) { 1671 string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, &new_error); 1672 if (new_error) { 1673 g_propagate_error (error, new_error); 1674 return FALSE; 1675 } 1676 } else { 1677 g_value_init (&gvalue, G_TYPE_INT64); 1678 g_value_set_int64 (&gvalue, value_id); 1679 } 1680 1681 if (!value_set_add_value (old_values, &gvalue)) { 1682 /* value already inserted */ 1683 g_value_unset (&gvalue); 1684 } else if (!multiple_values && old_values->n_values > 1) { 1685 /* trying to add second value to single valued property */ 1686 GValue old_value = { 0 }; 1687 GValue new_value = { 0 }; 1688 const gchar *old_value_str = NULL; 1689 const gchar *new_value_str = NULL; 1690 1691 g_value_init (&old_value, G_TYPE_STRING); 1692 g_value_init (&new_value, G_TYPE_STRING); 1693 1694 /* Get both old and new values as strings letting glib do 1695 * whatever transformation needed */ 1696 if (g_value_transform (g_value_array_get_nth (old_values, 0), &old_value)) {
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
1697 old_value_str = g_value_get_string (&old_value); 1698 } 1699 if (g_value_transform (g_value_array_get_nth (old_values, 1), &new_value)) { 1700 new_value_str = g_value_get_string (&new_value); 1701 } 1702 1703 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_CONSTRAINT, 1704 "Unable to insert multiple values for subject `%s' and single valued property `%s' " 1705 "(old_value: '%s', new value: '%s')", 1706 resource_buffer->subject, 1707 field_name, 1708 old_value_str ? old_value_str : "<untransformable>", 1709 new_value_str ? new_value_str : "<untransformable>"); 1710 1711 g_value_unset (&old_value); 1712 g_value_unset (&new_value); 1713 g_value_unset (&gvalue); 1714 1715 } else { 1716 cache_insert_value (table_name, field_name, 1717 tracker_property_get_transient (property), 1718 &gvalue, 1719 graph != NULL ? ensure_resource_id (graph, NULL) : graph_id, 1720 multiple_values, 1721 tracker_property_get_fulltext_indexed (property), 1722 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME); 1723 1724 if (!multiple_values) { 1725 process_domain_indexes (property, &gvalue, field_name, graph, graph_id); 1726 } 1727 1728 change = TRUE; 1729 } 1730 1731 return change; 1732 } 1733 1734 static gboolean 1735 delete_first_object (TrackerProperty *field, 1736 GValueArray *old_values, 1737 const gchar *graph, 1738 GError **error) 1739 { 1740 gint pred_id = 0, graph_id = 0; 1741 gint object_id = 0; 1742 gboolean change = FALSE; 1743 1744 if (old_values->n_values == 0) { 1745 return change; 1746 } 1747 1748 pred_id = tracker_property_get_id (field); 1749 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 1750 1751 if (tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_RESOURCE) { 1752 GError *new_error = NULL; 1753 1754 object_id = (gint) g_value_get_int64 (g_value_array_get_nth (old_values, 0));
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
1755 1756 /* This influences old_values, which is a reference, not a copy */ 1757 change = delete_metadata_decomposed (field, NULL, object_id, &new_error); 1758 1759 if (new_error) { 1760 g_propagate_error (error, new_error); 1761 return change; 1762 } 1763 1764 #ifndef DISABLE_JOURNAL 1765 if (!in_journal_replay && change && !tracker_property_get_transient (field)) { 1766 tracker_db_journal_append_delete_statement_id (graph_id, 1767 resource_buffer->id, 1768 pred_id, 1769 object_id); 1770 } 1771 #endif /* DISABLE_JOURNAL */ 1772 } else { 1773 GError *new_error = NULL; 1774 gchar *object_str = NULL; 1775 1776 object_id = 0; 1777 object_str = gvalue_to_string (tracker_property_get_data_type (field), 1778 g_value_array_get_nth (old_values, 0)); 1779 1780 /* This influences old_values, which is a reference, not a copy */ 1781 change = delete_metadata_decomposed (field, object_str, 0, &new_error); 1782 1783 if (new_error) { 1784 g_propagate_error (error, new_error); 1785 return change; 1786 } 1787 1788 #ifndef DISABLE_JOURNAL 1789 if (!in_journal_replay && change && !tracker_property_get_transient (field)) { 1790 if (!tracker_property_get_force_journal (field) && 1791 g_strcmp0 (graph, TRACKER_MINER_FS_GRAPH_URN) == 0) { 1792 /* do not journal this statement extracted from filesystem */ 1793 TrackerProperty *damaged; 1794 1795 damaged = tracker_ontologies_get_property_by_uri (TRACKER_TRACKER_PREFIX "damaged"); 1796 1797 tracker_db_journal_append_insert_statement (graph_id, 1798 resource_buffer->id, 1799 tracker_property_get_id (damaged), 1800 "true"); 1801 } else { 1802 tracker_db_journal_append_delete_statement (graph_id, 1803 resource_buffer->id, 1804 pred_id, 1805 object_str); 1806 } 1807 } 1808 1809 #endif /* DISABLE_JOURNAL */ 1810 1811 if (delete_callbacks && change) { 1812 guint n; 1813 for (n = 0; n < delete_callbacks->len; n++) { 1814 TrackerStatementDelegate *delegate; 1815 1816 delegate = g_ptr_array_index (delete_callbacks, n); 1817 delegate->callback (graph_id, graph, 1818 resource_buffer->id, 1819 resource_buffer->subject, 1820 pred_id, object_id, 1821 object_str, 1822 resource_buffer->types, 1823 delegate->user_data); 1824 } 1825 } 1826 1827 g_free (object_str); 1828 } 1829 1830 return change; 1831 } 1832 1833 static gboolean 1834 cache_update_metadata_decomposed (TrackerProperty *property, 1835 const gchar *value, 1836 gint value_id, 1837 const gchar *graph, 1838 gint graph_id, 1839 GError **error) 1840 { 1841 gboolean multiple_values; 1842 const gchar *table_name; 1843 const gchar *field_name; 1844 TrackerProperty **super_properties; 1845 GValue gvalue = { 0 }; 1846 GError *new_error = NULL; 1847 gboolean change = FALSE; 1848 1849 multiple_values = tracker_property_get_multiple_values (property); 1850 1851 /* also insert super property values */ 1852 super_properties = tracker_property_get_super_properties (property); 1853 while (*super_properties) { 1854 gboolean super_is_multi; 1855 super_is_multi = tracker_property_get_multiple_values (*super_properties); 1856 1857 if (!multiple_values && super_is_multi) { 1858 gint subject_id; 1859 gchar *subject; 1860 1861 GValueArray *old_values; 1862 1863 /* read existing property values */ 1864 old_values = get_old_property_values (property, &new_error); 1865 if (new_error) { 1866 g_propagate_error (error, new_error); 1867 return FALSE; 1868 } 1869 1870 /* Delete old values from super */ 1871 change |= delete_first_object (*super_properties, 1872 old_values, 1873 graph, 1874 &new_error); 1875 1876 if (new_error) { 1877 g_propagate_error (error, new_error); 1878 return FALSE; 1879 } 1880 1881 subject_id = resource_buffer->id; 1882 subject = g_strdup (resource_buffer->subject); 1883 1884 /* We need to flush to apply the delete */ 1885 tracker_data_update_buffer_flush (&new_error); 1886 if (new_error) { 1887 g_propagate_error (error, new_error); 1888 g_free (subject); 1889 return FALSE; 1890 } 1891 1892 /* After flush we need to switch the resource_buffer */ 1893 resource_buffer_switch (graph, graph_id, subject, subject_id); 1894 1895 g_free (subject); 1896 } 1897 1898 change |= cache_update_metadata_decomposed (*super_properties, value, value_id, 1899 graph, graph_id, &new_error); 1900 if (new_error) { 1901 g_propagate_error (error, new_error); 1902 return FALSE; 1903 } 1904 super_properties++; 1905 } 1906 1907 table_name = tracker_property_get_table_name (property); 1908 field_name = tracker_property_get_name (property); 1909 1910 if (value) { 1911 string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, &new_error); 1912 if (new_error) { 1913 g_propagate_error (error, new_error); 1914 return FALSE; 1915 } 1916 } else { 1917 g_value_init (&gvalue, G_TYPE_INT64); 1918 g_value_set_int64 (&gvalue, value_id); 1919 } 1920 1921 cache_insert_value (table_name, field_name, 1922 tracker_property_get_transient (property), 1923 &gvalue, 1924 graph != NULL ? ensure_resource_id (graph, NULL) : graph_id, 1925 multiple_values, 1926 tracker_property_get_fulltext_indexed (property), 1927 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME); 1928 1929 if (!multiple_values) { 1930 process_domain_indexes (property, &gvalue, field_name, graph, graph_id); 1931 } 1932 1933 return TRUE; 1934 } 1935 1936 static gboolean 1937 delete_metadata_decomposed (TrackerProperty *property, 1938 const gchar *value, 1939 gint value_id, 1940 GError **error) 1941 { 1942 gboolean multiple_values; 1943 const gchar *table_name; 1944 const gchar *field_name; 1945 TrackerProperty **super_properties; 1946 GValue gvalue = { 0 }; 1947 GValueArray *old_values; 1948 GError *new_error = NULL; 1949 gboolean change = FALSE; 1950 1951 multiple_values = tracker_property_get_multiple_values (property); 1952 table_name = tracker_property_get_table_name (property); 1953 field_name = tracker_property_get_name (property); 1954 1955 /* read existing property values */ 1956 old_values = get_old_property_values (property, &new_error); 1957 if (new_error) { 1958 /* no need to error out if statement does not exist for any reason */ 1959 g_clear_error (&new_error); 1960 return FALSE; 1961 } 1962 1963 if (value) { 1964 string_to_gvalue (value, tracker_property_get_data_type (property), &gvalue, &new_error); 1965 if (new_error) { 1966 g_propagate_error (error, new_error); 1967 return FALSE; 1968 } 1969 } else { 1970 g_value_init (&gvalue, G_TYPE_INT64); 1971 g_value_set_int64 (&gvalue, value_id); 1972 } 1973 1974 if (!value_set_remove_value (old_values, &gvalue)) { 1975 /* value not found */ 1976 g_value_unset (&gvalue); 1977 } else { 1978 cache_delete_value (table_name, field_name, 1979 tracker_property_get_transient (property), 1980 &gvalue, multiple_values, 1981 tracker_property_get_fulltext_indexed (property), 1982 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME); 1983 1984 if (!multiple_values) { 1985 TrackerClass **domain_index_classes; 1986 1987 domain_index_classes = tracker_property_get_domain_indexes (property); 1988 1989 while (*domain_index_classes) { 1990 if (resource_in_domain_index_class (*domain_index_classes)) { 1991 GValue gvalue_copy = { 0 }; 1992 g_value_init (&gvalue_copy, G_VALUE_TYPE (&gvalue)); 1993 g_value_copy (&gvalue, &gvalue_copy); 1994 cache_delete_value (tracker_class_get_name (*domain_index_classes), 1995 field_name, 1996 tracker_property_get_transient (property), 1997 &gvalue_copy, multiple_values, 1998 tracker_property_get_fulltext_indexed (property), 1999 tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_DATETIME); 2000 } 2001 domain_index_classes++; 2002 } 2003 } 2004 2005 change = TRUE; 2006 } 2007 2008 /* also delete super property values */ 2009 super_properties = tracker_property_get_super_properties (property); 2010 while (*super_properties) { 2011 change |= delete_metadata_decomposed (*super_properties, value, value_id, error); 2012 super_properties++; 2013 } 2014 2015 return change; 2016 } 2017 2018 static void 2019 db_delete_row (TrackerDBInterface *iface, 2020 const gchar *table_name, 2021 gint id) 2022 { 2023 TrackerDBStatement *stmt; 2024 GError *error = NULL; 2025 2026 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error, 2027 "DELETE FROM \"%s\" WHERE ID = ?", 2028 table_name); 2029 2030 if (stmt) { 2031 tracker_db_statement_bind_int (stmt, 0, id); 2032 tracker_db_statement_execute (stmt, &error); 2033 g_object_unref (stmt); 2034 } 2035 2036 if (error) { 2037 g_warning ("%s", error->message); 2038 g_error_free (error); 2039 error = NULL; 2040 } 2041 } 2042 2043 static void 2044 cache_delete_resource_type_full (TrackerClass *class, 2045 const gchar *graph, 2046 gint graph_id, 2047 gboolean single_type) 2048 { 2049 TrackerDBInterface *iface; 2050 TrackerDBStatement *stmt; 2051 TrackerDBCursor *cursor = NULL; 2052 TrackerProperty **properties, *prop; 2053 gboolean found, direct_delete; 2054 gint i; 2055 guint p, n_props; 2056 GError *error = NULL; 2057 2058 iface = tracker_db_manager_get_db_interface (); 2059 2060 if (!single_type) { 2061 if (!HAVE_TRACKER_FTS && 2062 strcmp (tracker_class_get_uri (class), RDFS_PREFIX "Resource") == 0 && 2063 g_hash_table_size (resource_buffer->tables) == 0) { 2064 /* skip subclass query when deleting whole resource 2065 to improve performance */ 2066 2067 while (resource_buffer->types->len > 0) { 2068 TrackerClass *type; 2069 2070 type = g_ptr_array_index (resource_buffer->types, 2071 resource_buffer->types->len - 1); 2072 cache_delete_resource_type_full (type, 2073 graph, 2074 graph_id, 2075 TRUE); 2076 } 2077 2078 return; 2079 } 2080 2081 found = FALSE; 2082 for (i = 0; i < resource_buffer->types->len; i++) { 2083 if (g_ptr_array_index (resource_buffer->types, i) == class) { 2084 found = TRUE; 2085 break; 2086 } 2087 } 2088 2089 if (!found) { 2090 /* type not found, nothing to do */ 2091 return; 2092 } 2093 2094 /* retrieve all subclasses we need to remove from the subject 2095 * before we can remove the class specified as object of the statement */ 2096 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT, &error, 2097 "SELECT (SELECT Uri FROM Resource WHERE ID = \"rdfs:Class_rdfs:subClassOf\".ID) " 2098 "FROM \"rdfs:Resource_rdf:type\" INNER JOIN \"rdfs:Class_rdfs:subClassOf\" ON (\"rdf:type\" = \"rdfs:Class_rdfs:subClassOf\".ID) " 2099 "WHERE \"rdfs:Resource_rdf:type\".ID = ? AND \"rdfs:subClassOf\" = (SELECT ID FROM Resource WHERE Uri = ?)"); 2100 2101 if (stmt) { 2102 tracker_db_statement_bind_int (stmt, 0, resource_buffer->id); 2103 tracker_db_statement_bind_text (stmt, 1, tracker_class_get_uri (class)); 2104 cursor = tracker_db_statement_start_cursor (stmt, &error); 2105 g_object_unref (stmt); 2106 } 2107 2108 if (cursor) { 2109 while (tracker_db_cursor_iter_next (cursor, NULL, &error)) { 2110 const gchar *class_uri; 2111 2112 class_uri = tracker_db_cursor_get_string (cursor, 0, NULL); 2113 cache_delete_resource_type_full (tracker_ontologies_get_class_by_uri (class_uri), 2114 graph, graph_id, FALSE); 2115 } 2116 2117 g_object_unref (cursor); 2118 } 2119 2120 if (error) { 2121 g_warning ("Could not delete cache resource (selecting subclasses): %s", error->message); 2122 g_error_free (error); 2123 error = NULL; 2124 } 2125 } 2126 2127 /* bypass buffer if possible 2128 we need old property values with FTS */ 2129 direct_delete = (!HAVE_TRACKER_FTS && g_hash_table_size (resource_buffer->tables) == 0); 2130 2131 /* delete all property values */ 2132 2133 properties = tracker_ontologies_get_properties (&n_props); 2134 2135 for (p = 0; p < n_props; p++) { 2136 gboolean multiple_values; 2137 const gchar *table_name; 2138 const gchar *field_name; 2139 GValueArray *old_values; 2140 gint y; 2141 2142 prop = properties[p]; 2143 2144 if (tracker_property_get_domain (prop) != class) { 2145 continue; 2146 } 2147 2148 multiple_values = tracker_property_get_multiple_values (prop); 2149 table_name = tracker_property_get_table_name (prop); 2150 field_name = tracker_property_get_name (prop); 2151 2152 if (direct_delete) { 2153 if (multiple_values) { 2154 db_delete_row (iface, table_name, resource_buffer->id); 2155 } 2156 /* single-valued property values are deleted right after the loop by deleting the row in the class table */ 2157 continue; 2158 } 2159 2160 old_values = get_old_property_values (prop, NULL); 2161 2162 for (y = old_values->n_values - 1; y >= 0 ; y--) { 2163 GValue *old_gvalue; 2164 GValue gvalue = { 0 }; 2165 2166 old_gvalue = g_value_array_get_nth (old_values, y);
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
2167 g_value_init (&gvalue, G_VALUE_TYPE (old_gvalue)); 2168 g_value_copy (old_gvalue, &gvalue); 2169 2170 value_set_remove_value (old_values, &gvalue); 2171 cache_delete_value (table_name, field_name, 2172 tracker_property_get_transient (prop), 2173 &gvalue, multiple_values, 2174 tracker_property_get_fulltext_indexed (prop), 2175 tracker_property_get_data_type (prop) == TRACKER_PROPERTY_TYPE_DATETIME); 2176 2177 2178 if (!multiple_values) { 2179 TrackerClass **domain_index_classes; 2180 2181 domain_index_classes = tracker_property_get_domain_indexes (prop); 2182 while (*domain_index_classes) { 2183 if (resource_in_domain_index_class (*domain_index_classes)) { 2184 GValue gvalue_copy = { 0 }; 2185 g_value_init (&gvalue_copy, G_VALUE_TYPE (&gvalue)); 2186 g_value_copy (&gvalue, &gvalue_copy); 2187 cache_delete_value (tracker_class_get_name (*domain_index_classes), 2188 field_name, 2189 tracker_property_get_transient (prop), 2190 &gvalue_copy, multiple_values, 2191 tracker_property_get_fulltext_indexed (prop), 2192 tracker_property_get_data_type (prop) == TRACKER_PROPERTY_TYPE_DATETIME); 2193 } 2194 domain_index_classes++; 2195 } 2196 } 2197 2198 } 2199 } 2200 2201 if (direct_delete) { 2202 /* delete row from class table */ 2203 db_delete_row (iface, tracker_class_get_name (class), resource_buffer->id); 2204 2205 if (!single_type) { 2206 /* delete row from rdfs:Resource_rdf:type table */ 2207 /* this is not necessary when deleting the whole resource 2208 as all property values are deleted implicitly */ 2209 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &error, 2210 "DELETE FROM \"rdfs:Resource_rdf:type\" WHERE ID = ? AND \"rdf:type\" = ?"); 2211 2212 if (stmt) { 2213 tracker_db_statement_bind_int (stmt, 0, resource_buffer->id); 2214 tracker_db_statement_bind_int (stmt, 1, tracker_class_get_id (class)); 2215 tracker_db_statement_execute (stmt, &error); 2216 g_object_unref (stmt); 2217 } 2218 2219 if (error) { 2220 g_warning ("Could not delete cache resource: %s", error->message); 2221 g_error_free (error); 2222 error = NULL; 2223 } 2224 } 2225 2226 add_class_count (class, -1); 2227 } else { 2228 cache_delete_row (class); 2229 } 2230 2231 if (!in_journal_replay && delete_callbacks) { 2232 guint n; 2233 gint final_graph_id; 2234 2235 final_graph_id = (graph != NULL ? ensure_resource_id (graph, NULL) : graph_id); 2236 2237 for (n = 0; n < delete_callbacks->len; n++) { 2238 TrackerStatementDelegate *delegate; 2239 2240 delegate = g_ptr_array_index (delete_callbacks, n); 2241 delegate->callback (final_graph_id, graph, resource_buffer->id, resource_buffer->subject, 2242 tracker_property_get_id (tracker_ontologies_get_rdf_type ()), 2243 tracker_class_get_id (class), 2244 tracker_class_get_uri (class), 2245 resource_buffer->types, 2246 delegate->user_data); 2247 } 2248 } 2249 2250 g_ptr_array_remove (resource_buffer->types, class); 2251 } 2252 2253 static void 2254 cache_delete_resource_type (TrackerClass *class, 2255 const gchar *graph, 2256 gint graph_id) 2257 { 2258 cache_delete_resource_type_full (class, graph, graph_id, FALSE); 2259 } 2260 2261 static void 2262 resource_buffer_switch (const gchar *graph, 2263 gint graph_id, 2264 const gchar *subject, 2265 gint subject_id) 2266 { 2267 if (in_journal_replay) { 2268 /* journal replay only provides subject id 2269 resource_buffer->subject is only used in error messages and callbacks 2270 both should never occur when in journal replay */ 2271 if (resource_buffer == NULL || resource_buffer->id != subject_id) { 2272 /* switch subject */ 2273 resource_buffer = g_hash_table_lookup (update_buffer.resources_by_id, GINT_TO_POINTER (subject_id)); 2274 } 2275 } else { 2276 if (resource_buffer == NULL || strcmp (resource_buffer->subject, subject) != 0) {
Null pointer passed as an argument to a 'nonnull' parameter
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2277 /* switch subject */ 2278 resource_buffer = g_hash_table_lookup (update_buffer.resources, subject); 2279 } 2280 } 2281 2282 if (resource_buffer == NULL) { 2283 gchar *subject_dup = NULL; 2284 2285 /* large INSERTs with thousands of resources could lead to 2286 high peak memory usage due to the update buffer 2287 flush the buffer if it already contains 1000 resources */ 2288 tracker_data_update_buffer_might_flush (NULL); 2289 2290 /* subject not yet in cache, retrieve or create ID */ 2291 resource_buffer = g_slice_new0 (TrackerDataUpdateBufferResource); 2292 if (subject != NULL) { 2293 subject_dup = g_strdup (subject); 2294 resource_buffer->subject = subject_dup; 2295 } 2296 if (subject_id > 0) { 2297 resource_buffer->id = subject_id; 2298 } else { 2299 resource_buffer->id = ensure_resource_id (resource_buffer->subject, &resource_buffer->create); 2300 } 2301 #if HAVE_TRACKER_FTS 2302 resource_buffer->fts_updated = FALSE; 2303 #endif 2304 if (resource_buffer->create) { 2305 resource_buffer->types = g_ptr_array_new (); 2306 } else { 2307 resource_buffer->types = tracker_data_query_rdf_type (resource_buffer->id); 2308 } 2309 resource_buffer->predicates = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, (GDestroyNotify) g_value_array_free);
'g_value_array_free' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:72): Use 'GArray' instead
(emitted by gcc)
2310 resource_buffer->tables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cache_table_free); 2311 2312 if (in_journal_replay) { 2313 g_hash_table_insert (update_buffer.resources_by_id, GINT_TO_POINTER (subject_id), resource_buffer); 2314 } else { 2315 g_hash_table_insert (update_buffer.resources, subject_dup, resource_buffer); 2316 2317 if (graph != NULL) { 2318 graph_id = ensure_resource_id (graph, NULL);
Value stored to 'graph_id' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2319 } 2320 } 2321 } 2322 } 2323 2324 void 2325 tracker_data_delete_statement (const gchar *graph, 2326 const gchar *subject, 2327 const gchar *predicate, 2328 const gchar *object, 2329 GError **error) 2330 { 2331 TrackerClass *class; 2332 gint subject_id = 0; 2333 gboolean change = FALSE; 2334 2335 g_return_if_fail (subject != NULL); 2336 g_return_if_fail (predicate != NULL); 2337 g_return_if_fail (object != NULL); 2338 g_return_if_fail (in_transaction); 2339 2340 subject_id = query_resource_id (subject); 2341 2342 if (subject_id == 0) { 2343 /* subject not in database */ 2344 return; 2345 } 2346 2347 resource_buffer_switch (graph, 0, subject, subject_id); 2348 2349 if (object && g_strcmp0 (predicate, RDF_PREFIX "type") == 0) { 2350 class = tracker_ontologies_get_class_by_uri (object); 2351 if (class != NULL) { 2352 has_persistent = TRUE; 2353 2354 #ifndef DISABLE_JOURNAL 2355 if (!in_journal_replay) { 2356 tracker_db_journal_append_delete_statement_id ( 2357 (graph != NULL ? query_resource_id (graph) : 0), 2358 resource_buffer->id, 2359 tracker_data_query_resource_id (predicate), 2360 tracker_class_get_id (class)); 2361 } 2362 #endif /* DISABLE_JOURNAL */ 2363 2364 cache_delete_resource_type (class, graph, 0); 2365 } else { 2366 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS, 2367 "Class '%s' not found in the ontology", object); 2368 } 2369 } else { 2370 gint pred_id = 0, graph_id = 0, object_id = 0; 2371 gboolean tried = FALSE; 2372 TrackerProperty *field; 2373 2374 field = tracker_ontologies_get_property_by_uri (predicate); 2375 if (field != NULL) { 2376 if (!tracker_property_get_transient (field)) { 2377 has_persistent = TRUE; 2378 } 2379 2380 change = delete_metadata_decomposed (field, object, 0, error); 2381 if (!in_journal_replay && change && !tracker_property_get_transient (field)) { 2382 if (tracker_property_get_data_type (field) == TRACKER_PROPERTY_TYPE_RESOURCE) { 2383 2384 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2385 pred_id = tracker_property_get_id (field); 2386 object_id = query_resource_id (object); 2387 tried = TRUE; 2388 2389 #ifndef DISABLE_JOURNAL 2390 tracker_db_journal_append_delete_statement_id (graph_id, 2391 resource_buffer->id, 2392 pred_id, 2393 object_id); 2394 #endif /* DISABLE_JOURNAL */ 2395 } else { 2396 pred_id = tracker_property_get_id (field); 2397 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2398 object_id = 0; 2399 tried = TRUE; 2400 2401 #ifndef DISABLE_JOURNAL 2402 if (!tracker_property_get_force_journal (field) && 2403 g_strcmp0 (graph, TRACKER_MINER_FS_GRAPH_URN) == 0) { 2404 /* do not journal this statement extracted from filesystem */ 2405 TrackerProperty *damaged; 2406 2407 damaged = tracker_ontologies_get_property_by_uri (TRACKER_TRACKER_PREFIX "damaged"); 2408 2409 tracker_db_journal_append_insert_statement (graph_id, 2410 resource_buffer->id, 2411 tracker_property_get_id (damaged), 2412 "true"); 2413 } else { 2414 tracker_db_journal_append_delete_statement (graph_id, 2415 resource_buffer->id, 2416 pred_id, 2417 object); 2418 } 2419 #endif /* DISABLE_JOURNAL */ 2420 } 2421 } 2422 } else { 2423 /* I wonder why in case of error the delete_callbacks are still executed */ 2424 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2425 "Property '%s' not found in the ontology", predicate); 2426 } 2427 2428 if (!tried) { 2429 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2430 if (field == NULL) { 2431 pred_id = tracker_data_query_resource_id (predicate); 2432 } else { 2433 pred_id = tracker_property_get_id (field); 2434 } 2435 } 2436 2437 if (delete_callbacks && change) { 2438 guint n; 2439 for (n = 0; n < delete_callbacks->len; n++) { 2440 TrackerStatementDelegate *delegate; 2441 2442 delegate = g_ptr_array_index (delete_callbacks, n); 2443 delegate->callback (graph_id, graph, subject_id, subject, 2444 pred_id, object_id, 2445 object, 2446 resource_buffer->types, 2447 delegate->user_data); 2448 } 2449 } 2450 } 2451 } 2452 2453 static void 2454 delete_all_objects (const gchar *graph, 2455 const gchar *subject, 2456 const gchar *predicate, 2457 GError **error) 2458 { 2459 gint subject_id = 0; 2460 gboolean change = FALSE; 2461 GError *new_error = NULL; 2462 TrackerProperty *field; 2463 2464 g_return_if_fail (subject != NULL); 2465 g_return_if_fail (predicate != NULL); 2466 g_return_if_fail (in_transaction); 2467 2468 subject_id = query_resource_id (subject); 2469 2470 if (subject_id == 0) { 2471 /* subject not in database */ 2472 return; 2473 } 2474 2475 resource_buffer_switch (graph, 0, subject, subject_id); 2476 2477 field = tracker_ontologies_get_property_by_uri (predicate); 2478 if (field != NULL) { 2479 GValueArray *old_values; 2480 2481 if (!tracker_property_get_transient (field)) { 2482 has_persistent = TRUE; 2483 } 2484 2485 old_values = get_old_property_values (field, &new_error); 2486 if (new_error) { 2487 g_propagate_error (error, new_error); 2488 return; 2489 } 2490 2491 while (old_values->n_values > 0) { 2492 GError *new_error = NULL; 2493 2494 change |= delete_first_object (field, old_values, graph, &new_error); 2495 2496 if (new_error) { 2497 g_propagate_error (error, new_error); 2498 return; 2499 } 2500 } 2501 } else { 2502 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2503 "Property '%s' not found in the ontology", predicate); 2504 } 2505 } 2506 2507 static gboolean 2508 tracker_data_insert_statement_common (const gchar *graph, 2509 const gchar *subject, 2510 const gchar *predicate, 2511 const gchar *object, 2512 GError **error) 2513 { 2514 if (g_str_has_prefix (subject, ":")) { 2515 /* blank node definition 2516 pile up statements until the end of the blank node */ 2517 gchar *value; 2518 GError *actual_error = NULL; 2519 2520 if (blank_buffer.subject != NULL) { 2521 /* active subject in buffer */ 2522 if (strcmp (blank_buffer.subject, subject) != 0) { 2523 /* subject changed, need to flush buffer */ 2524 tracker_data_blank_buffer_flush (&actual_error); 2525 2526 if (actual_error) { 2527 g_propagate_error (error, actual_error); 2528 return FALSE; 2529 } 2530 } 2531 } 2532 2533 if (blank_buffer.subject == NULL) { 2534 blank_buffer.subject = g_strdup (subject); 2535 if (blank_buffer.graphs == NULL) { 2536 blank_buffer.graphs = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4); 2537 blank_buffer.predicates = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4); 2538 blank_buffer.objects = g_array_sized_new (FALSE, FALSE, sizeof (char*), 4); 2539 } 2540 } 2541 2542 value = g_strdup (graph); 2543 g_array_append_val (blank_buffer.graphs, value); 2544 value = g_strdup (predicate); 2545 g_array_append_val (blank_buffer.predicates, value); 2546 value = g_strdup (object); 2547 g_array_append_val (blank_buffer.objects, value); 2548 2549 return FALSE; 2550 } 2551 2552 resource_buffer_switch (graph, 0, subject, 0); 2553 2554 return TRUE; 2555 } 2556 2557 void 2558 tracker_data_insert_statement (const gchar *graph, 2559 const gchar *subject, 2560 const gchar *predicate, 2561 const gchar *object, 2562 GError **error) 2563 { 2564 TrackerProperty *property; 2565 2566 g_return_if_fail (subject != NULL); 2567 g_return_if_fail (predicate != NULL); 2568 g_return_if_fail (object != NULL); 2569 g_return_if_fail (in_transaction); 2570 2571 property = tracker_ontologies_get_property_by_uri (predicate); 2572 if (property != NULL) { 2573 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_RESOURCE) { 2574 tracker_data_insert_statement_with_uri (graph, subject, predicate, object, error); 2575 } else { 2576 tracker_data_insert_statement_with_string (graph, subject, predicate, object, error); 2577 } 2578 } else { 2579 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2580 "Property '%s' not found in the ontology", predicate); 2581 } 2582 } 2583 2584 2585 static gboolean 2586 handle_blank_node (const gchar *subject, 2587 const gchar *predicate, 2588 const gchar *object, 2589 const gchar *graph, 2590 gboolean update, 2591 GError **error) 2592 { 2593 GError *actual_error = NULL; 2594 /* anonymous blank node used as object in a statement */ 2595 const gchar *blank_uri; 2596 2597 if (blank_buffer.subject != NULL) { 2598 if (strcmp (blank_buffer.subject, object) == 0) { 2599 /* object still in blank buffer, need to flush buffer */ 2600 tracker_data_blank_buffer_flush (&actual_error); 2601 2602 if (actual_error) { 2603 g_propagate_error (error, actual_error); 2604 return FALSE; 2605 } 2606 } 2607 } 2608 2609 blank_uri = g_hash_table_lookup (blank_buffer.table, object); 2610 2611 if (blank_uri != NULL) { 2612 /* now insert statement referring to blank node */ 2613 if (update) { 2614 tracker_data_update_statement (graph, subject, predicate, blank_uri, &actual_error); 2615 } else { 2616 tracker_data_insert_statement (graph, subject, predicate, blank_uri, &actual_error); 2617 } 2618 2619 g_hash_table_remove (blank_buffer.table, object); 2620 2621 if (actual_error) { 2622 g_propagate_error (error, actual_error); 2623 return FALSE; 2624 } 2625 2626 return TRUE; 2627 } else { 2628 g_critical ("Blank node '%s' not found", object); 2629 2630 return FALSE; 2631 } 2632 } 2633 2634 void 2635 tracker_data_insert_statement_with_uri (const gchar *graph, 2636 const gchar *subject, 2637 const gchar *predicate, 2638 const gchar *object, 2639 GError **error) 2640 { 2641 GError *actual_error = NULL; 2642 TrackerClass *class; 2643 TrackerProperty *property; 2644 gint prop_id = 0, graph_id = 0; 2645 gint final_prop_id = 0, object_id = 0; 2646 gboolean change = FALSE; 2647 2648 g_return_if_fail (subject != NULL); 2649 g_return_if_fail (predicate != NULL); 2650 g_return_if_fail (object != NULL); 2651 g_return_if_fail (in_transaction); 2652 2653 property = tracker_ontologies_get_property_by_uri (predicate); 2654 if (property == NULL) { 2655 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2656 "Property '%s' not found in the ontology", predicate); 2657 return; 2658 } else { 2659 if (tracker_property_get_data_type (property) != TRACKER_PROPERTY_TYPE_RESOURCE) { 2660 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_TYPE, 2661 "Property '%s' does not accept URIs", predicate); 2662 return; 2663 } 2664 prop_id = tracker_property_get_id (property); 2665 } 2666 2667 if (!tracker_property_get_transient (property)) { 2668 has_persistent = TRUE; 2669 } 2670 2671 /* subjects and objects starting with `:' are anonymous blank nodes */ 2672 if (g_str_has_prefix (object, ":")) { 2673 if (handle_blank_node (subject, predicate, object, graph, FALSE, &actual_error)) { 2674 return; 2675 } 2676 2677 if (actual_error) { 2678 g_propagate_error (error, actual_error); 2679 return; 2680 } 2681 } 2682 2683 if (!tracker_data_insert_statement_common (graph, subject, predicate, object, &actual_error)) { 2684 if (actual_error) { 2685 g_propagate_error (error, actual_error); 2686 return; 2687 } 2688 2689 return; 2690 } 2691 2692 if (property == tracker_ontologies_get_rdf_type ()) { 2693 /* handle rdf:type statements specially to 2694 cope with inference and insert blank rows */ 2695 class = tracker_ontologies_get_class_by_uri (object); 2696 if (class != NULL) { 2697 cache_create_service_decomposed (class, graph, 0); 2698 } else { 2699 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS, 2700 "Class '%s' not found in the ontology", object); 2701 return; 2702 } 2703 2704 if (!in_journal_replay && !tracker_property_get_transient (property)) { 2705 graph_id = (graph != NULL ? query_resource_id (graph) : 0);
Value stored to 'graph_id' is never read

Possibly related backtrace: 28f3eb595700555806698b89b41102de32d0216f MatchResult(frame_number=6, dist=1)

Possibly related backtrace: 61de754ac00793bc81d77cc302ac006884baff15 MatchResult(frame_number=5, dist=1)

Possibly related backtrace: bc7e9a0f3e93e18aaaef59e12735383f9a24d4b2 MatchResult(frame_number=3, dist=1)

Possibly related backtrace: cbce0118de9f79ab47e1e9e910d7873d222cefd6 MatchResult(frame_number=4, dist=1)

(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2706 final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (predicate); 2707 object_id = query_resource_id (object); 2708 } 2709 2710 change = TRUE; 2711 } else { 2712 /* add value to metadata database */ 2713 change = cache_insert_metadata_decomposed (property, object, 0, graph, 0, &actual_error); 2714 if (actual_error) { 2715 g_propagate_error (error, actual_error); 2716 return; 2717 } 2718 2719 if (change) { 2720 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2721 final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (predicate); 2722 object_id = query_resource_id (object); 2723 2724 if (insert_callbacks) { 2725 guint n; 2726 for (n = 0; n < insert_callbacks->len; n++) { 2727 TrackerStatementDelegate *delegate; 2728 2729 delegate = g_ptr_array_index (insert_callbacks, n); 2730 delegate->callback (graph_id, graph, resource_buffer->id, subject, 2731 final_prop_id, object_id, 2732 object, 2733 resource_buffer->types, 2734 delegate->user_data); 2735 } 2736 } 2737 } 2738 } 2739 2740 #ifndef DISABLE_JOURNAL 2741 if (!in_journal_replay && change && !tracker_property_get_transient (property)) { 2742 tracker_db_journal_append_insert_statement_id ( 2743 (graph != NULL ? query_resource_id (graph) : 0), 2744 resource_buffer->id, 2745 final_prop_id, 2746 object_id); 2747 } 2748 #endif /* DISABLE_JOURNAL */ 2749 2750 } 2751 2752 void 2753 tracker_data_insert_statement_with_string (const gchar *graph, 2754 const gchar *subject, 2755 const gchar *predicate, 2756 const gchar *object, 2757 GError **error) 2758 { 2759 GError *actual_error = NULL; 2760 TrackerProperty *property; 2761 gboolean change; 2762 gint graph_id = 0, pred_id = 0; 2763 #ifndef DISABLE_JOURNAL 2764 gboolean tried = FALSE; 2765 #endif 2766 2767 g_return_if_fail (subject != NULL); 2768 g_return_if_fail (predicate != NULL); 2769 g_return_if_fail (object != NULL); 2770 g_return_if_fail (in_transaction); 2771 2772 property = tracker_ontologies_get_property_by_uri (predicate); 2773 if (property == NULL) { 2774 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2775 "Property '%s' not found in the ontology", predicate); 2776 return; 2777 } else { 2778 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_RESOURCE) { 2779 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_TYPE, 2780 "Property '%s' only accepts URIs", predicate); 2781 return; 2782 } 2783 pred_id = tracker_property_get_id (property); 2784 } 2785 2786 if (!tracker_property_get_transient (property)) { 2787 has_persistent = TRUE; 2788 } 2789 2790 if (!tracker_data_insert_statement_common (graph, subject, predicate, object, &actual_error)) { 2791 if (actual_error) { 2792 g_propagate_error (error, actual_error); 2793 return; 2794 } 2795 2796 return; 2797 } 2798 2799 /* add value to metadata database */ 2800 change = cache_insert_metadata_decomposed (property, object, 0, graph, 0, &actual_error); 2801 if (actual_error) { 2802 g_propagate_error (error, actual_error); 2803 return; 2804 } 2805 2806 if (insert_callbacks && change) { 2807 guint n; 2808 2809 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2810 pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (predicate); 2811 #ifndef DISABLE_JOURNAL 2812 tried = TRUE; 2813 #endif 2814 2815 for (n = 0; n < insert_callbacks->len; n++) { 2816 TrackerStatementDelegate *delegate; 2817 2818 delegate = g_ptr_array_index (insert_callbacks, n); 2819 delegate->callback (graph_id, graph, resource_buffer->id, subject, 2820 pred_id, 0 /* Always a literal */, 2821 object, 2822 resource_buffer->types, 2823 delegate->user_data); 2824 } 2825 } 2826 2827 #ifndef DISABLE_JOURNAL 2828 if (!in_journal_replay && change && !tracker_property_get_transient (property)) { 2829 if (!tried) { 2830 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 2831 pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (predicate); 2832 } 2833 if (!tracker_property_get_force_journal (property) && 2834 g_strcmp0 (graph, TRACKER_MINER_FS_GRAPH_URN) == 0) { 2835 /* do not journal this statement extracted from filesystem */ 2836 TrackerProperty *damaged; 2837 2838 damaged = tracker_ontologies_get_property_by_uri (TRACKER_TRACKER_PREFIX "damaged"); 2839 tracker_db_journal_append_insert_statement (graph_id, 2840 resource_buffer->id, 2841 tracker_property_get_id (damaged), 2842 "true"); 2843 } else { 2844 tracker_db_journal_append_insert_statement (graph_id, 2845 resource_buffer->id, 2846 pred_id, 2847 object); 2848 } 2849 } 2850 #endif /* DISABLE_JOURNAL */ 2851 } 2852 2853 static void 2854 tracker_data_update_statement_with_uri (const gchar *graph, 2855 const gchar *subject, 2856 const gchar *predicate, 2857 const gchar *object, 2858 GError **error) 2859 { 2860 GError *actual_error = NULL; 2861 TrackerClass *class; 2862 TrackerProperty *property; 2863 gint prop_id = 0, graph_id = 0; 2864 gint final_prop_id = 0, object_id = 0; 2865 gboolean change = FALSE; 2866 2867 g_return_if_fail (subject != NULL); 2868 g_return_if_fail (predicate != NULL); 2869 g_return_if_fail (in_transaction); 2870 2871 property = tracker_ontologies_get_property_by_uri (predicate); 2872 if (property == NULL) { 2873 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 2874 "Property '%s' not found in the ontology", predicate); 2875 return; 2876 } else { 2877 if (tracker_property_get_data_type (property) != TRACKER_PROPERTY_TYPE_RESOURCE) { 2878 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_TYPE, 2879 "Property '%s' does not accept URIs", predicate); 2880 return; 2881 } 2882 prop_id = tracker_property_get_id (property); 2883 } 2884 2885 if (!tracker_property_get_transient (property)) { 2886 has_persistent = TRUE; 2887 } 2888 2889 /* subjects and objects starting with `:' are anonymous blank nodes */ 2890 if (g_str_has_prefix (object, ":")) { 2891 if (handle_blank_node (subject, predicate, object, graph, TRUE, &actual_error)) { 2892 return; 2893 } 2894 2895 if (actual_error) { 2896 g_propagate_error (error, actual_error); 2897 return; 2898 } 2899 } 2900 2901 /* Update and insert share the exact same code here */ 2902 if (!tracker_data_insert_statement_common (graph, subject, predicate, object, &actual_error)) { 2903 if (actual_error) { 2904 g_propagate_error (error, actual_error); 2905 return; 2906 } 2907 2908 return; 2909 } 2910 2911 if (property == tracker_ontologies_get_rdf_type ()) { 2912 /* handle rdf:type statements specially to 2913 cope with inference and insert blank rows */ 2914 class = tracker_ontologies_get_class_by_uri (object); 2915 if (class != NULL) { 2916 /* Create here is fine for Update too */ 2917 cache_create_service_decomposed (class, graph, 0); 2918 } else { 2919 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_CLASS, 2920 "Class '%s' not found in the ontology", object); 2921 return; 2922 } 2923 2924 if (!in_journal_replay && !tracker_property_get_transient (property)) { 2925 graph_id = (graph != NULL ? query_resource_id (graph) : 0);
Value stored to 'graph_id' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2926 final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (predicate); 2927 object_id = query_resource_id (object); 2928 } 2929 2930 change = TRUE; 2931 } else { 2932 gint old_object_id = 0; 2933 GValueArray *old_values; 2934 gboolean multiple_values; 2935 GError *new_error = NULL; 2936 gboolean domain_unchecked = TRUE; 2937 2938 multiple_values = tracker_property_get_multiple_values (property); 2939 2940 #if HAVE_TRACKER_FTS 2941 /* This is unavoidable with FTS */ 2942 /* This does a check_property_domain too */ 2943 old_values = get_old_property_values (property, &new_error); 2944 domain_unchecked = FALSE; 2945 if (!new_error) { 2946 if (old_values->n_values > 0) { 2947 /* evel knievel cast */ 2948 old_object_id = (gint) g_value_get_int64 (g_value_array_get_nth (old_values, 0));
'g_value_array_get_nth' is deprecated (declared at /usr/include/glib-2.0/gobject/gvaluearray.h:65): Use 'GArray' instead
(emitted by gcc)
2949 } 2950 } else { 2951 g_propagate_error (error, new_error); 2952 return; 2953 } 2954 #else 2955 /* We can disable correct object-id for deletes array here */ 2956 if (!multiple_values) { 2957 guint r; 2958 2959 for (r = 0; r < resource_buffer->types->len; r++) { 2960 TrackerClass *m_class = g_ptr_array_index (resource_buffer->types, r); 2961 2962 /* We only do the old_values for GraphUpdated in tracker-store. 2963 * The subject's known classes are in resource_buffer->types 2964 * since resource_buffer_switch, so we can quickly check if any 2965 * of them has tracker:notify annotated. If so, get the old 2966 * values for the old_object_id */ 2967 2968 if (tracker_class_get_notify (m_class)) { 2969 /* This does a check_property_domain too */ 2970 old_values = get_old_property_values (property, &new_error); 2971 domain_unchecked = FALSE; 2972 if (!new_error) { 2973 if (old_values->n_values > 0) { 2974 /* evel knievel cast */ 2975 old_object_id = (gint) g_value_get_int64 (g_value_array_get_nth (old_values, 0)); 2976 } 2977 } else { 2978 g_propagate_error (error, new_error); 2979 return; 2980 } 2981 2982 break; 2983 } 2984 } 2985 } 2986 #endif /* HAVE_TRACKER_FTS */ 2987 2988 if (domain_unchecked && !check_property_domain (property)) { 2989 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_CONSTRAINT, 2990 "Subject `%s' is not in domain `%s' of property `%s'", 2991 resource_buffer->subject, 2992 tracker_class_get_name (tracker_property_get_domain (property)), 2993 tracker_property_get_name (property)); 2994 return; 2995 } 2996 2997 /* update or add value to metadata database */ 2998 change = cache_update_metadata_decomposed (property, object, 0, graph, 0, &actual_error); 2999 if (actual_error) { 3000 g_propagate_error (error, actual_error); 3001 return; 3002 } 3003 3004 if (change) { 3005 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 3006 final_prop_id = (prop_id != 0) ? prop_id : tracker_data_query_resource_id (predicate); 3007 object_id = query_resource_id (object); 3008 3009 if (!multiple_values && delete_callbacks) { 3010 guint n; 3011 3012 for (n = 0; n < delete_callbacks->len; n++) { 3013 TrackerStatementDelegate *delegate; 3014 3015 /* Don't pass object to the delete, it's not correct */ 3016 delegate = g_ptr_array_index (delete_callbacks, n); 3017 delegate->callback (graph_id, graph, resource_buffer->id, subject, 3018 final_prop_id, old_object_id, 3019 NULL, 3020 resource_buffer->types, 3021 delegate->user_data); 3022 } 3023 } 3024 3025 if (insert_callbacks) { 3026 guint n; 3027 3028 for (n = 0; n < insert_callbacks->len; n++) { 3029 TrackerStatementDelegate *delegate; 3030 3031 delegate = g_ptr_array_index (insert_callbacks, n); 3032 delegate->callback (graph_id, graph, resource_buffer->id, subject, 3033 final_prop_id, object_id, 3034 object, 3035 resource_buffer->types, 3036 delegate->user_data); 3037 } 3038 } 3039 } 3040 } 3041 3042 #ifndef DISABLE_JOURNAL 3043 if (!in_journal_replay && change && !tracker_property_get_transient (property)) { 3044 tracker_db_journal_append_update_statement_id ( 3045 (graph != NULL ? query_resource_id (graph) : 0), 3046 resource_buffer->id, 3047 final_prop_id, 3048 object_id); 3049 } 3050 #endif /* DISABLE_JOURNAL */ 3051 } 3052 3053 static void 3054 tracker_data_update_statement_with_string (const gchar *graph, 3055 const gchar *subject, 3056 const gchar *predicate, 3057 const gchar *object, 3058 GError **error) 3059 { 3060 GError *actual_error = NULL; 3061 TrackerProperty *property; 3062 gboolean change; 3063 gint graph_id = 0, pred_id = 0; 3064 gboolean multiple_values; 3065 #ifndef DISABLE_JOURNAL 3066 gboolean tried = FALSE; 3067 #endif 3068 #if HAVE_TRACKER_FTS 3069 GError *new_error = NULL; 3070 #endif /* HAVE_TRACKER_FTS */ 3071 3072 g_return_if_fail (subject != NULL); 3073 g_return_if_fail (predicate != NULL); 3074 g_return_if_fail (in_transaction); 3075 3076 property = tracker_ontologies_get_property_by_uri (predicate); 3077 if (property == NULL) { 3078 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 3079 "Property '%s' not found in the ontology", predicate); 3080 return; 3081 } else { 3082 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_RESOURCE) { 3083 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_TYPE, 3084 "Property '%s' only accepts URIs", predicate); 3085 return; 3086 } 3087 pred_id = tracker_property_get_id (property); 3088 } 3089 3090 multiple_values = tracker_property_get_multiple_values (property); 3091 3092 if (!tracker_property_get_transient (property)) { 3093 has_persistent = TRUE; 3094 } 3095 3096 /* Update and insert share the exact same code here */ 3097 if (!tracker_data_insert_statement_common (graph, subject, predicate, object, &actual_error)) { 3098 if (actual_error) { 3099 g_propagate_error (error, actual_error); 3100 return; 3101 } 3102 3103 return; 3104 } 3105 3106 #if HAVE_TRACKER_FTS 3107 /* This is unavoidable with FTS */ 3108 get_old_property_values (property, &new_error); 3109 if (new_error) { 3110 g_propagate_error (error, new_error); 3111 return; 3112 } 3113 #else 3114 if (!check_property_domain (property)) { 3115 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_CONSTRAINT, 3116 "Subject `%s' is not in domain `%s' of property `%s'", 3117 resource_buffer->subject, 3118 tracker_class_get_name (tracker_property_get_domain (property)), 3119 tracker_property_get_name (property)); 3120 return; 3121 } 3122 #endif /* HAVE_TRACKER_FTS */ 3123 3124 /* add or update value to metadata database */ 3125 change = cache_update_metadata_decomposed (property, object, 0, graph, 0, &actual_error); 3126 if (actual_error) { 3127 g_propagate_error (error, actual_error); 3128 return; 3129 } 3130 3131 if (((!multiple_values && delete_callbacks) || insert_callbacks) && change) { 3132 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 3133 pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (predicate); 3134 #ifndef DISABLE_JOURNAL 3135 tried = TRUE; 3136 #endif 3137 } 3138 3139 if ((!multiple_values && delete_callbacks) && change) { 3140 guint n; 3141 3142 for (n = 0; n < delete_callbacks->len; n++) { 3143 TrackerStatementDelegate *delegate; 3144 3145 /* Don't pass object to the delete, it's not correct */ 3146 delegate = g_ptr_array_index (delete_callbacks, n); 3147 delegate->callback (graph_id, graph, resource_buffer->id, subject, 3148 pred_id, 0 /* Always a literal */, 3149 NULL, 3150 resource_buffer->types, 3151 delegate->user_data); 3152 } 3153 } 3154 3155 if (insert_callbacks && change) { 3156 guint n; 3157 for (n = 0; n < insert_callbacks->len; n++) { 3158 TrackerStatementDelegate *delegate; 3159 3160 delegate = g_ptr_array_index (insert_callbacks, n); 3161 delegate->callback (graph_id, graph, resource_buffer->id, subject, 3162 pred_id, 0 /* Always a literal */, 3163 object, 3164 resource_buffer->types, 3165 delegate->user_data); 3166 } 3167 } 3168 3169 #ifndef DISABLE_JOURNAL 3170 if (!in_journal_replay && change && !tracker_property_get_transient (property)) { 3171 if (!tried) { 3172 graph_id = (graph != NULL ? query_resource_id (graph) : 0); 3173 pred_id = (pred_id != 0) ? pred_id : tracker_data_query_resource_id (predicate); 3174 } 3175 if (!tracker_property_get_force_journal (property) && 3176 g_strcmp0 (graph, TRACKER_MINER_FS_GRAPH_URN) == 0) { 3177 /* do not journal this statement extracted from filesystem */ 3178 TrackerProperty *damaged; 3179 3180 damaged = tracker_ontologies_get_property_by_uri (TRACKER_TRACKER_PREFIX "damaged"); 3181 tracker_db_journal_append_update_statement (graph_id, 3182 resource_buffer->id, 3183 tracker_property_get_id (damaged), 3184 "true"); 3185 } else { 3186 tracker_db_journal_append_update_statement (graph_id, 3187 resource_buffer->id, 3188 pred_id, 3189 object); 3190 } 3191 } 3192 #endif /* DISABLE_JOURNAL */ 3193 } 3194 3195 void 3196 tracker_data_update_statement (const gchar *graph, 3197 const gchar *subject, 3198 const gchar *predicate, 3199 const gchar *object, 3200 GError **error) 3201 { 3202 TrackerProperty *property; 3203 3204 g_return_if_fail (subject != NULL); 3205 g_return_if_fail (predicate != NULL); 3206 g_return_if_fail (in_transaction); 3207 3208 property = tracker_ontologies_get_property_by_uri (predicate); 3209 if (property != NULL) { 3210 if (object == NULL) { 3211 GError *new_error = NULL; 3212 if (property == tracker_ontologies_get_rdf_type ()) { 3213 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNSUPPORTED, 3214 "Using 'null' with '%s' is not supported", predicate); 3215 return; 3216 } 3217 3218 /* Flush upfront to make a null,x,null,y,z work: When x is set then 3219 * if a null comes, we need to be flushed */ 3220 3221 tracker_data_update_buffer_flush (&new_error); 3222 if (new_error) { 3223 g_propagate_error (error, new_error); 3224 return; 3225 } 3226 3227 delete_all_objects (graph, subject, predicate, error); 3228 3229 /* Flush at the end to make null, x work. When x arrives the null 3230 * (delete all values of the multivalue) must be flushed */ 3231 3232 tracker_data_update_buffer_flush (&new_error); 3233 if (new_error) { 3234 g_propagate_error (error, new_error); 3235 } 3236 } else { 3237 if (tracker_property_get_data_type (property) == TRACKER_PROPERTY_TYPE_RESOURCE) { 3238 tracker_data_update_statement_with_uri (graph, subject, predicate, object, error); 3239 } else { 3240 tracker_data_update_statement_with_string (graph, subject, predicate, object, error); 3241 } 3242 } 3243 } else { 3244 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_UNKNOWN_PROPERTY, 3245 "Property '%s' not found in the ontology", predicate); 3246 } 3247 } 3248 3249 void 3250 tracker_data_begin_transaction (GError **error) 3251 { 3252 TrackerDBInterface *iface; 3253 3254 g_return_if_fail (!in_transaction); 3255 3256 if (!tracker_db_manager_has_enough_space ()) { 3257 g_set_error (error, TRACKER_SPARQL_ERROR, TRACKER_SPARQL_ERROR_NO_SPACE, 3258 "There is not enough space on the file system for update operations"); 3259 return; 3260 } 3261 3262 resource_time = time (NULL); 3263 3264 has_persistent = FALSE; 3265 3266 if (update_buffer.resource_cache == NULL) { 3267 update_buffer.resource_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); 3268 /* used for normal transactions */ 3269 update_buffer.resources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) resource_buffer_free); 3270 /* used for journal replay */ 3271 update_buffer.resources_by_id = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) resource_buffer_free); 3272 } 3273 3274 resource_buffer = NULL; 3275 if (blank_buffer.table == NULL) { 3276 blank_buffer.table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); 3277 } 3278 3279 iface = tracker_db_manager_get_db_interface (); 3280 3281 tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", TRACKER_DB_CACHE_SIZE_UPDATE); 3282 3283 tracker_db_interface_start_transaction (iface); 3284 3285 #ifndef DISABLE_JOURNAL 3286 if (!in_journal_replay) { 3287 if (in_ontology_transaction) { 3288 GError *n_error = NULL; 3289 tracker_db_journal_start_ontology_transaction (resource_time, &n_error); 3290 3291 if (n_error) { 3292 /* No need for rollback here */ 3293 tracker_db_interface_end_db_transaction (iface, NULL); 3294 g_propagate_error (error, n_error); 3295 return; 3296 } 3297 3298 } else { 3299 tracker_db_journal_start_transaction (resource_time); 3300 } 3301 } 3302 #endif /* DISABLE_JOURNAL */ 3303 3304 iface = tracker_db_manager_get_db_interface ();
Value stored to 'iface' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

3305 3306 in_transaction = TRUE; 3307 } 3308 3309 void 3310 tracker_data_begin_ontology_transaction (GError **error) 3311 { 3312 in_ontology_transaction = TRUE; 3313 tracker_data_begin_transaction (error); 3314 } 3315 3316 void 3317 tracker_data_begin_transaction_for_replay (time_t time, GError **error) 3318 { 3319 in_journal_replay = TRUE; 3320 tracker_data_begin_transaction (error); 3321 resource_time = time; 3322 } 3323 3324 void 3325 tracker_data_commit_transaction (GError **error) 3326 { 3327 TrackerDBInterface *iface; 3328 GError *actual_error = NULL; 3329 3330 g_return_if_fail (in_transaction); 3331 3332 iface = tracker_db_manager_get_db_interface (); 3333 3334 tracker_data_update_buffer_flush (&actual_error); 3335 if (actual_error) { 3336 tracker_data_rollback_transaction (); 3337 g_propagate_error (error, actual_error); 3338 return; 3339 } 3340 3341 tracker_db_interface_end_db_transaction (iface, 3342 &actual_error); 3343 3344 if (actual_error) { 3345 tracker_data_rollback_transaction (); 3346 g_propagate_error (error, actual_error); 3347 return; 3348 } 3349 3350 #ifndef DISABLE_JOURNAL 3351 if (!in_journal_replay) { 3352 if (has_persistent || in_ontology_transaction) { 3353 tracker_db_journal_commit_db_transaction (&actual_error); 3354 } else { 3355 /* If we only had transient properties, then we must not write 3356 * anything to the journal. So we roll it back, but only the 3357 * journal's part. */ 3358 tracker_db_journal_rollback_transaction (&actual_error); 3359 } 3360 3361 if (actual_error) { 3362 /* Can't write in journal anymore; quite a serious problem */ 3363 g_propagate_error (error, actual_error); 3364 /* Don't return, remainder of the function cleans things up */ 3365 } 3366 } 3367 #endif /* DISABLE_JOURNAL */ 3368 3369 get_transaction_modseq (); 3370 if (has_persistent && !in_ontology_transaction) { 3371 transaction_modseq++; 3372 } 3373 3374 resource_time = 0; 3375 in_transaction = FALSE; 3376 in_ontology_transaction = FALSE; 3377 3378 if (update_buffer.class_counts) { 3379 /* successful transaction, no need to rollback class counts, 3380 so remove them */ 3381 g_hash_table_remove_all (update_buffer.class_counts); 3382 } 3383 3384 #if HAVE_TRACKER_FTS 3385 if (update_buffer.fts_ever_updated) { 3386 update_buffer.fts_ever_updated = FALSE; 3387 } 3388 #endif 3389 3390 tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", TRACKER_DB_CACHE_SIZE_DEFAULT); 3391 3392 g_hash_table_remove_all (update_buffer.resources); 3393 g_hash_table_remove_all (update_buffer.resources_by_id); 3394 g_hash_table_remove_all (update_buffer.resource_cache); 3395 3396 in_journal_replay = FALSE; 3397 } 3398 3399 void 3400 tracker_data_notify_transaction (TrackerDataCommitType commit_type) 3401 { 3402 if (commit_callbacks) { 3403 guint n; 3404 for (n = 0; n < commit_callbacks->len; n++) { 3405 TrackerCommitDelegate *delegate; 3406 delegate = g_ptr_array_index (commit_callbacks, n); 3407 delegate->callback (commit_type, delegate->user_data); 3408 } 3409 } 3410 } 3411 3412 void 3413 tracker_data_rollback_transaction (void) 3414 { 3415 TrackerDBInterface *iface; 3416 GError *ignorable = NULL; 3417 3418 g_return_if_fail (in_transaction); 3419 3420 in_transaction = FALSE; 3421 in_ontology_transaction = FALSE; 3422 3423 iface = tracker_db_manager_get_db_interface (); 3424 3425 tracker_data_update_buffer_clear (); 3426 3427 tracker_db_interface_execute_query (iface, &ignorable, "ROLLBACK"); 3428 3429 if (ignorable) { 3430 g_error_free (ignorable); 3431 } 3432 3433 tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", TRACKER_DB_CACHE_SIZE_DEFAULT); 3434 3435 /* Runtime false in case of DISABLE_JOURNAL */ 3436 if (!in_journal_replay) { 3437 3438 #ifndef DISABLE_JOURNAL 3439 tracker_db_journal_rollback_transaction (&ignorable); 3440 3441 if (ignorable) { 3442 /* Not sure if this is also ignorable: it's the close() of the 3443 * journal file failing (in case of TRANSACTION_FORMAT_ONTOLOGY) */ 3444 g_warning ("Error ignored while rolling back transaction in journal: %s", 3445 ignorable->message ? ignorable->message : "No error given"); 3446 g_error_free (ignorable); 3447 } 3448 #endif /* DISABLE_JOURNAL */ 3449 3450 3451 if (rollback_callbacks) { 3452 guint n; 3453 for (n = 0; n < rollback_callbacks->len; n++) { 3454 TrackerCommitDelegate *delegate; 3455 delegate = g_ptr_array_index (rollback_callbacks, n); 3456 delegate->callback (TRUE, delegate->user_data); 3457 } 3458 } 3459 } 3460 } 3461 3462 static GVariant * 3463 update_sparql (const gchar *update, 3464 gboolean blank, 3465 GError **error) 3466 { 3467 GError *actual_error = NULL; 3468 TrackerSparqlQuery *sparql_query; 3469 GVariant *blank_nodes; 3470 3471 g_return_val_if_fail (update != NULL, NULL); 3472 3473 tracker_data_begin_transaction (&actual_error); 3474 if (actual_error) { 3475 g_propagate_error (error, actual_error); 3476 return NULL; 3477 } 3478 3479 sparql_query = tracker_sparql_query_new_update (update); 3480 blank_nodes = tracker_sparql_query_execute_update (sparql_query, blank, &actual_error); 3481 g_object_unref (sparql_query); 3482 3483 if (actual_error) { 3484 tracker_data_rollback_transaction (); 3485 g_propagate_error (error, actual_error); 3486 return NULL; 3487 } 3488 3489 tracker_data_commit_transaction (&actual_error); 3490 if (actual_error) { 3491 g_propagate_error (error, actual_error); 3492 return NULL; 3493 } 3494 3495 return blank_nodes; 3496 } 3497 3498 void 3499 tracker_data_update_sparql (const gchar *update, 3500 GError **error) 3501 { 3502 update_sparql (update, FALSE, error); 3503 } 3504 3505 GVariant * 3506 tracker_data_update_sparql_blank (const gchar *update, 3507 GError **error) 3508 { 3509 return update_sparql (update, TRUE, error); 3510 } 3511 3512 void 3513 tracker_data_load_turtle_file (GFile *file, 3514 GError **error) 3515 { 3516 gchar *path; 3517 3518 g_return_if_fail (G_IS_FILE (file) && g_file_is_native (file)); 3519 3520 path = g_file_get_path (file); 3521 tracker_turtle_reader_load (path, error); 3522 g_free (path); 3523 } 3524 3525 void 3526 tracker_data_sync (void) 3527 { 3528 #ifndef DISABLE_JOURNAL 3529 tracker_db_journal_fsync (); 3530 #endif 3531 } 3532 3533 #ifndef DISABLE_JOURNAL 3534 3535 void 3536 tracker_data_replay_journal (TrackerBusyCallback busy_callback, 3537 gpointer busy_user_data, 3538 const gchar *busy_status, 3539 GError **error) 3540 { 3541 GError *journal_error = NULL; 3542 TrackerProperty *rdf_type = NULL; 3543 gint last_operation_type = 0; 3544 const gchar *uri; 3545 GError *n_error = NULL; 3546 3547 3548 rdf_type = tracker_ontologies_get_rdf_type (); 3549 3550 tracker_db_journal_reader_init (NULL, &n_error); 3551 if (n_error) { 3552 /* This is fatal (doesn't happen when file doesn't exist, does happen 3553 * when for some other reason the reader can't be created) */ 3554 g_propagate_error (error, n_error); 3555 return; 3556 } 3557 3558 while (tracker_db_journal_reader_next (&journal_error)) { 3559 TrackerDBJournalEntryType type; 3560 const gchar *object; 3561 gint graph_id, subject_id, predicate_id, object_id; 3562 3563 type = tracker_db_journal_reader_get_type (); 3564 if (type == TRACKER_DB_JOURNAL_RESOURCE) { 3565 GError *new_error = NULL; 3566 TrackerDBInterface *iface; 3567 TrackerDBStatement *stmt; 3568 gint id; 3569 3570 tracker_db_journal_reader_get_resource (&id, &uri); 3571 3572 iface = tracker_db_manager_get_db_interface (); 3573 3574 stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE, &new_error, 3575 "INSERT INTO Resource (ID, Uri) VALUES (?, ?)"); 3576 3577 if (stmt) { 3578 tracker_db_statement_bind_int (stmt, 0, id); 3579 tracker_db_statement_bind_text (stmt, 1, uri); 3580 tracker_db_statement_execute (stmt, &new_error); 3581 g_object_unref (stmt); 3582 } 3583 3584 if (new_error) { 3585 g_warning ("Journal replay error: '%s'", new_error->message); 3586 g_error_free (new_error); 3587 } 3588 3589 } else if (type == TRACKER_DB_JOURNAL_START_TRANSACTION) { 3590 tracker_data_begin_transaction_for_replay (tracker_db_journal_reader_get_time (), NULL); 3591 } else if (type == TRACKER_DB_JOURNAL_END_TRANSACTION) { 3592 GError *new_error = NULL; 3593 tracker_data_update_buffer_might_flush (&new_error); 3594 3595 tracker_data_commit_transaction (&new_error); 3596 if (new_error) { 3597 /* Out of disk is an unrecoverable fatal error */ 3598 if (g_error_matches (new_error, TRACKER_DB_INTERFACE_ERROR, TRACKER_DB_NO_SPACE)) { 3599 g_propagate_error (error, new_error); 3600 return; 3601 } else { 3602 g_warning ("Journal replay error: '%s'", new_error->message); 3603 g_clear_error (&new_error); 3604 } 3605 } 3606 } else if (type == TRACKER_DB_JOURNAL_INSERT_STATEMENT || 3607 type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT) { 3608 GError *new_error = NULL; 3609 TrackerProperty *property = NULL; 3610 3611 tracker_db_journal_reader_get_statement (&graph_id, &subject_id, &predicate_id, &object); 3612 3613 if (last_operation_type == -1) { 3614 tracker_data_update_buffer_flush (&new_error); 3615 if (new_error) { 3616 g_warning ("Journal replay error: '%s'", new_error->message); 3617 g_clear_error (&new_error); 3618 } 3619 } 3620 last_operation_type = 1; 3621 3622 uri = tracker_ontologies_get_uri_by_id (predicate_id); 3623 if (uri) { 3624 property = tracker_ontologies_get_property_by_uri (uri); 3625 } 3626 3627 if (property) { 3628 resource_buffer_switch (NULL, graph_id, NULL, subject_id); 3629 3630 if (type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT) { 3631 cache_update_metadata_decomposed (property, object, 0, NULL, graph_id, &new_error); 3632 } else { 3633 cache_insert_metadata_decomposed (property, object, 0, NULL, graph_id, &new_error); 3634 } 3635 if (new_error) { 3636 g_warning ("Journal replay error: '%s'", new_error->message); 3637 g_clear_error (&new_error); 3638 } 3639 3640 } else { 3641 g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id); 3642 } 3643 3644 } else if (type == TRACKER_DB_JOURNAL_INSERT_STATEMENT_ID || 3645 type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID) { 3646 GError *new_error = NULL; 3647 TrackerClass *class = NULL; 3648 TrackerProperty *property = NULL; 3649 3650 tracker_db_journal_reader_get_statement_id (&graph_id, &subject_id, &predicate_id, &object_id); 3651 3652 if (last_operation_type == -1) { 3653 tracker_data_update_buffer_flush (&new_error); 3654 if (new_error) { 3655 g_warning ("Journal replay error: '%s'", new_error->message); 3656 g_clear_error (&new_error); 3657 } 3658 } 3659 last_operation_type = 1; 3660 3661 uri = tracker_ontologies_get_uri_by_id (predicate_id); 3662 if (uri) { 3663 property = tracker_ontologies_get_property_by_uri (uri); 3664 } 3665 3666 if (property) { 3667 if (tracker_property_get_data_type (property) != TRACKER_PROPERTY_TYPE_RESOURCE) { 3668 g_warning ("Journal replay error: 'property with ID %d does not account URIs'", predicate_id); 3669 } else { 3670 resource_buffer_switch (NULL, graph_id, NULL, subject_id); 3671 3672 if (property == rdf_type) { 3673 uri = tracker_ontologies_get_uri_by_id (object_id); 3674 if (uri) { 3675 class = tracker_ontologies_get_class_by_uri (uri); 3676 } 3677 if (class) { 3678 cache_create_service_decomposed (class, NULL, graph_id); 3679 } else { 3680 g_warning ("Journal replay error: 'class with ID %d not found in the ontology'", object_id); 3681 } 3682 } else { 3683 GError *new_error = NULL; 3684 3685 /* add value to metadata database */ 3686 if (type == TRACKER_DB_JOURNAL_UPDATE_STATEMENT_ID) { 3687 cache_update_metadata_decomposed (property, NULL, object_id, NULL, graph_id, &new_error); 3688 } else { 3689 cache_insert_metadata_decomposed (property, NULL, object_id, NULL, graph_id, &new_error); 3690 } 3691 3692 if (new_error) { 3693 g_warning ("Journal replay error: '%s'", new_error->message); 3694 g_error_free (new_error); 3695 } 3696 } 3697 } 3698 } else { 3699 g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id); 3700 } 3701 3702 } else if (type == TRACKER_DB_JOURNAL_DELETE_STATEMENT) { 3703 GError *new_error = NULL; 3704 TrackerProperty *property = NULL; 3705 3706 tracker_db_journal_reader_get_statement (&graph_id, &subject_id, &predicate_id, &object); 3707 3708 if (last_operation_type == 1) { 3709 tracker_data_update_buffer_flush (&new_error); 3710 if (new_error) { 3711 g_warning ("Journal replay error: '%s'", new_error->message); 3712 g_clear_error (&new_error); 3713 } 3714 } 3715 last_operation_type = -1; 3716 3717 resource_buffer_switch (NULL, graph_id, NULL, subject_id); 3718 3719 uri = tracker_ontologies_get_uri_by_id (predicate_id); 3720 if (uri) { 3721 property = tracker_ontologies_get_property_by_uri (uri); 3722 } 3723 3724 if (property) { 3725 GError *new_error = NULL; 3726 3727 if (object && rdf_type == property) { 3728 TrackerClass *class = NULL; 3729 3730 uri = tracker_ontologies_get_uri_by_id (object_id); 3731 if (uri) { 3732 class = tracker_ontologies_get_class_by_uri (uri); 3733 } 3734 if (class != NULL) { 3735 cache_delete_resource_type (class, NULL, graph_id); 3736 } else { 3737 g_warning ("Journal replay error: 'class with '%s' not found in the ontology'", object); 3738 } 3739 } else { 3740 delete_metadata_decomposed (property, object, 0, &new_error); 3741 } 3742 3743 if (new_error) { 3744 g_warning ("Journal replay error: '%s'", new_error->message); 3745 g_error_free (new_error); 3746 } 3747 3748 } else { 3749 g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id); 3750 } 3751 3752 } else if (type == TRACKER_DB_JOURNAL_DELETE_STATEMENT_ID) { 3753 GError *new_error = NULL; 3754 TrackerClass *class = NULL; 3755 TrackerProperty *property = NULL; 3756 3757 tracker_db_journal_reader_get_statement_id (&graph_id, &subject_id, &predicate_id, &object_id); 3758 3759 if (last_operation_type == 1) { 3760 tracker_data_update_buffer_flush (&new_error); 3761 if (new_error) { 3762 g_warning ("Journal replay error: '%s'", new_error->message); 3763 g_clear_error (&new_error); 3764 } 3765 } 3766 last_operation_type = -1; 3767 3768 uri = tracker_ontologies_get_uri_by_id (predicate_id); 3769 if (uri) { 3770 property = tracker_ontologies_get_property_by_uri (uri); 3771 } 3772 3773 if (property) { 3774 3775 resource_buffer_switch (NULL, graph_id, NULL, subject_id); 3776 3777 if (property == rdf_type) { 3778 uri = tracker_ontologies_get_uri_by_id (object_id); 3779 if (uri) { 3780 class = tracker_ontologies_get_class_by_uri (uri); 3781 } 3782 if (class) { 3783 cache_delete_resource_type (class, NULL, graph_id); 3784 } else { 3785 g_warning ("Journal replay error: 'class with ID %d not found in the ontology'", object_id); 3786 } 3787 } else { 3788 GError *new_error = NULL; 3789 3790 delete_metadata_decomposed (property, NULL, object_id, &new_error); 3791 3792 if (new_error) { 3793 g_warning ("Journal replay error: '%s'", new_error->message); 3794 g_error_free (new_error); 3795 } 3796 } 3797 } else { 3798 g_warning ("Journal replay error: 'property with ID %d doesn't exist'", predicate_id); 3799 } 3800 } 3801 3802 if (busy_callback) { 3803 busy_callback (busy_status, 3804 tracker_db_journal_reader_get_progress (), 3805 busy_user_data); 3806 } 3807 } 3808 3809 3810 if (journal_error) { 3811 GError *n_error = NULL; 3812 gsize size; 3813 3814 size = tracker_db_journal_reader_get_size_of_correct (); 3815 tracker_db_journal_reader_shutdown (); 3816 3817 tracker_db_journal_init (NULL, FALSE, &n_error); 3818 if (n_error) { 3819 g_clear_error (&journal_error); 3820 /* This is fatal (journal file not writable, etc) */ 3821 g_propagate_error (error, n_error); 3822 return; 3823 } 3824 tracker_db_journal_truncate (size); 3825 tracker_db_journal_shutdown (&n_error); 3826 3827 if (n_error) { 3828 g_clear_error (&journal_error); 3829 /* This is fatal (close of journal file failed after truncate) */ 3830 g_propagate_error (error, n_error); 3831 return; 3832 } 3833 3834 g_clear_error (&journal_error); 3835 } else { 3836 tracker_db_journal_reader_shutdown (); 3837 } 3838 } 3839 3840 #else 3841 3842 void 3843 tracker_data_replay_journal (TrackerBusyCallback busy_callback, 3844 gpointer busy_user_data, 3845 const gchar *busy_status, 3846 GError **error) 3847 { 3848 g_critical ("Not good. We disabled the journal and yet replaying it got called"); 3849 } 3850 3851 #endif /* DISABLE_JOURNAL */