static xmmsv_t *
convert_ghashtable_to_xmmsv (GHashTable *table, xmms_fetch_spec_t *spec)
{
	GHashTableIter iter;
	s4_resultset_t *value;
	const gchar *key;
	xmmsv_t *ret;

	g_hash_table_iter_init (&iter, table);

	ret = xmmsv_new_dict ();

	while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
		xmmsv_t *converted;

		if (value == NULL) {
			continue;
		}

		converted = xmms_medialib_query_to_xmmsv (value, spec);
		xmmsv_dict_set (ret, key, converted);
		xmmsv_unref (converted);
	}

	if (xmmsv_dict_get_size (ret) == 0) {
		xmmsv_unref (ret);
		ret = NULL;
	}

	return ret;
}
Beispiel #2
0
/**
 * Allocate a new collection of the given type.
 * The pointer will have to be deallocated using #xmmsv_coll_unref.
 *
 * @param type the #xmmsv_coll_type_t specifying the type of collection to create.
 * @return a pointer to the newly created collection, or NULL if the type is invalid.
 */
static xmmsv_coll_internal_t*
_xmmsv_coll_new (xmmsv_coll_type_t type)
{
	xmmsv_coll_internal_t *coll;

	x_return_val_if_fail (type <= XMMS_COLLECTION_TYPE_LAST, NULL);

	coll = x_new0 (xmmsv_coll_internal_t, 1);
	if (!coll) {
		x_oom ();
		return NULL;
	}

	coll->type = type;

	coll->idlist = xmmsv_new_list ();
	xmmsv_list_restrict_type (coll->idlist, XMMSV_TYPE_INT64);

	coll->operands = xmmsv_new_list ();
	xmmsv_list_restrict_type (coll->operands, XMMSV_TYPE_COLL);

	coll->attributes = xmmsv_new_dict ();

	return coll;
}
Beispiel #3
0
/**
 * Create a new argument for a method.
 *
 * @param name The name of the argument. Must not be NULL.
 * @param docstring The docstring of the argument.
 * @param type The expected type of the argument. Use XMMSV_TYPE_NONE to
 * accept any type. XMMSV_TYPE_ERROR is reserved and should not be used.
 * @param default_value Value to set this argument to if it's missing in the
 * method call. Implies that the argument is optional. If NULL, the argument
 * is not optional.
 */
xmmsv_t *
xmmsv_sc_argument_new (const char *name, const char *docstring,
                       xmmsv_type_t type, xmmsv_t *default_value)
{
	xmmsv_t *arg;

	x_api_error_if (!name, "with NULL name.", NULL);
	x_api_error_if (type == XMMSV_TYPE_ERROR, "with ERROR type.", NULL);
	x_api_error_if (default_value && type != XMMSV_TYPE_NONE &&
	                xmmsv_get_type (default_value) != type,
	                "with wrong type for default value.", NULL);

	arg = xmmsv_new_dict ();
	if (!arg) {
		x_oom ();
		return NULL;
	}

	xmmsv_dict_set_string (arg, "name", name);
	xmmsv_dict_set_int (arg, "type", type);

	if (docstring) {
		xmmsv_dict_set_string (arg, "docstring", docstring);
	}

	if (default_value) {
		xmmsv_dict_set (arg, "default_value", default_value);
	}

	return arg;
}
Beispiel #4
0
static xmmsv_t *
xmmsv_coll_normalize_order_arguments (xmmsv_t *value)
{
	xmmsv_t *order;
	const char *key;

	if (value == NULL) {
		return NULL;
	}

	if (xmmsv_is_type (value, XMMSV_TYPE_DICT)) {
		return xmmsv_ref (value);
	}

	x_api_error_if (!xmmsv_get_string (value, &key),
	                "order entry must be string or dict", NULL);

	order = xmmsv_new_dict ();

	if (key[0] == '-') {
		xmmsv_dict_set_string (order, "direction", "DESC");
		key++;
	}

	if (strcmp (key, "random") == 0) {
		xmmsv_dict_set_string (order, "type", "random");
	} else if (strcmp (key, "id") == 0) {
		xmmsv_dict_set_string (order, "type", "id");
	} else {
		xmmsv_dict_set_string (order, "type", "value");
		xmmsv_dict_set_string (order, "field", key);
	}

	return order;
}
Beispiel #5
0
xmmsv_t *
xmmsv_build_dict_va (const char *firstkey, va_list ap)
{
	const char *key;
	xmmsv_t *val, *res;

	res = xmmsv_new_dict ();
	if (!res)
		return NULL;

	key = firstkey;
	do {
		val = va_arg (ap, xmmsv_t *);

		if (!xmmsv_dict_set (res, key, val)) {
			xmmsv_unref (res);
			res = NULL;
			break;
		}
		xmmsv_unref (val);
		key = va_arg (ap, const char *);
	} while (key);

	return res;
}
Beispiel #6
0
/**
 * Creates a count fetch specification
 *
 * @return A new count fetch specification
 */
