tracker-0.16.2/src/tracker-utils/tracker-sparql.c

No issues found

  1 /*
  2  * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
  3  *
  4  * This library is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Library General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) any later version.
  8  *
  9  * This library is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Library General Public
 15  * License along with this library; if not, write to the
 16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 17  * Boston, MA  02110-1301, USA.
 18  */
 19 
 20 #include "config.h"
 21 
 22 #include <sys/param.h>
 23 #include <stdlib.h>
 24 #include <time.h>
 25 #include <locale.h>
 26 
 27 #include <glib.h>
 28 #include <glib/gi18n.h>
 29 
 30 #include <libtracker-sparql/tracker-sparql.h>
 31 
 32 #define ABOUT \
 33 	"Tracker " PACKAGE_VERSION "\n"
 34 
 35 #define LICENSE \
 36 	"This program is free software and comes without any warranty.\n" \
 37 	"It is licensed under version 2 or later of the General Public " \
 38 	"License which can be viewed at:\n" \
 39 	"\n" \
 40 	"  http://www.gnu.org/licenses/gpl.txt\n"
 41 
 42 static gboolean parse_list_notifies (const gchar  *option_name,
 43                                      const gchar  *value,
 44                                      gpointer      data,
 45                                      GError      **error);
 46 static gboolean parse_list_indexes  (const gchar  *option_name,
 47                                      const gchar  *value,
 48                                      gpointer      data,
 49                                      GError      **error);
 50 
 51 static gchar *file;
 52 static gchar *query;
 53 static gboolean update;
 54 static gboolean list_classes;
 55 static gboolean list_class_prefixes;
 56 static gchar *list_properties;
 57 static gchar *list_notifies;
 58 static gchar *list_indexes;
 59 static gboolean print_version;
 60 static gchar *search;
 61 
 62 static GOptionEntry   entries[] = {
 63 	{ "file", 'f', 0, G_OPTION_ARG_FILENAME, &file,
 64 	  N_("Path to use to run a query or update from file"),
 65 	  N_("FILE"),
 66 	},
 67 	{ "query", 'q', 0, G_OPTION_ARG_STRING, &query,
 68 	  N_("SPARQL query"),
 69 	  N_("SPARQL"),
 70 	},
 71 	{ "update", 'u', 0, G_OPTION_ARG_NONE, &update,
 72 	  N_("This is used with --query and for database updates only."),
 73 	  NULL,
 74 	},
 75 	{ "list-classes", 'c', 0, G_OPTION_ARG_NONE, &list_classes,
 76 	  N_("Retrieve classes"),
 77 	  NULL,
 78 	},
 79 	{ "list-class-prefixes", 'x', 0, G_OPTION_ARG_NONE, &list_class_prefixes,
 80 	  N_("Retrieve class prefixes"),
 81 	  NULL,
 82 	},
 83 	{ "list-properties", 'p', 0, G_OPTION_ARG_STRING, &list_properties,
 84 	  N_("Retrieve properties for a class, prefixes can be used too (e.g. rdfs:Resource)"),
 85 	  N_("CLASS"),
 86 	},
 87 	{ "list-notifies", 'n', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_list_notifies,
 88 	  N_("Retrieve classes which notify changes in the database (CLASS is optional)"),
 89 	  N_("CLASS"),
 90 	},
 91 	{ "list-indexes", 'i', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_list_indexes,
 92 	  N_("Retrieve indexes used in database to improve performance (PROPERTY is optional) "),
 93 	  N_("PROPERTY"),
 94 	},
 95 	{ "search", 's', 0, G_OPTION_ARG_STRING, &search,
 96 	  N_("Search for a class or property and display more information (e.g. Document)"),
 97 	  N_("CLASS/PROPERTY"),
 98 	},
 99 	{ "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
100 	  N_("Print version"),
101 	  NULL,
102 	},
103 	{ NULL }
104 };
105 
106 static GHashTable *
107 get_prefixes (TrackerSparqlConnection *connection)
108 {
109 	TrackerSparqlCursor *cursor;
110 	GError *error = NULL;
111 	GHashTable *retval;
112 	const gchar *query;
113 
114 	retval = g_hash_table_new_full (g_str_hash,
115 	                                g_str_equal,
116 	                                g_free,
117 	                                g_free);
118 
119 	/* FIXME: Would like to get this in the same SPARQL that we
120 	 * use to get the info, but doesn't seem possible at the
121 	 * moment with the limited string manipulation features we
122 	 * support in SPARQL.
123 	 */
124 	query = "SELECT ?ns ?prefix "
125 	        "WHERE {"
126 	        "  ?ns a tracker:Namespace ;"
127 	        "  tracker:prefix ?prefix "
128 	        "}";
129 
130 	cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
131 
132 	if (error) {
133 		g_printerr ("%s, %s\n",
134 			    _("Unable to retrieve namespace prefixes"),
135 			    error->message);
136 
137 		g_error_free (error);
138 		return retval;
139 	}
140 
141 	if (!cursor) {
142 		g_printerr ("%s\n", _("No namespace prefixes were returned"));
143 		return retval;
144 	}
145 
146 	while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
147 		const gchar *key, *value;
148 
149 		key = tracker_sparql_cursor_get_string (cursor, 0, NULL);
150 		value = tracker_sparql_cursor_get_string (cursor, 1, NULL);
151 
152 		if (!key || !value) {
153 			continue;
154 		}
155 
156 		g_hash_table_insert (retval,
157 		                     g_strndup (key, strlen (key) - 1),
158 		                     g_strdup (value));
159 	}
160 
161 	if (cursor) {
162 		g_object_unref (cursor);
163 	}
164 
165 	return retval;
166 }
167 
168 static gchar *
169 get_class_from_prefix (TrackerSparqlConnection *connection,
170                        const gchar             *prefix)
171 {
172 	GError *error = NULL;
173 	TrackerSparqlCursor *cursor;
174 	const gchar *query;
175 	gchar *found = NULL;
176 
177 	query = "SELECT ?prefix ?ns "
178 		"WHERE {"
179 		"  ?ns a tracker:Namespace ;"
180 		"  tracker:prefix ?prefix "
181 		"}";
182 
183 	/* We have namespace prefix, get full name */
184 	cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
185 
186 	if (error) {
187 		g_printerr ("%s, %s\n",
188 		            _("Could not get namespace prefixes"),
189 		            error->message);
190 		g_error_free (error);
191 
192 		return NULL;
193 	}
194 
195 	if (!cursor) {
196 		g_printerr ("%s\n",
197 		            _("No namespace prefixes were found"));
198 
199 		return NULL;
200 	}
201 
202 	while (tracker_sparql_cursor_next (cursor, NULL, NULL) && !found) {
203 		const gchar *class_prefix, *class_name;
204 
205 		class_prefix = tracker_sparql_cursor_get_string (cursor, 0, NULL);
206 		class_name = tracker_sparql_cursor_get_string (cursor, 1, NULL);
207 
208 		if (strcmp (class_prefix, prefix) == 0) {
209 			found = g_strdup (class_name);
210 		}
211 	}
212 
213 	g_object_unref (cursor);
214 
215 	return found;
216 }
217 
218 static gboolean
219 parse_list_notifies (const gchar  *option_name,
220                      const gchar  *value,
221                      gpointer      data,
222                      GError      **error)
223 {
224 	if (!value) {
225 		list_notifies = g_strdup ("");
226 	} else {
227 		list_notifies = g_strdup (value);
228 	}
229 
230 	return TRUE;
231 }
232 
233 static gboolean
234 parse_list_indexes (const gchar  *option_name,
235                     const gchar  *value,
236                     gpointer      data,
237                     GError      **error)
238 {
239 	if (!value) {
240 		list_indexes = g_strdup ("");
241 	} else {
242 		list_indexes = g_strdup (value);
243 	}
244 
245 	return TRUE;
246 }
247 
248 inline static gchar *
249 get_shorthand (GHashTable  *prefixes,
250                const gchar *namespace)
251 {
252 	gchar *hash;
253 
254 	hash = strrchr (namespace, '#');
255 
256 	if (hash) {
257 		gchar *property;
258 		const gchar *prefix;
259 
260 		property = hash + 1;
261 		*hash = '\0';
262 
263 		prefix = g_hash_table_lookup (prefixes, namespace);
264 
265 		return g_strdup_printf ("%s:%s", prefix, property);
266 	}
267 
268 	return g_strdup (namespace);
269 }
270 
271 inline static gchar *
272 get_shorthand_for_offsets (GHashTable  *prefixes,
273                            const gchar *str)
274 {
275 	GString *result = NULL;
276 	gchar **properties;
277 	gint i;
278 
279 	if (!str) {
280 		return NULL;
281 	}
282 
283 	properties = g_strsplit (str, ",", -1);
284 	if (!properties) {
285 		return NULL;
286 	}
287 
288 	for (i = 0; properties[i] != NULL && properties[i + 1] != NULL; i += 2) {
289 		const gchar *property;
290 		const gchar *offset;
291 		gchar *shorthand;
292 
293 		property = properties[i];
294 		offset = properties[i + 1];
295 
296 		if (!property || !offset) {
297 			g_warning ("Expected property AND offset to be valid for fts:offset results");
298 			continue;
299 		}
300 
301 		shorthand = get_shorthand (prefixes, property);
302 		/* shorthand = g_hash_table_lookup (prefixes, property); */
303 
304 		if (!shorthand) {
305 			shorthand = g_strdup (property);
306 		}
307 
308 		if (!result) {
309 			result = g_string_new ("");
310 		} else {
311 			result = g_string_append_c (result, ' ');
312 		}
313 
314 		g_string_append_printf (result, "%s:%s", shorthand, offset);
315 		g_free (shorthand);
316 	}
317 
318 	g_strfreev (properties);
319 
320 	return result ? g_string_free (result, FALSE) : NULL;
321 }
322 
323 static void
324 print_cursor_with_ftsoffsets (TrackerSparqlCursor *cursor,
325                               GHashTable          *prefixes,
326                               const gchar         *none_found,
327                               const gchar         *heading,
328                               gboolean             only_first_col)
329 {
330 	if (!cursor) {
331 		g_print ("%s\n", none_found);
332 	} else {
333 		gint count = 0;
334 
335 		g_print ("%s:\n", heading);
336 
337 		if (only_first_col) {
338 			while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
339 				const gchar *str;
340 				gchar *shorthand;
341 
342 				str = tracker_sparql_cursor_get_string (cursor, 0, NULL);
343 				shorthand = get_shorthand_for_offsets (prefixes, str);
344 				g_print ("  %s\n", shorthand ? shorthand : str);
345 				g_free (shorthand);
346 				count++;
347 			}
348 		} else {
349 			while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
350 				gint col;
351 
352 				for (col = 0; col < tracker_sparql_cursor_get_n_columns (cursor); col++) {
353 					const gchar *str;
354 					gchar *shorthand;
355 
356 					str = tracker_sparql_cursor_get_string (cursor, col, NULL);
357 					shorthand = get_shorthand_for_offsets (prefixes, str);
358 					g_print ("%c %s",
359 					         col == 0 ? ' ' : ',',
360 					         shorthand ? shorthand : str);
361 					g_free (shorthand);
362 				}
363 
364 				g_print ("\n");
365 
366 				count++;
367 			}
368 		}
369 
370 		if (count == 0) {
371 			g_print ("  %s\n", _("None"));
372 		}
373 
374 		g_print ("\n");
375 
376 		g_object_unref (cursor);
377 	}
378 }
379 
380 static void
381 print_cursor (TrackerSparqlCursor *cursor,
382               const gchar         *none_found,
383               const gchar         *heading,
384               gboolean             only_first_col)
385 {
386 	if (!cursor) {
387 		g_print ("%s\n", none_found);
388 	} else {
389 		gint count = 0;
390 
391 		g_print ("%s:\n", heading);
392 
393 		if (only_first_col) {
394 			while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
395 				g_print ("  %s\n", tracker_sparql_cursor_get_string (cursor, 0, NULL));
396 				count++;
397 			}
398 		} else {
399 			while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
400 				gint col;
401 
402 				for (col = 0; col < tracker_sparql_cursor_get_n_columns (cursor); col++) {
403 					g_print ("%c %s",
404 					         col == 0 ? ' ' : ',',
405 					         tracker_sparql_cursor_get_string (cursor, col, NULL));
406 				}
407 
408 				g_print ("\n");
409 
410 				count++;
411 			}
412 		}
413 
414 		if (count == 0) {
415 			g_print ("  %s\n", _("None"));
416 		}
417 
418 		g_print ("\n");
419 
420 		g_object_unref (cursor);
421 	}
422 }
423 
424 int
425 main (int argc, char **argv)
426 {
427 	TrackerSparqlConnection *connection;
428 	TrackerSparqlCursor *cursor;
429 	GOptionContext *context;
430 	GError *error = NULL;
431 	const gchar *error_message;
432 
433 	setlocale (LC_ALL, "");
434 
435 	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
436 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
437 	textdomain (GETTEXT_PACKAGE);
438 
439 	context = g_option_context_new (_("- Query or update using SPARQL"));
440 
441 	g_option_context_add_main_entries (context, entries, NULL);
442 	g_option_context_parse (context, &argc, &argv, NULL);
443 
444 	if (print_version) {
445 		g_print ("\n" ABOUT "\n" LICENSE "\n");
446 		g_option_context_free (context);
447 
448 		return EXIT_SUCCESS;
449 	}
450 
451 	if (!list_classes && !list_class_prefixes && !list_properties &&
452 	    !list_notifies && !list_indexes && !search && !file && !query) {
453 		error_message = _("An argument must be supplied");
454 	} else if (file && query) {
455 		error_message = _("File and query can not be used together");
456 	} else {
457 		error_message = NULL;
458 	}
459 
460 	if (error_message) {
461 		gchar *help;
462 
463 		g_printerr ("%s\n\n", error_message);
464 
465 		help = g_option_context_get_help (context, TRUE, NULL);
466 		g_option_context_free (context);
467 		g_printerr ("%s", help);
468 		g_free (help);
469 
470 		return EXIT_FAILURE;
471 	}
472 
473 	g_option_context_free (context);
474 
475 	connection = tracker_sparql_connection_get (NULL, &error);
476 
477 	if (!connection) {
478 		g_printerr ("%s: %s\n",
479 		            _("Could not establish a connection to Tracker"),
480 		            error ? error->message : _("No error given"));
481 		g_clear_error (&error);
482 		return EXIT_FAILURE;
483 	}
484 
485 	if (list_classes) {
486 		const gchar *query;
487 
488 		query = "SELECT ?c WHERE { ?c a rdfs:Class }";
489 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
490 
491 		if (error) {
492 			g_printerr ("%s, %s\n",
493 			            _("Could not list classes"),
494 			            error->message);
495 			g_error_free (error);
496 			g_object_unref (connection);
497 
498 			return EXIT_FAILURE;
499 		}
500 
501 		print_cursor (cursor, _("No classes were found"), _("Classes"), TRUE);
502 	}
503 
504 	if (list_class_prefixes) {
505 		const gchar *query;
506 
507 		query = "SELECT ?prefix ?ns "
508 		        "WHERE {"
509 		        "  ?ns a tracker:Namespace ;"
510 		        "  tracker:prefix ?prefix "
511 		        "}";
512 
513 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
514 
515 		if (error) {
516 			g_printerr ("%s, %s\n",
517 			            _("Could not list class prefixes"),
518 			            error->message);
519 			g_error_free (error);
520 			g_object_unref (connection);
521 
522 			return EXIT_FAILURE;
523 		}
524 
525 		print_cursor (cursor, _("No class prefixes were found"), _("Prefixes"), FALSE);
526 	}
527 
528 	if (list_properties) {
529 		gchar *query;
530 		gchar *class_name;
531 
532 		if (g_str_has_prefix (list_properties, "http://")) {
533 			/* We have full class name */
534 			class_name = g_strdup (list_properties);
535 		} else {
536 			gchar *p;
537 			gchar *prefix, *property;
538 			gchar *class_name_no_property;
539 
540 			prefix = g_strdup (list_properties);
541 			p = strchr (prefix, ':');
542 
543 			if (!p) {
544 				g_printerr ("%s\n",
545 				            _("Could not find property for class prefix, "
546 				              "e.g. :Resource in 'rdfs:Resource'"));
547 				g_free (prefix);
548 				g_object_unref (connection);
549 
550 				return EXIT_FAILURE;
551 			}
552 
553 			property = g_strdup (p + 1);
554 			*p = '\0';
555 
556 			class_name_no_property = get_class_from_prefix (connection, prefix);
557 			g_free (prefix);
558 
559 			if (!class_name_no_property) {
560 				g_free (property);
561 				g_object_unref (connection);
562 
563 				return EXIT_FAILURE;
564 			}
565 
566 			class_name = g_strconcat (class_name_no_property, property, NULL);
567 			g_free (class_name_no_property);
568 			g_free (property);
569 		}
570 
571 		query = g_strdup_printf ("SELECT ?p "
572 		                         "WHERE {"
573 		                         "  ?p a rdf:Property ;"
574 		                         "  rdfs:domain <%s>"
575 		                         "}",
576 		                         class_name);
577 
578 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
579 		g_free (query);
580 		g_free (class_name);
581 
582 		if (error) {
583 			g_printerr ("%s, %s\n",
584 			            _("Could not list properties"),
585 			            error->message);
586 			g_error_free (error);
587 			g_object_unref (connection);
588 
589 			return EXIT_FAILURE;
590 		}
591 
592 		print_cursor (cursor, _("No properties were found"), _("Properties"), TRUE);
593 	}
594 
595 	if (list_notifies) {
596 		gchar *query;
597 
598 		/* First list classes */
599 		if (*list_notifies == '\0') {
600 			query = g_strdup_printf ("SELECT ?c "
601 			                         "WHERE {"
602 			                         "  ?c a rdfs:Class ."
603 			                         "  ?c tracker:notify true ."
604 			                         "}");
605 		} else {
606 			query = g_strdup_printf ("SELECT ?c "
607 			                         "WHERE {"
608 			                         "  ?c a rdfs:Class ."
609 			                         "  ?c tracker:notify true "
610 			                         "  FILTER regex (?c, \"%s\", \"i\") "
611 			                         "}",
612 			                         list_notifies);
613 		}
614 
615 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
616 		g_free (query);
617 
618 		if (error) {
619 			g_printerr ("%s, %s\n",
620 			            _("Could not find notify classes"),
621 			            error->message);
622 			g_error_free (error);
623 			g_object_unref (connection);
624 
625 			return EXIT_FAILURE;
626 		}
627 
628 		print_cursor (cursor, _("No notifies were found"), _("Notifies"), TRUE);
629 	}
630 
631 	if (list_indexes) {
632 		gchar *query;
633 
634 		/* First list classes */
635 		if (*list_indexes == '\0') {
636 			query = g_strdup_printf ("SELECT ?p "
637 			                         "WHERE {"
638 			                         "  ?p tracker:indexed true ."
639 			                         "}");
640 		} else {
641 			query = g_strdup_printf ("SELECT ?p "
642 			                         "WHERE {"
643 			                         "  ?p tracker:indexed true "
644 			                         "  FILTER regex (?p, \"%s\", \"i\") "
645 			                         "}",
646 			                         list_indexes);
647 		}
648 
649 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
650 		g_free (query);
651 
652 		if (error) {
653 			g_printerr ("%s, %s\n",
654 			            _("Could not find indexed properties"),
655 			            error->message);
656 			g_error_free (error);
657 			g_object_unref (connection);
658 
659 			return EXIT_FAILURE;
660 		}
661 
662 		print_cursor (cursor, _("No indexes were found"), _("Indexes"), TRUE);
663 	}
664 
665 	if (search) {
666 		gchar *query;
667 
668 		/* First list classes */
669 		query = g_strdup_printf ("SELECT ?c "
670 		                         "WHERE {"
671 		                         "  ?c a rdfs:Class"
672 		                         "  FILTER regex (?c, \"%s\", \"i\") "
673 		                         "}",
674 		                         search);
675 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
676 		g_free (query);
677 
678 		if (error) {
679 			g_printerr ("%s, %s\n",
680 			            _("Could not search classes"),
681 			            error->message);
682 			g_error_free (error);
683 			g_object_unref (connection);
684 
685 			return EXIT_FAILURE;
686 		}
687 
688 		print_cursor (cursor, _("No classes were found to match search term"), _("Classes"), TRUE);
689 
690 		/* Second list properties */
691 		query = g_strdup_printf ("SELECT ?p "
692 		                         "WHERE {"
693 		                         "  ?p a rdf:Property"
694 		                         "  FILTER regex (?p, \"%s\", \"i\") "
695 		                         "}",
696 		                         search);
697 
698 		cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
699 		g_free (query);
700 
701 		if (error) {
702 			g_printerr ("%s, %s\n",
703 			            _("Could not search properties"),
704 			            error->message);
705 			g_error_free (error);
706 			g_object_unref (connection);
707 
708 			return EXIT_FAILURE;
709 		}
710 
711 		print_cursor (cursor, _("No properties were found to match search term"), _("Properties"), TRUE);
712 	}
713 
714 	if (file) {
715 		gchar *path_in_utf8;
716 		gsize size;
717 
718 		path_in_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, &error);
719 		if (error) {
720 			g_printerr ("%s:'%s', %s\n",
721 			            _("Could not get UTF-8 path from path"),
722 			            file,
723 			            error->message);
724 			g_error_free (error);
725 			g_object_unref (connection);
726 
727 			return EXIT_FAILURE;
728 		}
729 
730 		g_file_get_contents (path_in_utf8, &query, &size, &error);
731 		if (error) {
732 			g_printerr ("%s:'%s', %s\n",
733 			            _("Could not read file"),
734 			            path_in_utf8,
735 			            error->message);
736 			g_error_free (error);
737 			g_free (path_in_utf8);
738 			g_object_unref (connection);
739 
740 			return EXIT_FAILURE;
741 		}
742 
743 		g_free (path_in_utf8);
744 	}
745 
746 	if (query) {
747 		if (G_UNLIKELY (update)) {
748 			tracker_sparql_connection_update (connection, query, 0, NULL, &error);
749 
750 			if (error) {
751 				g_printerr ("%s, %s\n",
752 				            _("Could not run update"),
753 				            error->message);
754 				g_error_free (error);
755 
756 				return EXIT_FAILURE;
757 			}
758 
759 			g_print ("%s\n", _("Done"));
760 
761 #if 0
762 			if (results) {
763 				GPtrArray *insert;
764 				GHashTable *solution;
765 				GHashTableIter iter;
766 				gpointer key, value;
767 				gint i, s, n;
768 
769 				for (i = 0; i < results->len; i++) {
770 					insert = results->pdata[i];
771 
772 					for (s = 0; s < insert->len; s++) {
773 						solution = insert->pdata[s];
774 
775 						g_hash_table_iter_init (&iter, solution);
776 						n = 0;
777 						while (g_hash_table_iter_next (&iter, &key, &value)) {
778 							g_print ("%s%s: %s",
779 							         n > 0 ? ", " : "",
780 							         (const gchar *) key,
781 							         (const gchar *) value);
782 							n++;
783 						}
784 						g_print ("\n");
785 					}
786 				}
787 			}
788 #endif
789 		} else {
790 			GHashTable *prefixes = NULL;
791 
792 			if (strstr (query, "fts:offsets")) {
793 				prefixes = get_prefixes (connection);
794 			}
795 
796 			cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
797 
798 			if (error) {
799 				g_printerr ("%s, %s\n",
800 				            _("Could not run query"),
801 				            error->message);
802 				g_error_free (error);
803 
804 				if (prefixes) {
805 					g_hash_table_unref (prefixes);
806 				}
807 
808 				return EXIT_FAILURE;
809 			}
810 
811 			if (G_UNLIKELY (prefixes)) {
812 				print_cursor_with_ftsoffsets (cursor, prefixes, _("No results found matching your query"), _("Results"), FALSE);
813 				g_hash_table_unref (prefixes);
814 			} else {
815 				print_cursor (cursor, _("No results found matching your query"), _("Results"), FALSE);
816 			}
817 		}
818 	}
819 
820 	g_object_unref (connection);
821 
822 	return EXIT_SUCCESS;
823 }