[tracker/dbus-fd-experiment: 2/3] libtracker-client: Add Steroids support
- From: Adrien Bustany <abustany src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/dbus-fd-experiment: 2/3] libtracker-client: Add Steroids support
- Date: Mon, 24 May 2010 17:48:22 +0000 (UTC)
commit 90c46871714db7fd97635492274464c05d9536b7
Author: Adrien Bustany <abustany gnome org>
Date: Mon May 24 08:52:02 2010 -0400
libtracker-client: Add Steroids support
configure.ac | 16 ++
src/libtracker-client/tracker.c | 431 +++++++++++++++++++++++++++++++++++++++
src/libtracker-client/tracker.h | 12 +
3 files changed, 459 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7f506a9..16d8065 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1679,6 +1679,21 @@ fi
AM_CONDITIONAL(HAVE_TOTEM_PL_PARSER, test "x$have_playlist" = "xyes")
+####################################################################
+# Check for DBus > 1.3 for Steroids (query over pipe) support
+####################################################################
+
+PKG_CHECK_MODULES(DBUS_1_3,
+ [dbus-1 >= 1.3],
+ [have_dbus_1_3=yes],
+ [have_dbus_1_3=no])
+
+if test "x$have_dbus_1_3" = "xyes"; then
+ AC_DEFINE(HAVE_DBUS_1_3, [], [Define if we have DBus >= 1.3])
+fi
+
+AM_CONDITIONAL(HAVE_DBUS_1_3, test "x$have_dbus_1_3" = "xyes")
+
##################################################################
# Enable mockup extractor module?
##################################################################
@@ -1958,6 +1973,7 @@ Build Configuration:
Support for Cyrillic languages (enca): $have_enca
Support for network status detection: $have_network_manager
Unicode support library: $with_unicode_support
+ Support for DBus FD passing: $have_dbus_1_3
Applications:
diff --git a/src/libtracker-client/tracker.c b/src/libtracker-client/tracker.c
index 455fb86..291a8a1 100644
--- a/src/libtracker-client/tracker.c
+++ b/src/libtracker-client/tracker.c
@@ -27,6 +27,10 @@
#include <dbus/dbus-glib-bindings.h>
#include <libtracker-common/tracker-dbus.h>
+#include <tracker-store/tracker-steroids.h>
+
+#include <sys/types.h>
+#include <unistd.h>
#include "tracker.h"
@@ -92,6 +96,7 @@
**/
typedef struct {
+ DBusGConnection *connection;
DBusGProxy *proxy_statistics;
DBusGProxy *proxy_resources;
@@ -141,6 +146,24 @@ typedef struct {
guint id;
} CallbackArray;
+struct TrackerResultIterator {
+#ifdef HAVE_DBUS_1_3
+ int fd;
+ DBusPendingCall *call;
+ int rc;
+ char buffer[TRACKER_STEROIDS_BUFFER_SIZE];
+ int buffer_index;
+ char *large_row_buf;
+
+ guint n_columns;
+ int *offsets;
+ char *data;
+#else
+ GPtrArray *results;
+ gint current_row;
+#endif
+};
+
#endif /* TRACKER_DISABLE_DEPRECATED */
static gboolean is_service_available (void);
@@ -360,6 +383,8 @@ client_constructed (GObject *object)
return;
}
+ private->connection = connection;
+
private->proxy_statistics =
dbus_g_proxy_new_for_name (connection,
TRACKER_DBUS_SERVICE,
@@ -677,6 +702,35 @@ find_conversion (const char *format,
return start;
}
+#ifdef HAVE_DBUS_1_3
+static int
+iterator_buffer_read_int (TrackerResultIterator *iterator)
+{
+ int result = 0;
+ char *dst = iterator->buffer + iterator->buffer_index;
+
+ result += (((unsigned char)*(dst++)));
+ result += (((unsigned char)*(dst++)) << 8);
+ result += (((unsigned char)*(dst++)) << 16);
+ result += (((unsigned char)*(dst++)) << 24);
+ iterator->buffer_index += sizeof (int);
+
+ return result;
+}
+
+static void
+pipe_read (int fd, char *dst, int size)
+{
+ ssize_t readsofar = 0;
+
+ while (readsofar < size) {
+ readsofar += read (fd,
+ dst + readsofar,
+ size - readsofar);
+ }
+}
+#endif
+
/**
* tracker_uri_vprintf_escaped:
* @format: a standard printf() format string, but notice
@@ -1046,6 +1100,383 @@ tracker_resources_sparql_query (TrackerClient *client,
}
/**
+ * tracker_resources_sparql_query_iterate:
+ * @client: a #TrackerClient.
+ * @query: a string representing SPARQL.
+ * @error: a #GError.
+ *
+ * Queries the database using SPARQL, and returns an iterator instead of an
+ * array with all the results inside.
+ *
+ * Using an iterator will lower the memory usage. Additionally, this function
+ * uses a pipe when available get the results from Tracker store, which is
+ * roughly two times faster than using DBus.
+ *
+ * This API call is completely synchronous so it may block.
+ *
+ * <example>
+ * <title>Using tracker_resources_sparql_query_iterate(<!-- -->)</title>
+ * An example of using tracker_resources_sparql_query_iterate() to list all
+ * albums by title and include their song count and song total length.
+ * <programlisting>
+ * TrackerClient *client;
+ * TrackerResultIterator *iterator;
+ * GError *error = NULL;
+ * const gchar *query;
+ *
+ * /* Create D-Bus connection with no warnings and maximum timeout. */
+ * client = tracker_client_new (0, G_MAXINT);
+ * query = "SELECT {"
+ * " ?album"
+ * " ?title"
+ * " COUNT(?song) AS songs"
+ * " SUM(?length) AS totallength"
+ * "} WHERE {"
+ * " ?album a nmm:MusicAlbum ;"
+ * " nie:title ?title ."
+ * " ?song nmm:musicAlbum ?album ;"
+ * " nfo:duration ?length"
+ * "} "
+ * "GROUP BY (?album");
+ *
+ * iterator = tracker_resources_sparql_query_iterate (client, query, &error);
+ *
+ * if (error) {
+ * g_warning ("Could not query Tracker, %s", error->message);
+ * g_error_free (error);
+ * g_object_unref (client);
+ * return;
+ * }
+ *
+ * while (tracker_result_iterator_has_next (iterator)) {
+ * tracker_result_iterator_next (iterator);
+ *
+ * g_message ("Album: %s, Title: %s",
+ * tracker_result_iterator_value (iterator, 0),
+ * tracker_result_iterator_value (iterator, 1));
+ * }
+ *
+ * tracker_result_iterator_free (iterator);
+ *
+ * </programlisting>
+ * </example>
+ *
+ * Returns: A #TrackerResultIterator pointing before the first result row. This
+ * iterator must be disposed when done using tracker_result_iterator_free().
+ *
+ * Since: 0.9
+ **/
+TrackerResultIterator*
+tracker_resources_sparql_query_iterate (TrackerClient *client,
+ const gchar *query,
+ GError **error)
+{
+#ifdef HAVE_DBUS_1_3
+ TrackerClientPrivate *private;
+ DBusConnection *connection;
+ DBusMessage *message;
+ DBusMessage *reply;
+ guint query_id;
+ DBusError dbus_error;
+ TrackerResultIterator *iterator;
+
+ g_return_val_if_fail (TRACKER_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (query, NULL);
+
+ private = TRACKER_CLIENT_GET_PRIVATE (client);
+
+ connection = dbus_g_connection_get_connection (private->connection);
+
+ dbus_error_init (&dbus_error);
+
+ message = dbus_message_new_method_call (TRACKER_STEROIDS_SERVICE,
+ TRACKER_STEROIDS_PATH,
+ TRACKER_STEROIDS_INTERFACE,
+ "PrepareQuery");
+ dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &query,
+ DBUS_TYPE_INVALID);
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1,
+ &dbus_error);
+ dbus_message_unref (message);
+
+ if (!reply) {
+ dbus_set_g_error (error, &dbus_error);
+ return NULL;
+ }
+
+ iterator = g_slice_new0 (TrackerResultIterator);
+
+ dbus_message_get_args (reply,
+ &dbus_error,
+ DBUS_TYPE_UNIX_FD, &iterator->fd,
+ DBUS_TYPE_UINT32, &query_id,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref (reply);
+
+ message = dbus_message_new_method_call (TRACKER_STEROIDS_SERVICE,
+ TRACKER_STEROIDS_PATH,
+ TRACKER_STEROIDS_INTERFACE,
+ "Fetch");
+ dbus_message_append_args (message,
+ DBUS_TYPE_UINT32, &query_id,
+ DBUS_TYPE_INVALID);
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1,
+ &dbus_error);
+ dbus_message_unref (message);
+
+ if (!reply) {
+ dbus_set_g_error (error, &dbus_error);
+ tracker_result_iterator_free (iterator);
+ return NULL;
+ }
+
+ dbus_message_unref (reply);
+
+ /* Pre fetch the first buffer to initialize the iterator */
+ pipe_read (iterator->fd, iterator->buffer, TRACKER_STEROIDS_BUFFER_SIZE);
+
+ iterator->rc = iterator_buffer_read_int (iterator);
+
+ switch (iterator->rc) {
+ case TRACKER_STEROIDS_RC_ROW:
+ case TRACKER_STEROIDS_RC_LARGEROW:
+ iterator->n_columns = iterator_buffer_read_int (iterator);
+ break;
+ case TRACKER_STEROIDS_RC_DONE:
+ break;
+ case TRACKER_STEROIDS_RC_ERROR:
+ tracker_result_iterator_free (iterator);
+ iterator = NULL;
+ break;
+ }
+
+ /* Reset the iterator internal state */
+ iterator->buffer_index = 0;
+
+ return iterator;
+#else
+ TrackerResultIterator *iterator;
+ GError *inner_error = NULL;
+
+ g_return_val_if_fail (TRACKER_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (query, NULL);
+
+ iterator = g_slice_new0 (TrackerResultIterator);
+
+ iterator->results = tracker_resources_sparql_query (client, query, &inner_error);
+ iterator->current_row = -1;
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_slice_free (TrackerResultIterator, iterator);
+ iterator = NULL;
+ }
+
+ return iterator;
+#endif
+}
+
+/**
+ * tracker_result_iterator_free:
+ * @iterator: A TrackerResultIterator
+ *
+ * Frees a TrackerResultIterator and its associated resources
+ *
+ * Since: 0.9
+ **/
+void
+tracker_result_iterator_free (TrackerResultIterator *iterator)
+{
+#ifndef HAVE_DBUS_1_3
+ g_ptr_array_foreach (iterator->results, (GFunc) g_free, NULL);
+ g_ptr_array_free (iterator->results, TRUE);
+#endif
+ g_slice_free (TrackerResultIterator, iterator);
+}
+
+/**
+ * tracker_result_iterator_n_columns:
+ * @iterator: A TrackerResultIterator
+ *
+ * Returns: the number of columns in the row pointed by @iterator
+ *
+ * Since: 0.9
+ **/
+guint
+tracker_result_iterator_n_columns (TrackerResultIterator *iterator)
+{
+#ifdef HAVE_DBUS_1_3
+ g_return_val_if_fail (iterator, 0);
+
+ return iterator->n_columns;
+#else
+ GStrv row;
+ guint i = 0;
+
+ g_return_val_if_fail (iterator, 0);
+
+ if (!iterator->results->len) {
+ return 0;
+ }
+
+ row = g_ptr_array_index (iterator->results, 0);
+
+ while (row[i++]) {
+ }
+
+ return i - 1;
+#endif
+}
+
+/**
+ * tracker_result_iterator_has_next:
+ * @iterator: A TrackerResultIterator
+ *
+ * Checks if the iterator has more rows
+ *
+ * Returns: TRUE if there are more rows to fetch, FALSE else
+ *
+ * Since: 0.9
+ **/
+gboolean
+tracker_result_iterator_has_next (TrackerResultIterator *iterator)
+{
+#ifdef HAVE_DBUS_1_3
+ g_return_val_if_fail (iterator, FALSE);
+
+ return (iterator->rc == TRACKER_STEROIDS_RC_ROW || iterator->rc == TRACKER_STEROIDS_RC_LARGEROW);
+#else
+ g_return_val_if_fail (iterator, FALSE);
+
+ if (!iterator->results->len) {
+ return FALSE;
+ }
+
+ return (iterator->current_row < (gint)(iterator->results->len - 1));
+#endif
+}
+
+/**
+ * tracker_result_iterator_next:
+ * @iterator: A TrackerResultIterator
+ *
+ * Fetches the next results row.
+ *
+ * Since: 0.9
+ **/
+void
+tracker_result_iterator_next (TrackerResultIterator *iterator)
+{
+#ifdef HAVE_DBUS_1_3
+ int row_size;
+
+ g_return_if_fail (iterator);
+
+ if (iterator->large_row_buf) {
+ g_free (iterator->large_row_buf);
+ iterator->large_row_buf = NULL;
+ }
+
+ if ((unsigned char)(*(iterator->buffer + iterator->buffer_index)) == TRACKER_STEROIDS_EOP ||
+ iterator->buffer_index == TRACKER_STEROIDS_BUFFER_SIZE) {
+ pipe_read (iterator->fd, iterator->buffer, TRACKER_STEROIDS_BUFFER_SIZE);
+ iterator->buffer_index = 0;
+ }
+
+ iterator->rc = iterator_buffer_read_int (iterator);
+
+ switch (iterator->rc) {
+ case TRACKER_STEROIDS_RC_ROW:
+ iterator->n_columns = iterator_buffer_read_int (iterator);
+ iterator->offsets = (int *)(iterator->buffer + iterator->buffer_index);
+ iterator->buffer_index += iterator->n_columns * sizeof (int);
+ iterator->data = iterator->buffer + iterator->buffer_index;
+ iterator->buffer_index += iterator->offsets[iterator->n_columns - 1] + 1;
+ break;
+ case TRACKER_STEROIDS_RC_LARGEROW:
+ row_size = iterator_buffer_read_int (iterator);
+ iterator->large_row_buf = malloc (sizeof (char) * row_size);
+ memcpy (iterator->large_row_buf, iterator->buffer, TRACKER_STEROIDS_BUFFER_SIZE);
+ pipe_read (iterator->fd,
+ iterator->large_row_buf + TRACKER_STEROIDS_BUFFER_SIZE,
+ row_size - TRACKER_STEROIDS_BUFFER_SIZE);
+ /* The number of columns will always fit into the iterator read buffer,
+ * since it's an int */
+ iterator->n_columns = iterator_buffer_read_int (iterator);
+ /* For subsequent reads we need additional steps so that we can treat
+ * iterator->read_buffer and large_row_buf as a single large buffer */
+ iterator->offsets = (int *)(iterator->large_row_buf + 3 * sizeof (int));
+ iterator->data = (char *)iterator->offsets + sizeof (int) * iterator->n_columns;
+ iterator->buffer_index = TRACKER_STEROIDS_BUFFER_SIZE;
+ break;
+ case TRACKER_STEROIDS_RC_DONE:
+ break;
+ default:
+ /* If an error happened, it has been reported by
+ * tracker_resources_sparql_query_iterate */
+ break;
+ }
+#else
+ g_return_if_fail (iterator);
+
+ if (!iterator->results->len) {
+ return;
+ }
+
+ if (iterator->current_row < (gint)iterator->results->len) {
+ iterator->current_row++;
+ }
+#endif
+}
+
+/**
+ * tracker_result_iterator_value:
+ * @iterator: A TrackerResultIterator
+ *
+ * Get a column's value as a string
+ *
+ * Returns: the value of the column as a string. The returned string belongs to
+ * the iterator and should not be freed.
+ *
+ * Since: 0.9
+ **/
+const gchar *
+tracker_result_iterator_value (TrackerResultIterator *iterator,
+ guint column)
+{
+#ifdef HAVE_DBUS_1_3
+ g_return_val_if_fail (iterator, NULL);
+ g_return_val_if_fail (column < iterator->n_columns, NULL);
+
+ if (column == 0) {
+ return iterator->data;
+ } else {
+ return iterator->data + iterator->offsets[column-1] + 1;
+ }
+#else
+ GStrv row;
+
+ g_return_val_if_fail (iterator, NULL);
+ g_return_val_if_fail (column < tracker_result_iterator_n_columns (iterator), NULL);
+
+ if (!iterator->results->len) {
+ return NULL;
+ }
+
+ g_return_val_if_fail (iterator->current_row < (gint)iterator->results->len, NULL);
+
+ row = g_ptr_array_index (iterator->results, iterator->current_row);
+
+ return row[column];
+#endif
+}
+
+/**
* tracker_resources_sparql_update:
* @client: a #TrackerClient.
* @query: a string representing SPARQL.
diff --git a/src/libtracker-client/tracker.h b/src/libtracker-client/tracker.h
index 0069907..e049a75 100644
--- a/src/libtracker-client/tracker.h
+++ b/src/libtracker-client/tracker.h
@@ -49,6 +49,8 @@ typedef struct {
GObjectClass parent;
} TrackerClientClass;
+typedef struct TrackerResultIterator TrackerResultIterator;
+
/**
* TrackerClientFlags:
* @TRACKER_CLIENT_ENABLE_WARNINGS: If supplied warnings will be
@@ -122,6 +124,16 @@ void tracker_resources_load (TrackerClient
GPtrArray * tracker_resources_sparql_query (TrackerClient *client,
const gchar *query,
GError **error);
+TrackerResultIterator *
+ tracker_resources_sparql_query_iterate (TrackerClient *client,
+ const gchar *query,
+ GError **error);
+void tracker_result_iterator_free (TrackerResultIterator *iterator);
+guint tracker_result_iterator_n_columns (TrackerResultIterator *iterator);
+gboolean tracker_result_iterator_has_next (TrackerResultIterator *iterator);
+void tracker_result_iterator_next (TrackerResultIterator *iterator);
+const gchar * tracker_result_iterator_value (TrackerResultIterator *iterator,
+ guint column);
void tracker_resources_sparql_update (TrackerClient *client,
const gchar *query,
GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]