xmmsv_t *xmmsv_build_count ()
{
	xmmsv_t *res = xmmsv_new_dict ();

	xmmsv_dict_set_string (res, "type", "count");
	return res;
}
Beispiel #7
0
/**
 * Creates a cluster-dict fetch specification.
 *
 * @param cluster_by A list of attributes to cluster by
 * @param cluster_data The fetch specifcation to use when filling the list
 * @return A cluster-list fetch specification
 */
xmmsv_t *xmmsv_build_cluster_dict (xmmsv_t *cluster_by, xmmsv_t *cluster_field, xmmsv_t *cluster_data)
{
	xmmsv_t *res = xmmsv_new_dict ();
	if (res == NULL)
		return NULL;

	xmmsv_dict_set_string (res, "type", "cluster-dict");

	if (cluster_by != NULL) {
		xmmsv_dict_set (res, "cluster-by", cluster_by);
		xmmsv_unref (cluster_by);
	}


	if (cluster_field != NULL) {
		xmmsv_dict_set (res, "cluster-field", cluster_field);
		xmmsv_unref (cluster_field);
	}

	if (cluster_data != NULL) {
		xmmsv_dict_set (res, "data", cluster_data);
		xmmsv_unref (cluster_data);
	}

	return res;
}
/* Applies an aggregation function to the leafs in an xmmsv dict tree */
static xmmsv_t *
aggregate_result (xmmsv_t *val, gint depth, aggregate_function_t aggr_func)
{
	xmmsv_dict_iter_t *it;

	if (depth == 0) {
		return aggregate_data (val, aggr_func);
	}

	if (val == NULL && depth > 0) {
		return xmmsv_new_dict();
	}

	/* If it's a dict we call this function recursively on all its values */
	xmmsv_get_dict_iter (val, &it);

	while (xmmsv_dict_iter_valid (it)) {
		xmmsv_t *entry;

		xmmsv_dict_iter_pair (it, NULL, &entry);
		xmmsv_ref (entry);

		entry = aggregate_result (entry, depth - 1, aggr_func);
		xmmsv_dict_iter_set (it, entry);
		xmmsv_unref (entry);

		xmmsv_dict_iter_next (it);
	}

	return val;
}
Beispiel #9
0
static xmmsv_t *
create_structure (int stack_offset, int is_object)
{
	if (is_object) {
		return xmmsv_new_dict ();
	} else {
		return xmmsv_new_list ();
	}
}
Beispiel #10
0
/**
 * Create a new #xmmsv_t dict initialized with the argument.
 * @param dict The dict of values to initially fill the #xmmsv_t with.
 * @return a new #xmmsv_t dict.
 */
