XMMS2
collquery.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2011 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 
18 /** @file
19  * Functions to build an SQL query from a collection.
20  */
21 
22 #include <string.h>
23 #include <glib.h>
24 
26 #include "xmms/xmms_log.h"
27 
28 
29 /* Query structures */
30 
31 typedef struct {
32  guint limit_start;
33  guint limit_len;
34  xmmsv_t *order;
35  xmmsv_t *fetch;
36  xmmsv_t *group;
37 } coll_query_params_t;
38 
39 typedef enum {
43 
44 typedef struct {
46  guint id;
47  gboolean optional;
48 } coll_query_alias_t;
49 
50 typedef struct {
51  GHashTable *aliases;
52  guint alias_count;
53  gchar *alias_base;
54  GString *conditions;
55  coll_query_params_t *params;
56 } coll_query_t;
57 
58 typedef enum {
63 
64 static coll_query_t* init_query (coll_query_params_t *params);
65 static void add_fetch_group_aliases (coll_query_t *query, coll_query_params_t *params);
66 static void destroy_query (coll_query_t* query);
67 static GString* xmms_collection_gen_query (coll_query_t *query);
68 static void xmms_collection_append_to_query (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, coll_query_t *query);
69 
70 static void query_append_int (coll_query_t *query, gint i);
71 static void query_append_string (coll_query_t *query, const gchar *s);
72 static void query_append_protect_string (coll_query_t *query, gchar *s);
73 static void query_append_operand (coll_query_t *query, xmms_coll_dag_t *dag, xmmsv_coll_t *coll);
74 static void query_append_intersect_operand (coll_query_t *query, xmms_coll_dag_t *dag, xmmsv_coll_t *coll);
75 static void query_append_filter (coll_query_t *query, xmmsv_coll_type_t type, gchar *key, gchar *value, gboolean case_sens);
76 static void query_string_append_joins (gpointer key, gpointer val, gpointer udata);
77 static void query_string_append_alias_list (coll_query_t *query, GString *qstring, xmmsv_t *fields);
78 static void query_string_append_fetch (coll_query_t *query, GString *qstring);
79 static void query_string_append_alias (GString *qstring, coll_query_alias_t *alias, coll_query_value_type_t type);
80 
81 static const gchar *canonical_field_name (const gchar *field);
82 static gboolean operator_is_allmedia (xmmsv_coll_t *op);
83 static coll_query_alias_t *query_make_alias (coll_query_t *query, const gchar *field, gboolean optional);
84 static coll_query_alias_t *query_get_alias (coll_query_t *query, const gchar *field);
85 
86 
87 
88 /** @defgroup CollectionQuery CollectionQuery
89  * @ingroup XMMSServer
90  * @brief This module generates queries from collections.
91  *
92  * @{
93  */
94 
95 /* Generate a query string from a collection and query parameters. */
96 GString*
98  guint limit_start, guint limit_len,
99  xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group)
100 {
101  GString *qstring;
102  coll_query_t *query;
103  coll_query_params_t params = { limit_start, limit_len, order, fetch, group };
104 
105  query = init_query (&params);
106  xmms_collection_append_to_query (dag, coll, query);
107  add_fetch_group_aliases (query, &params);
108 
109  qstring = xmms_collection_gen_query (query);
110 
111  destroy_query (query);
112 
113  return qstring;
114 }
115 
116 
117 /* Initialize a query structure */
118 static coll_query_t*
119 init_query (coll_query_params_t *params)
120 {
121  coll_query_t *query;
122 
123  query = g_new (coll_query_t, 1);
124  if (query == NULL) {
125  return NULL;
126  }
127 
128  query->aliases = g_hash_table_new_full (g_str_hash, g_str_equal,
129  g_free, g_free);
130 
131  query->alias_count = 1;
132  query->alias_base = NULL;
133  query->conditions = g_string_new (NULL);
134  query->params = params;
135 
136  return query;
137 }
138 
139 static void
140 append_each_alias (xmmsv_t *value, void *udata)
141 {
142  const gchar *name;
143  coll_query_t *query = (coll_query_t *) udata;
144  xmmsv_get_string (value, &name);
145  query_make_alias (query, name, TRUE);
146 }
147 
148 static void
149 add_fetch_group_aliases (coll_query_t *query, coll_query_params_t *params)
150 {
151  /* Prepare aliases for the group/fetch fields */
152  xmmsv_list_foreach (query->params->group, append_each_alias, query);
153  xmmsv_list_foreach (query->params->fetch, append_each_alias, query);
154 }
155 
156 /* Free a coll_query_t object */
157 static void
158 destroy_query (coll_query_t* query)
159 {
160  g_hash_table_destroy (query->aliases);
161  g_string_free (query->conditions, TRUE);
162  g_free (query);
163 }
164 
165 
166 /* Generate a query string from a query structure. */
167 static GString*
168 xmms_collection_gen_query (coll_query_t *query)
169 {
170  GString *qstring;
171 
172  /* If no alias base yet (m0), select the default base property */
173  if (query->alias_base == NULL) {
174  query_make_alias (query, XMMS_COLLQUERY_DEFAULT_BASE, FALSE);
175  } else {
176  /* We are actually interested in the property of m0...
177  Let's make sure it comes from a good source. */
178  if (query->conditions->len > 0) {
179  g_string_append (query->conditions, " AND ");
180  }
181  g_string_append_printf (query->conditions,
182  "xmms_source_pref (m0.source) = "
183  "(SELECT MIN (xmms_source_pref (n.source)) FROM Media AS n "
184  "WHERE n.id = m0.id AND n.key = '%s')",
185  query->alias_base);
186  }
187 
188  /* Append select and joins */
189  qstring = g_string_new ("SELECT DISTINCT ");
190  query_string_append_fetch (query, qstring);
191  g_string_append (qstring, " FROM Media AS m0");
192  g_hash_table_foreach (query->aliases, query_string_append_joins, qstring);
193 
194  /* Append conditions */
195  g_string_append_printf (qstring, " WHERE m0.key='%s'", query->alias_base);
196  if (query->conditions->len > 0) {
197  g_string_append_printf (qstring, " AND %s", query->conditions->str);
198  }
199 
200  /* Append grouping */
201  if (xmmsv_list_get_size (query->params->group) > 0) {
202  g_string_append (qstring, " GROUP BY ");
203  query_string_append_alias_list (query, qstring, query->params->group);
204  }
205 
206  /* Append ordering */
207  /* FIXME: Ordering is Teh Broken (source?) */
208  if (xmmsv_list_get_size (query->params->order) > 0) {
209  g_string_append (qstring, " ORDER BY ");
210  query_string_append_alias_list (query, qstring, query->params->order);
211  }
212 
213  /* Append limit */
214  if (query->params->limit_len != 0) {
215  if (query->params->limit_start ) {
216  g_string_append_printf (qstring, " LIMIT %u,%u",
217  query->params->limit_start,
218  query->params->limit_len);
219  } else {
220  g_string_append_printf (qstring, " LIMIT %u",
221  query->params->limit_len);
222  }
223  }
224 
225  return qstring;
226 }
227 
228 /* Recursively append conditions corresponding to the given collection to the query. */
229 static void
230 xmms_collection_append_to_query (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
231  coll_query_t *query)
232 {
233  xmmsv_coll_t *op;
234  xmms_medialib_entry_t entry;
235  gchar *attr1, *attr2, *attr3;
236  gboolean case_sens;
237  xmmsv_list_iter_t *iter;
238  xmmsv_t *tmp;
239  gboolean first;
240 
242  switch (type) {
244  if (!operator_is_allmedia (coll)) {
245  query_append_operand (query, dag, coll);
246  } else {
247  /* FIXME: Hackish solution to append a ref to All Media */
248  query_append_string (query, "1");
249  }
250  break;
251 
254  first = TRUE;
255  query_append_string (query, "(");
256 
258 
259  for (xmmsv_list_iter_first (iter);
260  xmmsv_list_iter_valid (iter);
261  xmmsv_list_iter_next (iter)) {
262  if (first) {
263  first = FALSE;
264  } else {
265  if (type == XMMS_COLLECTION_TYPE_UNION)
266  query_append_string (query, " OR ");
267  else
268  query_append_string (query, " AND ");
269  }
270  xmmsv_list_iter_entry (iter, &tmp);
271  xmmsv_get_coll (tmp, &op);
272  xmms_collection_append_to_query (dag, op, query);
273  }
275 
276  query_append_string (query, ")");
277  break;
278 
280  query_append_string (query, "NOT ");
281  query_append_operand (query, dag, coll);
282  break;
283 
289  xmmsv_coll_attribute_get (coll, "field", &attr1);
290  xmmsv_coll_attribute_get (coll, "value", &attr2);
291  xmmsv_coll_attribute_get (coll, "case-sensitive", &attr3);
292  case_sens = (attr3 != NULL && strcmp (attr3, "true") == 0);
293 
294  query_append_string (query, "(");
295  query_append_filter (query, type, attr1, attr2, case_sens);
296 
297  query_append_intersect_operand (query, dag, coll);
298  query_append_string (query, ")");
299  break;
300 
304  first = TRUE;
305  query_append_string (query, "m0.id IN (");
306 
308  for (xmmsv_list_iter_first (iter);
309  xmmsv_list_iter_valid (iter);
310  xmmsv_list_iter_next (iter)) {
311 
312  if (first) {
313  first = FALSE;
314  } else {
315  query_append_string (query, ",");
316  }
317 
318  xmmsv_list_iter_entry_int (iter, &entry);
319  query_append_int (query, entry);
320  }
322 
323  query_append_string (query, ")");
324  break;
325 
326  /* invalid type */
327  default:
328  XMMS_DBG ("Cannot append invalid collection operator!");
329  g_assert_not_reached ();
330  break;
331  }
332 
333 }
334 
335 
336 /** Register a (unique) field alias in the query structure and return
337  * the corresponding alias pointer.
338  *
339  * @param query The query object to insert the alias in.
340  * @param field The name of the property that will correspond to the alias.
341  * @param optional Whether the property can be optional (i.e. LEFT JOIN)
342  * @return The alias pointer.
343  */
344 static coll_query_alias_t *
345 query_make_alias (coll_query_t *query, const gchar *field, gboolean optional)
346 {
347  coll_query_alias_t *alias;
348  alias = g_hash_table_lookup (query->aliases, field);
349 
350  /* Insert in the hashtable */
351  if (alias == NULL) {
352  gchar *fieldkey = g_strdup (field);
353 
354  alias = g_new (coll_query_alias_t, 1);
355  alias->optional = optional;
356  alias->id = 0;
357 
358  if (strcmp (field, "id") == 0) {
359  alias->type = XMMS_QUERY_ALIAS_ID;
360  } else {
361  alias->type = XMMS_QUERY_ALIAS_PROP;
362 
363  /* Found a base */
364  if (query->alias_base == NULL &&
365  (!optional || strcmp (field, XMMS_COLLQUERY_DEFAULT_BASE) == 0)) {
366  alias->id = 0;
367  query->alias_base = fieldkey;
368  } else {
369  alias->id = query->alias_count;
370  query->alias_count++;
371  }
372  }
373 
374  g_hash_table_insert (query->aliases, fieldkey, alias);
375 
376  /* If was not optional but now is, update */
377  } else if (!alias->optional && optional) {
378  alias->optional = optional;
379  }
380 
381  return alias;
382 }
383 
384 static coll_query_alias_t *
385 query_get_alias (coll_query_t *query, const gchar *field)
386 {
387  return g_hash_table_lookup (query->aliases, field);
388 }
389 
390 /* Find the canonical name of a field (strip flags, if any) */
391 static const gchar *
392 canonical_field_name (const gchar *field) {
393  if (*field == '-') {
394  field++;
395  } else if (*field == '~') {
396  field = NULL;
397  }
398  return field;
399 }
400 
401 
402 /* Determine whether the given operator is a reference to "All Media" */
403 static gboolean
404 operator_is_allmedia (xmmsv_coll_t *op)
405 {
406  gchar *target_name;
407  xmmsv_coll_attribute_get (op, "reference", &target_name);
408  return (target_name != NULL && strcmp (target_name, "All Media") == 0);
409 }
410 
411 static void
412 query_append_int (coll_query_t *query, gint i)
413 {
414  g_string_append_printf (query->conditions, "%d", i);
415 }
416 
417 static void
418 query_append_string (coll_query_t *query, const gchar *s)
419 {
420  g_string_append (query->conditions, s);
421 }
422 
423 static void
424 query_append_protect_string (coll_query_t *query, gchar *s)
425 {
426  gchar *preps;
427  if ((preps = sqlite_prepare_string (s)) != NULL) { /* FIXME: Return oom error */
428  query_append_string (query, preps);
429  g_free (preps);
430  }
431 }
432 
433 static void
434 query_append_operand (coll_query_t *query, xmms_coll_dag_t *dag, xmmsv_coll_t *coll)
435 {
436  xmmsv_coll_t *op = NULL;
437  gchar *target_name;
438  gchar *target_ns;
439  guint target_nsid;
440 
441  if (!xmmsv_list_get_coll (xmmsv_coll_operands_get (coll), 0, &op)) {
442 
443  /* Ref'd coll not saved as operand, look for it */
444  if (xmmsv_coll_attribute_get (coll, "reference", &target_name) &&
445  xmmsv_coll_attribute_get (coll, "namespace", &target_ns)) {
446 
447  target_nsid = xmms_collection_get_namespace_id (target_ns);
448  op = xmms_collection_get_pointer (dag, target_name, target_nsid);
449  }
450  }
451 
452  /* Append reference operator */
453  if (op != NULL) {
454  xmms_collection_append_to_query (dag, op, query);
455 
456  /* Cannot find reference, append dummy TRUE */
457  } else {
458  query_append_string (query, "1");
459  }
460 }
461 
462 static void
463 query_append_intersect_operand (coll_query_t *query, xmms_coll_dag_t *dag,
464  xmmsv_coll_t *coll)
465 {
466  xmmsv_coll_t *op;
467  xmmsv_t *tmp;
468 
469  if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
470  xmmsv_get_coll (tmp, &op);
471 
472  if (!operator_is_allmedia (op)) {
473  query_append_string (query, " AND ");
474  xmms_collection_append_to_query (dag, op, query);
475  }
476  }
477 }
478 
479 /* Append a filtering clause on the field value, depending on the operator type. */
480 static void
481 query_append_filter (coll_query_t *query, xmmsv_coll_type_t type,
482  gchar *key, gchar *value, gboolean case_sens)
483 {
484  coll_query_alias_t *alias;
485  gboolean optional;
486  gchar *temp;
487  gint i;
488 
489  if (type == XMMS_COLLECTION_TYPE_HAS) {
490  optional = TRUE;
491  } else {
492  optional = FALSE;
493  }
494 
495  alias = query_make_alias (query, key, optional);
496 
497  switch (type) {
498  /* escape strings */
501  if (case_sens) {
502  query_string_append_alias (query->conditions, alias,
504  } else {
505  query_append_string (query, "(");
506  query_string_append_alias (query->conditions, alias,
508  query_append_string (query, " COLLATE NOCASE)");
509  }
510 
511  if (type == XMMS_COLLECTION_TYPE_EQUALS) {
512  query_append_string (query, "=");
513  } else {
514  if (case_sens) {
515  query_append_string (query, " GLOB ");
516  } else {
517  query_append_string (query, " LIKE ");
518  }
519  }
520 
521  if (type == XMMS_COLLECTION_TYPE_MATCH && !case_sens) {
522  temp = g_strdup(value);
523  for (i = 0; temp[i]; i++) {
524  switch (temp[i]) {
525  case '*': temp[i] = '%'; break;
526  case '?': temp[i] = '_'; break;
527  default : break;
528  }
529  }
530  query_append_protect_string (query, temp);
531  g_free(temp);
532  } else {
533  query_append_protect_string (query, value);
534  }
535  break;
536 
537  /* do not escape numerical values */
540  query_string_append_alias (query->conditions, alias,
542  if (type == XMMS_COLLECTION_TYPE_SMALLER) {
543  query_append_string (query, " < ");
544  } else {
545  query_append_string (query, " > ");
546  }
547  query_append_string (query, value);
548  break;
549 
551  query_string_append_alias (query->conditions, alias,
553  query_append_string (query, " is not null");
554  break;
555 
556  /* Called with invalid type? */
557  default:
558  g_assert_not_reached ();
559  break;
560  }
561 }
562 
563 /* Append SELECT joins to the argument string for each alias of the hashtable. */
564 static void
565 query_string_append_joins (gpointer key, gpointer val, gpointer udata)
566 {
567  gchar *field;
568  GString *qstring;
569  coll_query_alias_t *alias;
570 
571  field = key;
572  qstring = (GString*)udata;
573  alias = (coll_query_alias_t*)val;
574 
575  if ((alias->id > 0) && (alias->type == XMMS_QUERY_ALIAS_PROP)) {
576  if (alias->optional) {
577  g_string_append_printf (qstring, " LEFT");
578  }
579 
580  g_string_append_printf (qstring,
581  " JOIN Media AS m%u ON m0.id=m%u.id AND m%u.key='%s' AND"
582  " xmms_source_pref (m%u.source) = "
583  "(SELECT MIN (xmms_source_pref (n.source)) FROM Media AS n"
584  " WHERE n.id = m0.id AND n.key = '%s')",
585  alias->id, alias->id, alias->id, field, alias->id, field);
586  }
587 }
588 
589 /* Given a list of fields, append the corresponding aliases to the argument string. */
590 static void
591 query_string_append_alias_list (coll_query_t *query, GString *qstring,
592  xmmsv_t *fields)
593 {
594  coll_query_alias_t *alias;
595  xmmsv_list_iter_t *it;
596  xmmsv_t *valstr;
597  gboolean first = TRUE;
598 
599  for (xmmsv_get_list_iter (fields, &it);
601  xmmsv_list_iter_next (it)) {
602 
603  /* extract string from cmdval_t */
604  const gchar *field, *canon_field;
605  xmmsv_list_iter_entry (it, &valstr);
606  xmmsv_get_string (valstr, &field);
607  canon_field = canonical_field_name (field);
608 
609  if (first) first = FALSE;
610  else {
611  g_string_append (qstring, ", ");
612  }
613 
614  if (canon_field != NULL) {
615  alias = query_get_alias (query, canon_field);
616  if (alias != NULL) {
617  query_string_append_alias (qstring, alias,
619  } else {
620  if (*field != '~') {
621  if (strcmp(canon_field, "id") == 0) {
622  g_string_append (qstring, "m0.id");
623  } else {
624  g_string_append_printf (qstring,
625  "(SELECT IFNULL (intval, value) "
626  "FROM Media WHERE id = m0.id AND key='%s' AND "
627  "xmms_source_pref (source) = "
628  "(SELECT MIN (xmms_source_pref (n.source)) "
629  "FROM Media AS n WHERE n.id = m0.id AND "
630  "n.key = '%s'))",
631  canon_field, canon_field);
632  }
633  }
634  }
635  }
636 
637  /* special prefix for ordering */
638  if (*field == '-') {
639  g_string_append (qstring, " DESC");
640  } else if (*field == '~') {
641  /* FIXME: Temporary hack to allow custom ordering functions */
642  g_string_append (qstring, field + 1);
643  }
644  }
645 }
646 
647 static void
648 query_string_append_fetch (coll_query_t *query, GString *qstring)
649 {
650  coll_query_alias_t *alias;
651  xmmsv_list_iter_t *it;
652  xmmsv_t *valstr;
653  gboolean first = TRUE;
654  const gchar *name;
655 
656  for (xmmsv_get_list_iter (query->params->fetch, &it);
658  xmmsv_list_iter_next (it)) {
659 
660  /* extract string from cmdval_t */
661  xmmsv_list_iter_entry (it, &valstr);
662  xmmsv_get_string (valstr, &name);
663  alias = query_make_alias (query, name, TRUE);
664 
665  if (first) first = FALSE;
666  else {
667  g_string_append (qstring, ", ");
668  }
669 
670  query_string_append_alias (qstring, alias,
672  g_string_append_printf (qstring, " AS %s", name);
673  }
674 }
675 
676 static void
677 query_string_append_alias (GString *qstring, coll_query_alias_t *alias,
679 {
680  switch (alias->type) {
682  switch (type) {
684  g_string_append_printf (qstring, "m%u.value", alias->id);
685  break;
687  g_string_append_printf (qstring, "m%u.intval", alias->id);
688  break;
690  g_string_append_printf (qstring, "IFNULL (m%u.intval, m%u.value)",
691  alias->id, alias->id);
692  break;
693  }
694  break;
695 
696  case XMMS_QUERY_ALIAS_ID:
697  g_string_append (qstring, "m0.id");
698  break;
699 
700  default:
701  break;
702  }
703 }
704 
705 /**
706  * @}
707  */
void xmmsv_list_iter_first(xmmsv_list_iter_t *it)
Rewind the iterator to the start of the list.
Definition: value.c:1523
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv_list.h:69
int xmmsv_coll_attribute_get(xmmsv_coll_t *coll, const char *key, char **value)
Retrieve the value of the attribute of the given collection.
Definition: coll.c:498
xmms_collection_namespace_id_t xmms_collection_get_namespace_id(const gchar *namespace)
Find the namespace id corresponding to a namespace string.
Definition: collection.c:1297
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
xmmsv_coll_t * xmms_collection_get_pointer(xmms_coll_dag_t *dag, const gchar *collname, guint nsid)
Find the collection structure corresponding to the given name in the given namespace.
Definition: collection.c:873
int xmmsv_get_list_iter(const xmmsv_t *val, xmmsv_list_iter_t **it)
Retrieves a list iterator from a list xmmsv_t.
Definition: value.c:926
coll_query_alias_type_t
Definition: collquery.c:39
xmmsv_coll_type_t
coll_query_value_type_t
Definition: collquery.c:58
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition: coll.c:437
struct xmmsv_St * xmmsv_coll_idlist_get(xmmsv_coll_t *coll)
Return the list of ids stored in the collection.
Definition: coll.c:429
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1553
void xmmsv_list_iter_explicit_destroy(xmmsv_list_iter_t *it)
Explicitly free list iterator.
Definition: value.c:1478
struct xmmsv_coll_St xmmsv_coll_t
Definition: xmmsv_coll.h:28
int xmmsv_list_get(xmmsv_t *listv, int pos, xmmsv_t **val)
Get the element at the given position in the list xmmsv_t.
Definition: value.c:1218
GString * xmms_collection_get_query(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, guint limit_start, guint limit_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group)
Definition: collquery.c:97
gchar * sqlite_prepare_string(const gchar *input)
Definition: sqlite.c:809
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition: value.c:1403
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
int xmmsv_list_iter_valid(xmmsv_list_iter_t *it)
Check whether the iterator is valid and points to a valid element.
Definition: value.c:1512
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
int xmmsv_list_iter_entry(xmmsv_list_iter_t *it, xmmsv_t **val)
Get the element currently pointed at by the iterator.
Definition: value.c:1495
int xmmsv_list_iter_entry_int(xmmsv_list_iter_t *it, int32_t *val)
int xmmsv_list_get_coll(xmmsv_t *v, int pos, xmmsv_coll_t **val)
#define XMMS_COLLQUERY_DEFAULT_BASE
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition: coll.c:367
int xmmsv_list_foreach(xmmsv_t *listv, xmmsv_list_foreach_func func, void *user_data)
Apply a function to each element in the list, in sequential order.
Definition: value.c:1375
int xmmsv_get_coll(const xmmsv_t *val, xmmsv_coll_t **coll)
Retrieves a collection from the value.
Definition: value.c:883
struct xmms_coll_dag_St xmms_coll_dag_t