1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of RhythmDB - Rhythmbox backend queryable database
5 * Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <glib-object.h>
29 #include <gobject/gvaluecollector.h>
32 #include "rhythmdb-private.h"
35 #define RB_PARSE_CONJ (xmlChar *) "conjunction"
36 #define RB_PARSE_SUBQUERY (xmlChar *) "subquery"
37 #define RB_PARSE_LIKE (xmlChar *) "like"
38 #define RB_PARSE_PROP (xmlChar *) "prop"
39 #define RB_PARSE_NOT_LIKE (xmlChar *) "not-like"
40 #define RB_PARSE_PREFIX (xmlChar *) "prefix"
41 #define RB_PARSE_SUFFIX (xmlChar *) "suffix"
42 #define RB_PARSE_EQUALS (xmlChar *) "equals"
43 #define RB_PARSE_DISJ (xmlChar *) "disjunction"
44 #define RB_PARSE_GREATER (xmlChar *) "greater"
45 #define RB_PARSE_LESS (xmlChar *) "less"
46 #define RB_PARSE_CURRENT_TIME_WITHIN (xmlChar *) "current-time-within"
47 #define RB_PARSE_CURRENT_TIME_NOT_WITHIN (xmlChar *) "current-time-not-within"
48 #define RB_PARSE_YEAR_EQUALS RB_PARSE_EQUALS
49 #define RB_PARSE_YEAR_GREATER RB_PARSE_GREATER
50 #define RB_PARSE_YEAR_LESS RB_PARSE_LESS
52 * rhythmdb_query_copy:
53 * @array: the query to copy.
55 * Creates a copy of a query.
57 * Return value: a copy of the passed query. It must be freed with rhythmdb_query_free()
60 rhythmdb_query_copy (GPtrArray
*array
)
67 ret
= g_ptr_array_sized_new (array
->len
);
68 rhythmdb_query_concatenate (ret
, array
);
74 rhythmdb_query_concatenate (GPtrArray
*query1
, GPtrArray
*query2
)
82 for (i
= 0; i
< query2
->len
; i
++) {
83 RhythmDBQueryData
*data
= g_ptr_array_index (query2
, i
);
84 RhythmDBQueryData
*new_data
= g_new0 (RhythmDBQueryData
, 1);
85 new_data
->type
= data
->type
;
86 new_data
->propid
= data
->propid
;
88 new_data
->val
= g_new0 (GValue
, 1);
89 g_value_init (new_data
->val
, G_VALUE_TYPE (data
->val
));
90 g_value_copy (data
->val
, new_data
->val
);
93 new_data
->subquery
= rhythmdb_query_copy (data
->subquery
);
94 g_ptr_array_add (query1
, new_data
);
99 rhythmdb_query_parse_valist (RhythmDB
*db
, va_list args
)
101 RhythmDBQueryType query
;
102 GPtrArray
*ret
= g_ptr_array_new ();
105 while ((query
= va_arg (args
, RhythmDBQueryType
)) != RHYTHMDB_QUERY_END
) {
106 RhythmDBQueryData
*data
= g_new0 (RhythmDBQueryData
, 1);
109 case RHYTHMDB_QUERY_DISJUNCTION
:
111 case RHYTHMDB_QUERY_SUBQUERY
:
112 data
->subquery
= rhythmdb_query_copy (va_arg (args
, GPtrArray
*));
114 case RHYTHMDB_QUERY_PROP_EQUALS
:
115 case RHYTHMDB_QUERY_PROP_LIKE
:
116 case RHYTHMDB_QUERY_PROP_NOT_LIKE
:
117 case RHYTHMDB_QUERY_PROP_PREFIX
:
118 case RHYTHMDB_QUERY_PROP_SUFFIX
:
119 case RHYTHMDB_QUERY_PROP_GREATER
:
120 case RHYTHMDB_QUERY_PROP_LESS
:
121 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
:
122 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
:
123 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS
:
124 case RHYTHMDB_QUERY_PROP_YEAR_GREATER
:
125 case RHYTHMDB_QUERY_PROP_YEAR_LESS
:
126 data
->propid
= va_arg (args
, guint
);
127 data
->val
= g_new0 (GValue
, 1);
128 g_value_init (data
->val
, rhythmdb_get_property_type (db
, data
->propid
));
129 G_VALUE_COLLECT (data
->val
, args
, 0, &error
);
131 case RHYTHMDB_QUERY_END
:
132 g_assert_not_reached ();
135 g_ptr_array_add (ret
, data
);
141 * rhythmdb_query_parse:
142 * @db: a #RhythmDB instance
144 * Creates a query from a list of criteria.
146 * Most criteria consists of an operator (#RhythmDBQueryType),
147 * a property (#RhythmDBPropType) and the data to compare with. An entry
148 * matches a criteria if the operator returns true with the value of the
149 * entries property as the first argument, and the given data as the second
152 * Three types criteria are special. Passing RHYTHMDB_QUERY_END indicates the
153 * end of the list of criteria, and must be the last passes parameter.
155 * The second special criteria is a subquery which is defined by passing
156 * RHYTHMDB_QUERY_SUBQUERY, followed by a query (#GPtrArray). An entry will
157 * match a subquery criteria if it matches all criteria in the subquery.
159 * The third special criteria is a disjunction which is defined by passing
160 * RHYTHMDB_QUERY_DISJUNCTION, which will make an entry match the query if
161 * it matches the criteria before the disjunction, the criteria after the
162 * disjunction, or both.
165 * rhythmdb_query_parse (db,
166 * RHYTHMDB_QUERY_SUBQUERY, subquery,
167 * RHYTHMDB_QUERY_DISJUNCTION
168 * RHYTHMDB_QUERY_PROP_LIKE, RHYTHMDB_PROP_TITLE, "cat",
169 * RHYTHMDB_QUERY_DISJUNCTION
170 * RHYTHMDB_QUERY_PROP_GREATER, RHYTHMDB_PROP_RATING, 2.5,
171 * RHYTHMDB_QUERY_PROP_LESS, RHYTHMDB_PROP_PLAY_COUNT, 10,
172 * RHYTHMDB_QUERY_END);
174 * will create a query that matches entries:
175 * a) that match the query "subquery", or
176 * b) that have "cat" in their title, or
177 * c) have a rating of at least 2.5, and a play count of at most 10
179 * Returns: a the newly created query. It must be freed with rhythmdb_query_free()
182 rhythmdb_query_parse (RhythmDB
*db
, ...)
189 ret
= rhythmdb_query_parse_valist (db
, args
);
197 * rhythmdb_query_append:
198 * @db: a #RhythmDB instance
201 * Appends new criteria to the query @query.
203 * The list of criteria must be in the same format as for rhythmdb_query_parse,
204 * and ended by RHYTHMDB_QUERY_END.
207 rhythmdb_query_append (RhythmDB
*db
, GPtrArray
*query
, ...)
211 GPtrArray
*new = g_ptr_array_new ();
213 va_start (args
, query
);
215 new = rhythmdb_query_parse_valist (db
, args
);
217 for (i
= 0; i
< new->len
; i
++)
218 g_ptr_array_add (query
, g_ptr_array_index (new, i
));
220 g_ptr_array_free (new, TRUE
);
226 * rhythmdb_query_free:
229 * Frees the query @query
232 rhythmdb_query_free (GPtrArray
*query
)
239 for (i
= 0; i
< query
->len
; i
++) {
240 RhythmDBQueryData
*data
= g_ptr_array_index (query
, i
);
241 switch (data
->type
) {
242 case RHYTHMDB_QUERY_DISJUNCTION
:
244 case RHYTHMDB_QUERY_SUBQUERY
:
245 rhythmdb_query_free (data
->subquery
);
247 case RHYTHMDB_QUERY_PROP_EQUALS
:
248 case RHYTHMDB_QUERY_PROP_LIKE
:
249 case RHYTHMDB_QUERY_PROP_NOT_LIKE
:
250 case RHYTHMDB_QUERY_PROP_PREFIX
:
251 case RHYTHMDB_QUERY_PROP_SUFFIX
:
252 case RHYTHMDB_QUERY_PROP_GREATER
:
253 case RHYTHMDB_QUERY_PROP_LESS
:
254 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
:
255 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
:
256 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS
:
257 case RHYTHMDB_QUERY_PROP_YEAR_GREATER
:
258 case RHYTHMDB_QUERY_PROP_YEAR_LESS
:
259 g_value_unset (data
->val
);
262 case RHYTHMDB_QUERY_END
:
263 g_assert_not_reached ();
269 g_ptr_array_free (query
, TRUE
);
273 prop_gvalue_to_string (RhythmDB
*db
,
274 RhythmDBPropType propid
,
277 /* special-case some properties */
279 case RHYTHMDB_PROP_TYPE
:
281 RhythmDBEntryType type
= g_value_get_pointer (val
);
282 return g_strdup (type
->name
);
289 /* otherwise just convert numbers to strings */
290 switch (G_VALUE_TYPE (val
)) {
292 return g_value_dup_string (val
);
294 return g_strdup_printf ("%d", g_value_get_boolean (val
));
296 return g_strdup_printf ("%d", g_value_get_int (val
));
298 return g_strdup_printf ("%ld", g_value_get_long (val
));
300 return g_strdup_printf ("%lu", g_value_get_ulong (val
));
302 return g_strdup_printf ("%" G_GUINT64_FORMAT
, g_value_get_uint64 (val
));
304 return g_strdup_printf ("%f", g_value_get_float (val
));
306 return g_strdup_printf ("%f", g_value_get_double (val
));
308 g_assert_not_reached ();
314 write_encoded_gvalue (RhythmDB
*db
,
316 RhythmDBPropType propid
,
322 strval
= prop_gvalue_to_string (db
, propid
, val
);
323 quoted
= xmlEncodeEntitiesReentrant (NULL
, BAD_CAST strval
);
326 xmlNodeSetContent (node
, quoted
);
331 rhythmdb_read_encoded_property (RhythmDB
*db
,
333 RhythmDBPropType propid
,
336 g_value_init (val
, rhythmdb_get_property_type (db
, propid
));
338 switch (G_VALUE_TYPE (val
)) {
340 g_value_set_string (val
, content
);
343 g_value_set_boolean (val
, g_ascii_strtoull (content
, NULL
, 10));
346 g_value_set_ulong (val
, g_ascii_strtoull (content
, NULL
, 10));
349 g_value_set_uint64 (val
, g_ascii_strtoull (content
, NULL
, 10));
352 g_value_set_double (val
, g_ascii_strtod (content
, NULL
));
355 if (propid
== RHYTHMDB_PROP_TYPE
) {
356 RhythmDBEntryType entry_type
;
357 entry_type
= rhythmdb_entry_type_get_by_name (db
, content
);
358 if (entry_type
!= RHYTHMDB_ENTRY_TYPE_INVALID
) {
359 g_value_set_pointer (val
, entry_type
);
362 g_warning ("Unexpected entry type");
366 /* Falling through on purpose to get an assert for unexpected
370 g_warning ("Attempt to read '%s' of unhandled type %s",
371 rhythmdb_nice_elt_name_from_propid (db
, propid
),
372 g_type_name (G_VALUE_TYPE (val
)));
373 g_assert_not_reached ();
379 rhythmdb_query_serialize (RhythmDB
*db
, GPtrArray
*query
,
383 xmlNodePtr node
= xmlNewChild (parent
, NULL
, RB_PARSE_CONJ
, NULL
);
386 for (i
= 0; i
< query
->len
; i
++) {
387 RhythmDBQueryData
*data
= g_ptr_array_index (query
, i
);
389 switch (data
->type
) {
390 case RHYTHMDB_QUERY_SUBQUERY
:
391 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_SUBQUERY
, NULL
);
392 rhythmdb_query_serialize (db
, data
->subquery
, subnode
);
394 case RHYTHMDB_QUERY_PROP_LIKE
:
395 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_LIKE
, NULL
);
396 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
397 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
399 case RHYTHMDB_QUERY_PROP_NOT_LIKE
:
400 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_NOT_LIKE
, NULL
);
401 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
402 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
404 case RHYTHMDB_QUERY_PROP_PREFIX
:
405 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_PREFIX
, NULL
);
406 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
407 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
409 case RHYTHMDB_QUERY_PROP_SUFFIX
:
410 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_SUFFIX
, NULL
);
411 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
412 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
414 case RHYTHMDB_QUERY_PROP_EQUALS
:
415 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_EQUALS
, NULL
);
416 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
417 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
419 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS
:
420 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_YEAR_EQUALS
, NULL
);
421 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
422 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
424 case RHYTHMDB_QUERY_DISJUNCTION
:
425 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_DISJ
, NULL
);
427 case RHYTHMDB_QUERY_END
:
429 case RHYTHMDB_QUERY_PROP_GREATER
:
430 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_GREATER
, NULL
);
431 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
432 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
434 case RHYTHMDB_QUERY_PROP_YEAR_GREATER
:
435 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_YEAR_GREATER
, NULL
);
436 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
437 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
439 case RHYTHMDB_QUERY_PROP_LESS
:
440 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_LESS
, NULL
);
441 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
442 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
444 case RHYTHMDB_QUERY_PROP_YEAR_LESS
:
445 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_YEAR_LESS
, NULL
);
446 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
447 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
449 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
:
450 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_CURRENT_TIME_WITHIN
, NULL
);
451 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
452 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
454 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
:
455 subnode
= xmlNewChild (node
, NULL
, RB_PARSE_CURRENT_TIME_NOT_WITHIN
, NULL
);
456 xmlSetProp (subnode
, RB_PARSE_PROP
, rhythmdb_nice_elt_name_from_propid (db
, data
->propid
));
457 write_encoded_gvalue (db
, subnode
, data
->propid
, data
->val
);
464 rhythmdb_query_deserialize (RhythmDB
*db
, xmlNodePtr parent
)
466 GPtrArray
*query
= g_ptr_array_new ();
469 g_assert (!xmlStrcmp (parent
->name
, RB_PARSE_CONJ
));
471 for (child
= parent
->children
; child
; child
= child
->next
) {
472 RhythmDBQueryData
*data
;
474 if (xmlNodeIsText (child
))
477 data
= g_new0 (RhythmDBQueryData
, 1);
479 if (!xmlStrcmp (child
->name
, RB_PARSE_SUBQUERY
)) {
481 data
->type
= RHYTHMDB_QUERY_SUBQUERY
;
482 subquery
= child
->children
;
483 while (xmlNodeIsText (subquery
))
484 subquery
= subquery
->next
;
486 data
->subquery
= rhythmdb_query_deserialize (db
, subquery
);
487 } else if (!xmlStrcmp (child
->name
, RB_PARSE_DISJ
)) {
488 data
->type
= RHYTHMDB_QUERY_DISJUNCTION
;
489 } else if (!xmlStrcmp (child
->name
, RB_PARSE_LIKE
)) {
490 data
->type
= RHYTHMDB_QUERY_PROP_LIKE
;
491 } else if (!xmlStrcmp (child
->name
, RB_PARSE_NOT_LIKE
)) {
492 data
->type
= RHYTHMDB_QUERY_PROP_NOT_LIKE
;
493 } else if (!xmlStrcmp (child
->name
, RB_PARSE_PREFIX
)) {
494 data
->type
= RHYTHMDB_QUERY_PROP_PREFIX
;
495 } else if (!xmlStrcmp (child
->name
, RB_PARSE_SUFFIX
)) {
496 data
->type
= RHYTHMDB_QUERY_PROP_SUFFIX
;
497 } else if (!xmlStrcmp (child
->name
, RB_PARSE_EQUALS
)) {
500 prop
= xmlGetProp(child
, RB_PARSE_PROP
);
501 if (!xmlStrcmp(prop
, (xmlChar
*)"date"))
502 data
->type
= RHYTHMDB_QUERY_PROP_YEAR_EQUALS
;
504 data
->type
= RHYTHMDB_QUERY_PROP_EQUALS
;
506 } else if (!xmlStrcmp (child
->name
, RB_PARSE_GREATER
)) {
509 prop
= xmlGetProp(child
, RB_PARSE_PROP
);
510 if (!xmlStrcmp(prop
, (xmlChar
*)"date"))
511 data
->type
= RHYTHMDB_QUERY_PROP_YEAR_GREATER
;
513 data
->type
= RHYTHMDB_QUERY_PROP_GREATER
;
515 } else if (!xmlStrcmp (child
->name
, RB_PARSE_LESS
)) {
518 prop
= xmlGetProp(child
, RB_PARSE_PROP
);
519 if (!xmlStrcmp(prop
, (xmlChar
*)"date"))
520 data
->type
= RHYTHMDB_QUERY_PROP_YEAR_LESS
;
522 data
->type
= RHYTHMDB_QUERY_PROP_LESS
;
524 } else if (!xmlStrcmp (child
->name
, RB_PARSE_CURRENT_TIME_WITHIN
)) {
525 data
->type
= RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
;
526 } else if (!xmlStrcmp (child
->name
, RB_PARSE_CURRENT_TIME_NOT_WITHIN
)) {
527 data
->type
= RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
;
529 g_assert_not_reached ();
531 if (!xmlStrcmp (child
->name
, RB_PARSE_LIKE
)
532 || !xmlStrcmp (child
->name
, RB_PARSE_NOT_LIKE
)
533 || !xmlStrcmp (child
->name
, RB_PARSE_PREFIX
)
534 || !xmlStrcmp (child
->name
, RB_PARSE_SUFFIX
)
535 || !xmlStrcmp (child
->name
, RB_PARSE_EQUALS
)
536 || !xmlStrcmp (child
->name
, RB_PARSE_GREATER
)
537 || !xmlStrcmp (child
->name
, RB_PARSE_LESS
)
538 || !xmlStrcmp (child
->name
, RB_PARSE_YEAR_EQUALS
)
539 || !xmlStrcmp (child
->name
, RB_PARSE_YEAR_GREATER
)
540 || !xmlStrcmp (child
->name
, RB_PARSE_YEAR_LESS
)
541 || !xmlStrcmp (child
->name
, RB_PARSE_CURRENT_TIME_WITHIN
)
542 || !xmlStrcmp (child
->name
, RB_PARSE_CURRENT_TIME_NOT_WITHIN
)) {
544 xmlChar
*propstr
= xmlGetProp (child
, RB_PARSE_PROP
);
545 gint propid
= rhythmdb_propid_from_nice_elt_name (db
, propstr
);
548 g_assert (propid
>= 0 && propid
< RHYTHMDB_NUM_PROPERTIES
);
550 data
->propid
= propid
;
551 data
->val
= g_new0 (GValue
, 1);
553 content
= (char *)xmlNodeGetContent (child
);
554 rhythmdb_read_encoded_property (db
, content
, data
->propid
, data
->val
);
558 g_ptr_array_add (query
, data
);
565 * This is used to "process" queries, before using them. It is mainly used to two things:
567 * 1) performing expensive data transformations once per query, rather than
568 * once per entry we try to match against. e.g. RHYTHMDB_PROP_SEARCH_MATCH
570 * 2) defining criteria in terms of other lower-level ones that the db backend
571 * actually implements. e.g. RHYTHMDB_QUERY_YEAR_*
575 rhythmdb_query_preprocess (RhythmDB
*db
, GPtrArray
*query
)
582 for (i
= 0; i
< query
->len
; i
++) {
583 RhythmDBQueryData
*data
= g_ptr_array_index (query
, i
);
584 gboolean restart_criteria
= FALSE
;
586 if (data
->subquery
) {
587 rhythmdb_query_preprocess (db
, data
->subquery
);
588 } else switch (data
->propid
) {
589 case RHYTHMDB_PROP_TITLE_FOLDED
:
590 case RHYTHMDB_PROP_GENRE_FOLDED
:
591 case RHYTHMDB_PROP_ARTIST_FOLDED
:
592 case RHYTHMDB_PROP_ALBUM_FOLDED
:
594 /* as we are matching against a folded property, the string needs to also be folded */
595 const char *orig
= g_value_get_string (data
->val
);
596 char *folded
= rb_search_fold (orig
);
598 g_value_reset (data
->val
);
599 g_value_take_string (data
->val
, folded
);
603 case RHYTHMDB_PROP_SEARCH_MATCH
:
605 const char *orig
= g_value_get_string (data
->val
);
606 char *folded
= rb_search_fold (orig
);
607 char **words
= rb_string_split_words (folded
);
610 g_value_unset (data
->val
);
611 g_value_init (data
->val
, G_TYPE_STRV
);
612 g_value_take_boxed (data
->val
, words
);
616 case RHYTHMDB_PROP_DATE
:
624 search_date
= g_value_get_ulong (data
->val
);
625 g_date_set_julian (&date
, search_date
);
626 year
= g_date_get_year (&date
);
627 g_date_clear (&date
, 1);
629 /* get Julian dates for beginning and end of year */
630 g_date_set_dmy (&date
, 1, G_DATE_JANUARY
, year
);
631 begin
= g_date_get_julian (&date
);
632 g_date_clear (&date
, 1);
634 /* and the day before the beginning of the next year */
635 g_date_set_dmy (&date
, 1, G_DATE_JANUARY
, year
+ 1);
636 end
= g_date_get_julian (&date
) - 1;
640 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS
:
641 restart_criteria
= TRUE
;
642 data
->type
= RHYTHMDB_QUERY_SUBQUERY
;
643 data
->subquery
= rhythmdb_query_parse (db
,
644 RHYTHMDB_QUERY_PROP_GREATER
, data
->propid
, begin
,
645 RHYTHMDB_QUERY_PROP_LESS
, data
->propid
, end
,
649 case RHYTHMDB_QUERY_PROP_YEAR_LESS
:
650 restart_criteria
= TRUE
;
651 data
->type
= RHYTHMDB_QUERY_PROP_LESS
;
652 g_value_set_ulong (data
->val
, end
);
655 case RHYTHMDB_QUERY_PROP_YEAR_GREATER
:
656 restart_criteria
= TRUE
;
657 data
->type
= RHYTHMDB_QUERY_PROP_GREATER
;
658 g_value_set_ulong (data
->val
, begin
);
672 /* re-do this criteria, in case it needs further transformation */
673 if (restart_criteria
)
679 rhythmdb_query_append_prop_multiple (RhythmDB
*db
, GPtrArray
*query
, RhythmDBPropType propid
, GList
*items
)
686 if (items
->next
== NULL
) {
687 rhythmdb_query_append (db
,
689 RHYTHMDB_QUERY_PROP_EQUALS
,
696 subquery
= g_ptr_array_new ();
698 rhythmdb_query_append (db
,
700 RHYTHMDB_QUERY_PROP_EQUALS
,
706 rhythmdb_query_append (db
,
708 RHYTHMDB_QUERY_DISJUNCTION
,
709 RHYTHMDB_QUERY_PROP_EQUALS
,
715 rhythmdb_query_append (db
, query
, RHYTHMDB_QUERY_SUBQUERY
, subquery
,
720 rhythmdb_query_is_time_relative (RhythmDB
*db
, GPtrArray
*query
)
726 for (i
=0; i
< query
->len
; i
++) {
727 RhythmDBQueryData
*data
= g_ptr_array_index (query
, i
);
729 if (data
->subquery
) {
730 if (rhythmdb_query_is_time_relative (db
, data
->subquery
))
736 switch (data
->type
) {
737 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
:
738 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
:
749 * rhythmdb_query_to_string:
750 * @db: a #RhythmDB instance
753 * Returns a supposedly human-readable form of the query.
754 * This is only intended for debug usage.
757 rhythmdb_query_to_string (RhythmDB
*db
, GPtrArray
*query
)
762 buf
= g_string_sized_new (100);
763 for (i
= 0; i
< query
->len
; i
++) {
765 RhythmDBQueryData
*data
= g_ptr_array_index (query
, i
);
767 switch (data
->type
) {
768 case RHYTHMDB_QUERY_SUBQUERY
:
772 s
= rhythmdb_query_to_string (db
, data
->subquery
);
773 g_string_append_printf (buf
, "{ %s }", s
);
777 case RHYTHMDB_QUERY_PROP_LIKE
:
780 case RHYTHMDB_QUERY_PROP_NOT_LIKE
:
783 case RHYTHMDB_QUERY_PROP_PREFIX
:
786 case RHYTHMDB_QUERY_PROP_SUFFIX
:
789 case RHYTHMDB_QUERY_PROP_EQUALS
:
792 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS
:
793 fmt
= "(year(%s) == %s)";
795 case RHYTHMDB_QUERY_DISJUNCTION
:
796 g_string_append_printf (buf
, " || ");
798 case RHYTHMDB_QUERY_END
:
800 case RHYTHMDB_QUERY_PROP_GREATER
:
803 case RHYTHMDB_QUERY_PROP_YEAR_GREATER
:
804 fmt
= "(year(%s) > %s)";
806 case RHYTHMDB_QUERY_PROP_LESS
:
809 case RHYTHMDB_QUERY_PROP_YEAR_LESS
:
810 fmt
= "(year(%s) < %s)";
812 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN
:
815 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN
:
823 value
= prop_gvalue_to_string (db
, data
->propid
, data
->val
);
824 g_string_append_printf (buf
, fmt
,
825 rhythmdb_nice_elt_name_from_propid (db
, data
->propid
),
832 return g_string_free (buf
, FALSE
);
836 rhythmdb_query_get_type (void)
838 static GType type
= 0;
840 if (G_UNLIKELY (type
== 0)) {
841 type
= g_boxed_type_register_static ("RhythmDBQuery",
842 (GBoxedCopyFunc
)rhythmdb_query_copy
,
843 (GBoxedFreeFunc
)rhythmdb_query_free
);