static xmmsv_t *
xmms_create_xmmsv_dict (GTree *dict)
{
	xmmsv_t *v = NULL;
	if (dict) {
		v = xmmsv_new_dict ();
		g_tree_foreach (dict, create_xmmsv_dict_foreach, (gpointer) v);
	}
	return v;
}
/* Converts an S4 resultset into an xmmsv_t, based on the fetch specification */
xmmsv_t *
xmms_medialib_query_to_xmmsv (s4_resultset_t *set, xmms_fetch_spec_t *spec)
{
	GHashTable *set_table;
	GList *sets;
	xmmsv_t *val, *ret = NULL;
	gint i;

	switch (spec->type) {
		case FETCH_COUNT:
			ret = xmmsv_new_int (s4_resultset_get_rowcount (set));
			break;
		case FETCH_METADATA:
			ret = metadata_to_xmmsv (set, spec);
			break;
		case FETCH_ORGANIZE:
			ret = xmmsv_new_dict ();

			for (i = 0; i < spec->data.organize.count; i++) {
				val = xmms_medialib_query_to_xmmsv (set, spec->data.organize.data[i]);
				if (val != NULL) {
					xmmsv_dict_set (ret, spec->data.organize.keys[i], val);
					xmmsv_unref (val);
				}
			}
			break;
		case FETCH_CLUSTER_LIST:
			sets = cluster_list (set, spec);
			ret = xmmsv_new_list ();
			for (; sets != NULL; sets = g_list_delete_link (sets, sets)) {
				set = sets->data;

				val = xmms_medialib_query_to_xmmsv (set, spec->data.cluster.data);
				if (val != NULL) {
					xmmsv_list_append (ret, val);
					xmmsv_unref (val);
				}
				s4_resultset_free (set);
			}
			break;
		case FETCH_CLUSTER_DICT:
			set_table = cluster_dict (set, spec);
			ret = convert_ghashtable_to_xmmsv (set_table, spec->data.cluster.data);

			g_hash_table_destroy (set_table);
			break;
		default:
			g_assert_not_reached ();
	}

	return ret;
}
Beispiel #12
0
static xmmsv_t *
xmms_volume_map_to_dict (xmms_volume_map_t *vl)
{
	xmmsv_t *ret;
	gint i;

	ret = xmmsv_new_dict ();

	for (i = 0; i < vl->num_channels; i++) {
		xmmsv_dict_set_int (ret, vl->names[i], vl->values[i]);
	}

	return ret;
}
Beispiel #13
0
static xmmsv_t *
xmms_medialib_entry_to_tree (xmms_medialib_session_t *session,
                             xmms_medialib_entry_t entry)
{
	s4_resultset_t *set;
	s4_val_t *song_id;
	xmmsv_t *ret, *id;
	gint i;

	song_id = s4_val_new_int (entry);
	set = xmms_medialib_filter (session, "song_id", song_id, S4_COND_PARENT,
	                            NULL, NULL, S4_FETCH_PARENT | S4_FETCH_DATA);
	s4_val_free (song_id);

	ret = xmmsv_new_dict ();

	for (i = 0; i < s4_resultset_get_rowcount (set); i++) {
		const s4_result_t *res;

		res = s4_resultset_get_result (set, 0, 0);
		while (res != NULL) {
			xmmsv_t *v_entry = NULL;
			const s4_val_t *val;
			const char *s;
			gint32 i;

			val = s4_result_get_val (res);
			if (s4_val_get_str (val, &s)) {
				v_entry = xmmsv_new_string (s);
			} else if (s4_val_get_int (val, &i)) {
				v_entry = xmmsv_new_int (i);
			}

			xmms_medialib_tree_add_tuple (ret, s4_result_get_key (res),
			                              s4_result_get_src (res), v_entry);
			xmmsv_unref (v_entry);

			res = s4_result_next (res);
		}
	}

	s4_resultset_free (set);

	id = xmmsv_new_int (entry);
	xmms_medialib_tree_add_tuple (ret, "id", "server", id);
	xmmsv_unref (id);

	return ret;
}
Beispiel #14
0
/**
 * Creates an organize fetch specification that may be passed to xmmsc_coll_query.
 * It takes a dict with key-value pairs where the values are fetch specifications.
 *
 * @return An organize fetch specification
 */
xmmsv_t *
xmmsv_build_organize (xmmsv_t *data)
{
	xmmsv_t *res;

	x_return_val_if_fail (data != NULL, NULL);
	res = xmmsv_new_dict ();

	if (res != NULL) {
		xmmsv_dict_set_string (res, "type", "organize");
		xmmsv_dict_set (res, "data", data);
		xmmsv_unref (data);
	}

	return res;
}
status_entry_t *
currently_playing_init (cli_context_t *ctx, const gchar *format, gint refresh)
{
	currently_playing_t *entry;

	entry = g_new0 (currently_playing_t, 1);
	entry->data = xmmsv_new_dict ();
	entry->format = g_strdup (format);
	entry->ctx = ctx;

	return status_init (currently_playing_free,
	                    currently_playing_refresh,
	                    NULL,
	                    currently_playing_keymap,
	                    entry, refresh);
}
Beispiel #16
0
void
xmms_xform_browse_add_entry (xmms_xform_t *xform, const gchar *filename,
                             guint32 flags)
{
	const gchar *url;
	gchar *efile, *eurl, *t;
	gint l, isdir;

	g_return_if_fail (filename);

	t = strchr (filename, '/');
	g_return_if_fail (!t); /* filenames can't contain '/', can they? */

	url = xmms_xform_get_url (xform);
	g_return_if_fail (url);

	xform->browse_dict = xmmsv_new_dict ();

	eurl = xmms_medialib_url_encode (url);
	efile = xmms_medialib_url_encode (filename);

	/* can't use g_build_filename as we need to preserve
	   slashes stuff like file:/// */
	l = strlen (url);
	if (l && url[l - 1] == '/') {
		t = g_strdup_printf ("%s%s", eurl, efile);
	} else {
		t = g_strdup_printf ("%s/%s", eurl, efile);
	}

	isdir = !!(flags & XMMS_XFORM_BROWSE_FLAG_DIR);
	xmms_xform_browse_add_entry_property_str (xform, "path", t);
	xmms_xform_browse_add_entry_property_int (xform, "isdir", isdir);

	if (xform->browse_list == NULL) {
		xform->browse_list = xmmsv_new_list ();
	}
	xmmsv_list_append (xform->browse_list, xform->browse_dict);
	xmmsv_unref (xform->browse_dict);

	g_free (t);
	g_free (efile);
	g_free (eurl);
}
Beispiel #17
0
/**
 * Helper function to transform a key-source-value dict-of-dict
 * #xmmsv_t (formerly a propdict) to a regular key-value dict, given a
 * list of source preference.
 *
 * @param propdict A key-source-value dict-of-dict #xmmsv_t.
 * @param src_prefs A list of source names or patterns. Must be
 *                  NULL-terminated. If this argument is NULL, the
 *                  default source preferences is used.
 * @return An #xmmsv_t containing a simple key-value dict. Must be
 *         unreffed manually when done.
 */
xmmsv_t *
xmmsv_propdict_to_dict (xmmsv_t *propdict, const char **src_prefs)
{
	xmmsv_t *dict, *source_dict, *value, *best_value;
	xmmsv_dict_iter_t *key_it, *source_it;
	const char *key, *source;
	const char **local_prefs;
	int match_index, best_index;

	dict = xmmsv_new_dict ();

	local_prefs = src_prefs ? src_prefs : xmmsv_default_source_pref;

	xmmsv_get_dict_iter (propdict, &key_it);
	while (xmmsv_dict_iter_valid (key_it)) {
		xmmsv_dict_iter_pair (key_it, &key, &source_dict);

		best_value = NULL;
		best_index = -1;
		xmmsv_get_dict_iter (source_dict, &source_it);
		while (xmmsv_dict_iter_valid (source_it)) {
			xmmsv_dict_iter_pair (source_it, &source, &value);
			match_index = find_match_index (source, local_prefs);
			/* keep first match or better match */
			if (match_index >= 0 && (best_index < 0 ||
			                         match_index < best_index)) {
				best_value = value;
				best_index = match_index;
			}
			xmmsv_dict_iter_next (source_it);
		}

		/* Note: we do not insert a key-value pair if no source matches */
		if (best_value) {
			xmmsv_dict_set (dict, key, best_value);
		}

		xmmsv_dict_iter_next (key_it);
	}

	return dict;
}
Beispiel #18
0
static void
xmms_medialib_tree_add_tuple (xmmsv_t *dict, const char *key,
                              const char *source, xmmsv_t *value)
{
	xmmsv_t *entry;

	if (key == NULL || source == NULL || value == NULL) {
		return;
	}

	/* Find (or insert) subtree matching the prop key */
	if (!xmmsv_dict_get (dict, key, &entry)) {
		entry = xmmsv_new_dict ();
		xmmsv_dict_set (dict, key, entry);
		xmmsv_unref (entry);
	}

	/* Replace (or insert) value matching the prop source */
	xmmsv_dict_set (entry, source, value);
}
Beispiel #19
0
static int
xmmsc_deserialize_dict (xmmsv_t *bb, xmmsv_t **val)
{
	xmmsv_t *dict;
	int32_t len;
	unsigned int ignore;
	char *key;

	dict = xmmsv_new_dict ();

	if (!_internal_get_from_bb_int32_positive (bb, &len)) {
		goto err;
	}

	while (len--) {
		xmmsv_t *v;

		if (!_internal_get_from_bb_string_alloc (bb, &key, &ignore)) {
			goto err;
		}

		if (!xmmsv_bitbuffer_deserialize_value (bb, &v)) {
			free (key);
			goto err;
		}

		xmmsv_dict_set (dict, key, v);
		free (key);
		xmmsv_unref (v);
	}

	*val = dict;

	return true;

err:
	x_internal_error ("Message from server did not parse correctly!");
	xmmsv_unref (dict);
	return false;
}
Beispiel #20
0
xmmsv_t *
duplicate_dict_value (xmmsv_t *val)
{
	xmmsv_t *dup_val;
	xmmsv_dict_iter_t *it;
	const char *key;
	xmmsv_t *v;
	xmmsv_t *new_elem;

	x_return_val_if_fail (xmmsv_get_dict_iter (val, &it), NULL);
	dup_val = xmmsv_new_dict ();
	while (xmmsv_dict_iter_pair (it, &key, &v)) {
		new_elem = xmmsv_copy (v);
		xmmsv_dict_set (dup_val, key, new_elem);
		xmmsv_unref (new_elem);
		xmmsv_dict_iter_next (it);
	}

	xmmsv_dict_iter_explicit_destroy (it);

	return dup_val;
}
Beispiel #21
0
static xmmsv_t *
args_to_dict (gchar **argv, gint argc)
{
	xmmsv_t *dict;
	gint i;

	dict = xmmsv_new_dict ();
	if (!dict) {
		return NULL;
	}

	for (i = 0; i < argc; i++) {
		gchar **tuple;
		xmmsv_t *val;

		/* Split the string into arg and value. */
		tuple = g_strsplit (argv[i], "=", 2);
		if (tuple[0] && tuple[1]) {
			val = xmmsv_new_string (tuple[1]);
		} else if (tuple[0]) {
			val = xmmsv_new_none ();
		} else {
			g_strfreev (tuple);
			continue; /* Empty tuple, means empty string. */
		}
		if (!val) {
			xmmsv_unref (dict);
			return NULL;
		}

		xmmsv_dict_set (dict, tuple[0], val);

		g_strfreev (tuple);
		xmmsv_unref (val);
	}

	return dict;
}
Beispiel #22
0
/**
 * Creates a metadata fetch specification.
 *
 * @param fields A list of fields to fetch, or NULL to fetch everything
 * @param get A list of what to get ("id", "key", "value", "source")
 * @param aggregate The aggregation function to use
 * @param sourcepref A list of sources, first one has the highest priority
 * @return A metadata fetch specification
 */
xmmsv_t *xmmsv_build_metadata (xmmsv_t *fields, xmmsv_t *get, const char *aggregate, xmmsv_t *sourcepref)
{
	xmmsv_t *res = xmmsv_new_dict ();
	if (res == NULL)
		return NULL;

	xmmsv_dict_set_string (res, "type", "metadata");

	if (fields != NULL) {
		if (xmmsv_get_type (fields) == XMMSV_TYPE_STRING) {
			xmmsv_t *list = xmmsv_new_list ();
			xmmsv_list_append (list, fields);
			xmmsv_unref (fields);
			fields = list;
		}
		xmmsv_dict_set (res, "fields", fields);
		xmmsv_unref (fields);
	}
	if (get != NULL) {
		if (xmmsv_get_type (get) == XMMSV_TYPE_STRING) {
			xmmsv_t *list = xmmsv_new_list ();
			xmmsv_list_append (list, get);
			xmmsv_unref (get);
			get = list;
		}
		xmmsv_dict_set (res, "get", get);
		xmmsv_unref (get);
	}
	if (sourcepref != NULL) {
		xmmsv_dict_set (res, "source-preference", sourcepref);
		xmmsv_unref (sourcepref);
	}
	if (aggregate != NULL) {
		xmmsv_dict_set_string (res, "aggregate", aggregate);
	}

	return res;
}
Beispiel #23
0
/** Sorts the playlist by properties.
 *
 *  This will sort the list.
 *  @param playlist The playlist to sort.
 *  @param properties Tells xmms_playlist_sort which properties it
 *  should use when sorting.
 *  @param err An #xmms_error_t - needed since xmms_playlist_sort is an ipc
 *  method handler.
 */
static void
xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname,
                           xmmsv_t *properties, xmms_error_t *err)
{

	xmmsv_t *tmp, *idlist, *val, *spec, *metadata, *get;
	xmmsv_coll_t *plcoll, *ordered;
	gint currpos, pos;
	xmms_medialib_entry_t currid;

	g_return_if_fail (playlist);
	g_return_if_fail (properties);

	if (xmmsv_list_get_size (properties) < 1) {
		xmms_error_set (err, XMMS_ERROR_NOENT,
		                "need at least one property to sort");
		return;
	}

	g_mutex_lock (playlist->mutex);

	plcoll = xmms_playlist_get_coll (playlist, plname, err);
	if (plcoll == NULL) {
		xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist!");
		g_mutex_unlock (playlist->mutex);
		return;
	}

	currpos = xmms_playlist_coll_get_currpos (plcoll);
	xmmsv_coll_idlist_get_index (plcoll, currpos, &currid);

	get = xmmsv_new_list ();
	xmmsv_list_append_string (get, "id");

	metadata = xmmsv_new_dict ();
	xmmsv_dict_set_string (metadata, "type", "metadata");
	xmmsv_dict_set_string (metadata, "aggregate", "first");
	xmmsv_dict_set (metadata, "get", get);
	xmmsv_unref (get);

	spec = xmmsv_new_dict ();
	xmmsv_dict_set_string (spec, "type", "cluster-list");
	xmmsv_dict_set_string (spec, "cluster-by", "position");
	xmmsv_dict_set (spec, "data", metadata);
	xmmsv_unref (metadata);

	ordered = xmmsv_coll_add_order_operators (plcoll, properties);

	MEDIALIB_BEGIN (playlist->medialib);
	tmp = xmms_medialib_query (session, ordered, spec, err);
	MEDIALIB_COMMIT ();

	xmmsv_coll_unref (ordered);
	xmmsv_unref (spec);

	if (tmp == NULL) {
		g_mutex_unlock (playlist->mutex);
		return;
	}

	idlist = xmmsv_coll_idlist_get (plcoll);
	xmmsv_list_clear (idlist);

	for (pos = 0; xmmsv_list_get (tmp, pos, &val); pos++) {
		xmms_medialib_entry_t id;

		xmmsv_get_int (val, &id);
		xmmsv_list_append (idlist, val);

		if (id == currid) {
			xmms_collection_set_int_attr (plcoll, "position", pos);
			currpos = pos;
		}
	}

	xmmsv_unref (tmp);

	XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SORT, 0, plname);
	XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname);

	g_mutex_unlock (playlist->mutex);
}
Beispiel #24
0
Dict::Dict() : value_( xmmsv_new_dict() )
{
}
/* Converts an S4 result (a column) into an xmmsv values */
static void *
result_to_xmmsv (xmmsv_t *ret, gint32 id, const s4_result_t *res,
                 xmms_fetch_spec_t *spec)
{
	static xmmsv_t * (*aggregate_functions[AGGREGATE_END])(xmmsv_t *c, gint i, const gchar *s) = {
		aggregate_first,
		aggregate_sum,
		aggregate_max,
		aggregate_min,
		aggregate_set,
		aggregate_list,
		aggregate_random,
		aggregate_average
	};
	const s4_val_t *val;
	xmmsv_t *dict, *current;
	const gchar *str_value, *key = NULL;
	gint32 i, int_value;
	xmmsv_t *newval;

	g_return_val_if_fail (spec->data.metadata.get_size > 0, ret);
	g_return_val_if_fail (spec->data.metadata.get_size <= METADATA_END, ret);
	g_return_val_if_fail (spec->data.metadata.aggr_func >= 0, ret);
	g_return_val_if_fail (spec->data.metadata.aggr_func < AGGREGATE_END, ret);

	/* Loop through all the values the column has */
	while (res != NULL) {
		dict = ret;
		current = ret;

		/* Loop through the list of what to get ("key", "source", ..) */
		for (i = 0; i < spec->data.metadata.get_size; i++) {
			str_value = NULL;
			int_value = 0;

			/* Fill str_value with the correct value if it is a string
			 * or int_value if it is an integer
			 */
			switch (spec->data.metadata.get[i]) {
				case METADATA_KEY:
					str_value = s4_result_get_key (res);
					break;
				case METADATA_SOURCE:
					str_value = s4_result_get_src (res);
					if (str_value == NULL)
						str_value = "server";
					break;
				case METADATA_ID:
					int_value = id;
					break;
				case METADATA_VALUE:
					val = s4_result_get_val (res);

					if (!s4_val_get_int (val, &int_value)) {
						s4_val_get_str (val, &str_value);
					}
					break;
				default:
					g_assert_not_reached ();
			}

			/* If this is not the last property to get we use this property
			 * as a key in a dict
			 */
			if (i < (spec->data.metadata.get_size - 1)) {
				/* Convert integers to strings */
				if (str_value == NULL) {
					/* Big enough to hold 2^32 with minus sign */
					gchar buf[12];
					g_sprintf (buf, "%i", int_value);
					key = buf;
				} else {
					key = str_value;
				}

				/* Make sure the root dict exists */
				if (dict == NULL) {
					ret = dict = xmmsv_new_dict ();
				}

				/* If this dict contains dicts we have to create a new
				 * dict if one does not exists for the key yet
				 */
				if (!xmmsv_dict_get (dict, key, &current))
					current = NULL;

				if (i < (spec->data.metadata.get_size - 2)) {
					if (current == NULL) {
						current = xmmsv_new_dict ();
						xmmsv_dict_set (dict, key, current);
						xmmsv_unref (current);
					}
					dict = current;
				}
			}
		}

		newval = aggregate_functions[spec->data.metadata.aggr_func](current, int_value, str_value);

		/* Update the previous dict (if there is one) */
		if (newval != current) {
			if (i > 1) {
				xmmsv_dict_set (dict, key, newval);
				xmmsv_unref (newval);
			} else {
				ret = newval;
				if (current != NULL) {
					xmmsv_unref (current);
				}
			}
		}

		res = s4_result_next (res);
	}

	return ret;
}