hythmbox-2.98/rhythmdb/rhythmdb-query.c

Location Tool Test ID Function Issue
rhythmdb-query.c:243:13 clang-analyzer Value stored to 'new' during its initialization is never read
rhythmdb-query.c:243:13 clang-analyzer Value stored to 'new' during its initialization is never read
rhythmdb-query.c:553:4 clang-analyzer Value stored to 'subnode' is never read
rhythmdb-query.c:553:4 clang-analyzer Value stored to 'subnode' is never read
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2003,2004 Colin Walters <walters@gnome.org>
   4  *
   5  *  This program is free software; you can redistribute it and/or modify
   6  *  it under the terms of the GNU General Public License as published by
   7  *  the Free Software Foundation; either version 2 of the License, or
   8  *  (at your option) any later version.
   9  *
  10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  11  *  GStreamer plugins to be used and distributed together with GStreamer
  12  *  and Rhythmbox. This permission is above and beyond the permissions granted
  13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  14  *  you may extend this exception to your version of the code, but you are not
  15  *  obligated to do so. If you do not wish to do so, delete this exception
  16  *  statement from your version.
  17  *
  18  *  This program is distributed in the hope that it will be useful,
  19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21  *  GNU General Public License for more details.
  22  *
  23  *  You should have received a copy of the GNU General Public License
  24  *  along with this program; if not, write to the Free Software
  25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  26  *
  27  */
  28 
  29 #include <config.h>
  30 
  31 #include <string.h>
  32 
  33 #include <glib.h>
  34 #include <glib-object.h>
  35 #include <gobject/gvaluecollector.h>
  36 
  37 #include "rhythmdb.h"
  38 #include "rhythmdb-private.h"
  39 #include "rb-util.h"
  40 
  41 #define RB_PARSE_CONJ (xmlChar *) "conjunction"
  42 #define RB_PARSE_SUBQUERY (xmlChar *) "subquery"
  43 #define RB_PARSE_LIKE (xmlChar *) "like"
  44 #define RB_PARSE_PROP (xmlChar *) "prop"
  45 #define RB_PARSE_NOT_LIKE (xmlChar *) "not-like"
  46 #define RB_PARSE_PREFIX (xmlChar *) "prefix"
  47 #define RB_PARSE_SUFFIX (xmlChar *) "suffix"
  48 #define RB_PARSE_EQUALS (xmlChar *) "equals"
  49 #define RB_PARSE_NOT_EQUAL (xmlChar *) "not-equal"
  50 #define RB_PARSE_DISJ (xmlChar *) "disjunction"
  51 #define RB_PARSE_GREATER (xmlChar *) "greater"
  52 #define RB_PARSE_LESS (xmlChar *) "less"
  53 #define RB_PARSE_CURRENT_TIME_WITHIN (xmlChar *) "current-time-within"
  54 #define RB_PARSE_CURRENT_TIME_NOT_WITHIN (xmlChar *) "current-time-not-within"
  55 #define RB_PARSE_YEAR_EQUALS RB_PARSE_EQUALS
  56 #define RB_PARSE_YEAR_NOT_EQUAL RB_PARSE_NOT_EQUAL
  57 #define RB_PARSE_YEAR_GREATER RB_PARSE_GREATER
  58 #define RB_PARSE_YEAR_LESS RB_PARSE_LESS
  59 
  60 /**
  61  * rhythmdb_query_copy:
  62  * @array: the query to copy.
  63  *
  64  * Creates a copy of a query.
  65  *
  66  * Return value: (transfer full): a copy of the passed query.
  67  * It must be freed with rhythmdb_query_free()
  68  **/
  69 GPtrArray *
  70 rhythmdb_query_copy (GPtrArray *array)
  71 {
  72 	GPtrArray *ret;
  73 
  74 	if (!array)
  75 		return NULL;
  76 
  77 	ret = g_ptr_array_sized_new (array->len);
  78 	rhythmdb_query_concatenate (ret, array);
  79 
  80 	return ret;
  81 }
  82 
  83 /**
  84  * rhythmdb_query_concatenate:
  85  * @query1: query to append to
  86  * @query2: query to append
  87  *
  88  * Appends @query2 to @query1.
  89  */
  90 void
  91 rhythmdb_query_concatenate (GPtrArray *query1, GPtrArray *query2)
  92 {
  93 	guint i;
  94 
  95 	g_assert (query2);
  96 	if (!query2)
  97 		return;
  98 
  99 	for (i = 0; i < query2->len; i++) {
 100 		RhythmDBQueryData *data = g_ptr_array_index (query2, i);
 101 		RhythmDBQueryData *new_data = g_new0 (RhythmDBQueryData, 1);
 102 		new_data->type = data->type;
 103 		new_data->propid = data->propid;
 104 		if (data->val) {
 105 			new_data->val = g_new0 (GValue, 1);
 106 			g_value_init (new_data->val, G_VALUE_TYPE (data->val));
 107 			g_value_copy (data->val, new_data->val);
 108 		}
 109 		if (data->subquery)
 110 			new_data->subquery = rhythmdb_query_copy (data->subquery);
 111 		g_ptr_array_add (query1, new_data);
 112 	}
 113 }
 114 
 115 /**
 116  * rhythmdb_query_parse_valist:
 117  * @db: the #RhythmDB
 118  * @args: the arguments to parse
 119  *
 120  * Converts a va_list into a parsed query in the form of a @GPtrArray.
 121  * See @rhythmdb_query_parse for more information on the parsing process.
 122  *
 123  * Return value: converted query
 124  */
 125 GPtrArray *
 126 rhythmdb_query_parse_valist (RhythmDB *db, va_list args)
 127 {
 128 	RhythmDBQueryType query;
 129 	GPtrArray *ret = g_ptr_array_new ();
 130 	char *error;
 131 
 132 	while ((query = va_arg (args, RhythmDBQueryType)) != RHYTHMDB_QUERY_END) {
 133 		RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1);
 134 		data->type = query;
 135 		switch (query) {
 136 		case RHYTHMDB_QUERY_DISJUNCTION:
 137 			break;
 138 		case RHYTHMDB_QUERY_SUBQUERY:
 139 			data->subquery = rhythmdb_query_copy (va_arg (args, GPtrArray *));
 140 			break;
 141 		case RHYTHMDB_QUERY_PROP_EQUALS:
 142 		case RHYTHMDB_QUERY_PROP_NOT_EQUAL:
 143 		case RHYTHMDB_QUERY_PROP_LIKE:
 144 		case RHYTHMDB_QUERY_PROP_NOT_LIKE:
 145 		case RHYTHMDB_QUERY_PROP_PREFIX:
 146 		case RHYTHMDB_QUERY_PROP_SUFFIX:
 147 		case RHYTHMDB_QUERY_PROP_GREATER:
 148 		case RHYTHMDB_QUERY_PROP_LESS:
 149 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
 150 		case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
 151 		case RHYTHMDB_QUERY_PROP_YEAR_EQUALS:
 152 		case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL:
 153 		case RHYTHMDB_QUERY_PROP_YEAR_GREATER:
 154 		case RHYTHMDB_QUERY_PROP_YEAR_LESS:
 155 			data->propid = va_arg (args, guint);
 156 			data->val = g_new0 (GValue, 1);
 157 			g_value_init (data->val, rhythmdb_get_property_type (db, data->propid));
 158 			G_VALUE_COLLECT (data->val, args, 0, &error);
 159 			break;
 160 		case RHYTHMDB_QUERY_END:
 161 			g_assert_not_reached ();
 162 			break;
 163 		}
 164 		g_ptr_array_add (ret, data);
 165 	}
 166 	return ret;
 167 }
 168 
 169 /**
 170  * rhythmdb_query_parse:
 171  * @db: a #RhythmDB instance
 172  * @Varargs: query criteria to parse
 173  *
 174  * Creates a query from a list of criteria.
 175  *
 176  * Most criteria consists of an operator (#RhythmDBQueryType),
 177  * a property (#RhythmDBPropType) and the data to compare with. An entry
 178  * matches a criteria if the operator returns true with the value of the
 179  * entries property as the first argument, and the given data as the second
 180  * argument.
 181  *
 182  * Three types criteria are special. Passing RHYTHMDB_QUERY_END indicates the
 183  * end of the list of criteria, and must be the last passes parameter.
 184  *
 185  * The second special criteria is a subquery which is defined by passing
 186  * RHYTHMDB_QUERY_SUBQUERY, followed by a query (#GPtrArray). An entry will
 187  * match a subquery criteria if it matches all criteria in the subquery.
 188  *
 189  * The third special criteria is a disjunction which is defined by passing
 190  * RHYTHMDB_QUERY_DISJUNCTION, which will make an entry match the query if
 191  * it matches the criteria before the disjunction, the criteria after the
 192  * disjunction, or both.
 193  *
 194  * Example:
 195  * 	rhythmdb_query_parse (db,
 196  * 		RHYTHMDB_QUERY_SUBQUERY, subquery,
 197  * 		RHYTHMDB_QUERY_DISJUNCTION
 198  * 		RHYTHMDB_QUERY_PROP_LIKE, RHYTHMDB_PROP_TITLE, "cat",
 199  *		RHYTHMDB_QUERY_DISJUNCTION
 200  *		RHYTHMDB_QUERY_PROP_GREATER, RHYTHMDB_PROP_RATING, 2.5,
 201  *		RHYTHMDB_QUERY_PROP_LESS, RHYTHMDB_PROP_PLAY_COUNT, 10,
 202  * 		RHYTHMDB_QUERY_END);
 203  *
 204  * 	will create a query that matches entries:
 205  * 	a) that match the query "subquery", or
 206  * 	b) that have "cat" in their title, or
 207  * 	c) have a rating of at least 2.5, and a play count of at most 10
 208  *
 209  * Returns: (transfer full): a the newly created query.
 210  * It must be freed with rhythmdb_query_free()
 211  **/
 212 GPtrArray *
 213 rhythmdb_query_parse (RhythmDB *db, ...)
 214 {
 215 	GPtrArray *ret;
 216 	va_list args;
 217 
 218 	va_start (args, db);
 219 
 220 	ret = rhythmdb_query_parse_valist (db, args);
 221 
 222 	va_end (args);
 223 
 224 	return ret;
 225 }
 226 
 227 /**
 228  * rhythmdb_query_append:
 229  * @db: a #RhythmDB instance
 230  * @query: a query.
 231  * @Varargs: query criteria to append
 232  *
 233  * Appends new criteria to the query @query.
 234  *
 235  * The list of criteria must be in the same format as for rhythmdb_query_parse,
 236  * and ended by RHYTHMDB_QUERY_END.
 237  **/
 238 void
 239 rhythmdb_query_append (RhythmDB *db, GPtrArray *query, ...)
 240 {
 241 	va_list args;
 242 	guint i;
 243 	GPtrArray *new = g_ptr_array_new ();
Value stored to 'new' during its initialization is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'new' during its initialization is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

244 245 va_start (args, query); 246 247 new = rhythmdb_query_parse_valist (db, args); 248 249 for (i = 0; i < new->len; i++) 250 g_ptr_array_add (query, g_ptr_array_index (new, i)); 251 252 g_ptr_array_free (new, TRUE); 253 254 va_end (args); 255 } 256 257 /** 258 * rhythmdb_query_append_params: 259 * @db: the #RhythmDB 260 * @query: the query to append to 261 * @type: query type 262 * @prop: query property 263 * @value: query value 264 * 265 * Appends a new query term to @query. 266 */ 267 void 268 rhythmdb_query_append_params (RhythmDB *db, GPtrArray *query, 269 RhythmDBQueryType type, RhythmDBPropType prop, const GValue *value) 270 { 271 RhythmDBQueryData *data = g_new0 (RhythmDBQueryData, 1); 272 273 data->type = type; 274 switch (type) { 275 case RHYTHMDB_QUERY_END: 276 g_assert_not_reached (); 277 break; 278 case RHYTHMDB_QUERY_DISJUNCTION: 279 break; 280 case RHYTHMDB_QUERY_SUBQUERY: 281 data->subquery = rhythmdb_query_copy (g_value_get_pointer (value)); 282 break; 283 case RHYTHMDB_QUERY_PROP_EQUALS: 284 case RHYTHMDB_QUERY_PROP_NOT_EQUAL: 285 case RHYTHMDB_QUERY_PROP_LIKE: 286 case RHYTHMDB_QUERY_PROP_NOT_LIKE: 287 case RHYTHMDB_QUERY_PROP_PREFIX: 288 case RHYTHMDB_QUERY_PROP_SUFFIX: 289 case RHYTHMDB_QUERY_PROP_GREATER: 290 case RHYTHMDB_QUERY_PROP_LESS: 291 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN: 292 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN: 293 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS: 294 case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL: 295 case RHYTHMDB_QUERY_PROP_YEAR_GREATER: 296 case RHYTHMDB_QUERY_PROP_YEAR_LESS: 297 data->propid = prop; 298 data->val = g_new0 (GValue, 1); 299 g_value_init (data->val, rhythmdb_get_property_type (db, data->propid)); 300 g_value_copy (value, data->val); 301 break; 302 } 303 304 g_ptr_array_add (query, data); 305 } 306 307 /** 308 * rhythmdb_query_free: 309 * @query: a query. 310 * 311 * Frees the query @query 312 */ 313 void 314 rhythmdb_query_free (GPtrArray *query) 315 { 316 guint i; 317 318 if (query == NULL) 319 return; 320 321 for (i = 0; i < query->len; i++) { 322 RhythmDBQueryData *data = g_ptr_array_index (query, i); 323 switch (data->type) { 324 case RHYTHMDB_QUERY_DISJUNCTION: 325 break; 326 case RHYTHMDB_QUERY_SUBQUERY: 327 rhythmdb_query_free (data->subquery); 328 break; 329 case RHYTHMDB_QUERY_PROP_EQUALS: 330 case RHYTHMDB_QUERY_PROP_NOT_EQUAL: 331 case RHYTHMDB_QUERY_PROP_LIKE: 332 case RHYTHMDB_QUERY_PROP_NOT_LIKE: 333 case RHYTHMDB_QUERY_PROP_PREFIX: 334 case RHYTHMDB_QUERY_PROP_SUFFIX: 335 case RHYTHMDB_QUERY_PROP_GREATER: 336 case RHYTHMDB_QUERY_PROP_LESS: 337 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN: 338 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN: 339 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS: 340 case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL: 341 case RHYTHMDB_QUERY_PROP_YEAR_GREATER: 342 case RHYTHMDB_QUERY_PROP_YEAR_LESS: 343 g_value_unset (data->val); 344 g_free (data->val); 345 break; 346 case RHYTHMDB_QUERY_END: 347 g_assert_not_reached (); 348 break; 349 } 350 g_free (data); 351 } 352 353 g_ptr_array_free (query, TRUE); 354 } 355 356 static char * 357 prop_gvalue_to_string (RhythmDB *db, 358 RhythmDBPropType propid, 359 GValue *val) 360 { 361 /* special-case some properties */ 362 switch (propid) { 363 case RHYTHMDB_PROP_TYPE: 364 { 365 RhythmDBEntryType *type = g_value_get_object (val); 366 return g_strdup (rhythmdb_entry_type_get_name (type)); 367 } 368 break; 369 default: 370 break; 371 } 372 373 /* otherwise just convert numbers to strings */ 374 switch (G_VALUE_TYPE (val)) { 375 case G_TYPE_STRING: 376 return g_value_dup_string (val); 377 case G_TYPE_BOOLEAN: 378 return g_strdup_printf ("%d", g_value_get_boolean (val)); 379 case G_TYPE_INT: 380 return g_strdup_printf ("%d", g_value_get_int (val)); 381 case G_TYPE_LONG: 382 return g_strdup_printf ("%ld", g_value_get_long (val)); 383 case G_TYPE_ULONG: 384 return g_strdup_printf ("%lu", g_value_get_ulong (val)); 385 case G_TYPE_UINT64: 386 return g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (val)); 387 case G_TYPE_FLOAT: 388 return g_strdup_printf ("%f", g_value_get_float (val)); 389 case G_TYPE_DOUBLE: 390 return g_strdup_printf ("%f", g_value_get_double (val)); 391 default: 392 g_assert_not_reached (); 393 return NULL; 394 } 395 } 396 397 static void 398 write_encoded_gvalue (RhythmDB *db, 399 xmlNodePtr node, 400 RhythmDBPropType propid, 401 GValue *val) 402 { 403 char *strval = NULL; 404 xmlChar *quoted; 405 406 strval = prop_gvalue_to_string (db, propid, val); 407 quoted = xmlEncodeEntitiesReentrant (NULL, BAD_CAST strval); 408 g_free (strval); 409 410 xmlNodeSetContent (node, quoted); 411 g_free (quoted); 412 } 413 414 /** 415 * rhythmdb_read_encoded_property: 416 * @db: the #RhythmDB 417 * @content: encoded property value 418 * @propid: property ID 419 * @val: returns the property value 420 * 421 * Converts a string containing a property value into a #GValue 422 * containing the native form of the property value. For boolean 423 * and numeric properties, this converts the string to a number. 424 * For #RHYTHMDB_PROP_TYPE, this looks up the entry type by name. 425 * For strings, no conversion is required. 426 */ 427 void 428 rhythmdb_read_encoded_property (RhythmDB *db, 429 const char *content, 430 RhythmDBPropType propid, 431 GValue *val) 432 { 433 g_value_init (val, rhythmdb_get_property_type (db, propid)); 434 435 switch (G_VALUE_TYPE (val)) { 436 case G_TYPE_STRING: 437 g_value_set_string (val, content); 438 break; 439 case G_TYPE_BOOLEAN: 440 g_value_set_boolean (val, g_ascii_strtoull (content, NULL, 10)); 441 break; 442 case G_TYPE_ULONG: 443 g_value_set_ulong (val, g_ascii_strtoull (content, NULL, 10)); 444 break; 445 case G_TYPE_UINT64: 446 g_value_set_uint64 (val, g_ascii_strtoull (content, NULL, 10)); 447 break; 448 case G_TYPE_DOUBLE: 449 { 450 gdouble d; 451 char *end; 452 453 d = g_ascii_strtod (content, &end); 454 if (*end != '\0') { 455 /* conversion wasn't entirely successful. 456 * try locale-aware strtod(). 457 */ 458 d = strtod (content, NULL); 459 } 460 g_value_set_double (val, d); 461 } 462 break; 463 case G_TYPE_OBJECT: /* hm, really? */ 464 if (propid == RHYTHMDB_PROP_TYPE) { 465 RhythmDBEntryType *entry_type; 466 entry_type = rhythmdb_entry_type_get_by_name (db, content); 467 if (entry_type != NULL) { 468 g_value_set_object (val, entry_type); 469 break; 470 } else { 471 g_warning ("Unexpected entry type"); 472 /* Fall through */ 473 } 474 } 475 /* Falling through on purpose to get an assert for unexpected 476 * cases 477 */ 478 default: 479 g_warning ("Attempt to read '%s' of unhandled type %s", 480 rhythmdb_nice_elt_name_from_propid (db, propid), 481 g_type_name (G_VALUE_TYPE (val))); 482 g_assert_not_reached (); 483 break; 484 } 485 } 486 487 /** 488 * rhythmdb_query_serialize: 489 * @db: the #RhythmDB 490 * @query: query to serialize 491 * @parent: XML node to attach the query to 492 * 493 * Converts @query into XML form as a child of @parent. It can be converted 494 * back into a query by passing @parent to @rhythmdb_query_deserialize. 495 */ 496 void 497 rhythmdb_query_serialize (RhythmDB *db, GPtrArray *query, 498 xmlNodePtr parent) 499 { 500 guint i; 501 xmlNodePtr node = xmlNewChild (parent, NULL, RB_PARSE_CONJ, NULL); 502 xmlNodePtr subnode; 503 504 for (i = 0; i < query->len; i++) { 505 RhythmDBQueryData *data = g_ptr_array_index (query, i); 506 507 switch (data->type) { 508 case RHYTHMDB_QUERY_SUBQUERY: 509 subnode = xmlNewChild (node, NULL, RB_PARSE_SUBQUERY, NULL); 510 rhythmdb_query_serialize (db, data->subquery, subnode); 511 break; 512 case RHYTHMDB_QUERY_PROP_LIKE: 513 subnode = xmlNewChild (node, NULL, RB_PARSE_LIKE, NULL); 514 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 515 write_encoded_gvalue (db, subnode, data->propid, data->val); 516 break; 517 case RHYTHMDB_QUERY_PROP_NOT_LIKE: 518 subnode = xmlNewChild (node, NULL, RB_PARSE_NOT_LIKE, NULL); 519 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 520 write_encoded_gvalue (db, subnode, data->propid, data->val); 521 break; 522 case RHYTHMDB_QUERY_PROP_PREFIX: 523 subnode = xmlNewChild (node, NULL, RB_PARSE_PREFIX, NULL); 524 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 525 write_encoded_gvalue (db, subnode, data->propid, data->val); 526 break; 527 case RHYTHMDB_QUERY_PROP_SUFFIX: 528 subnode = xmlNewChild (node, NULL, RB_PARSE_SUFFIX, NULL); 529 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 530 write_encoded_gvalue (db, subnode, data->propid, data->val); 531 break; 532 case RHYTHMDB_QUERY_PROP_EQUALS: 533 subnode = xmlNewChild (node, NULL, RB_PARSE_EQUALS, NULL); 534 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 535 write_encoded_gvalue (db, subnode, data->propid, data->val); 536 break; 537 case RHYTHMDB_QUERY_PROP_NOT_EQUAL: 538 subnode = xmlNewChild (node, NULL, RB_PARSE_NOT_EQUAL, NULL); 539 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 540 write_encoded_gvalue (db, subnode, data->propid, data->val); 541 break; 542 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS: 543 subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_EQUALS, NULL); 544 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 545 write_encoded_gvalue (db, subnode, data->propid, data->val); 546 break; 547 case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL: 548 subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_NOT_EQUAL, NULL); 549 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 550 write_encoded_gvalue (db, subnode, data->propid, data->val); 551 break; 552 case RHYTHMDB_QUERY_DISJUNCTION: 553 subnode = xmlNewChild (node, NULL, RB_PARSE_DISJ, NULL);
Value stored to 'subnode' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'subnode' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

554 break; 555 case RHYTHMDB_QUERY_END: 556 break; 557 case RHYTHMDB_QUERY_PROP_GREATER: 558 subnode = xmlNewChild (node, NULL, RB_PARSE_GREATER, NULL); 559 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 560 write_encoded_gvalue (db, subnode, data->propid, data->val); 561 break; 562 case RHYTHMDB_QUERY_PROP_YEAR_GREATER: 563 subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_GREATER, NULL); 564 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 565 write_encoded_gvalue (db, subnode, data->propid, data->val); 566 break; 567 case RHYTHMDB_QUERY_PROP_LESS: 568 subnode = xmlNewChild (node, NULL, RB_PARSE_LESS, NULL); 569 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 570 write_encoded_gvalue (db, subnode, data->propid, data->val); 571 break; 572 case RHYTHMDB_QUERY_PROP_YEAR_LESS: 573 subnode = xmlNewChild (node, NULL, RB_PARSE_YEAR_LESS, NULL); 574 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 575 write_encoded_gvalue (db, subnode, data->propid, data->val); 576 break; 577 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN: 578 subnode = xmlNewChild (node, NULL, RB_PARSE_CURRENT_TIME_WITHIN, NULL); 579 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 580 write_encoded_gvalue (db, subnode, data->propid, data->val); 581 break; 582 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN: 583 subnode = xmlNewChild (node, NULL, RB_PARSE_CURRENT_TIME_NOT_WITHIN, NULL); 584 xmlSetProp (subnode, RB_PARSE_PROP, rhythmdb_nice_elt_name_from_propid (db, data->propid)); 585 write_encoded_gvalue (db, subnode, data->propid, data->val); 586 break; 587 } 588 } 589 } 590 591 /** 592 * rhythmdb_query_deserialize: 593 * @db: the #RhythmDB 594 * @parent: parent XML node of serialized query 595 * 596 * Converts a serialized query back into a @GPtrArray query. 597 * 598 * Return value: (transfer full): deserialized query. 599 */ 600 GPtrArray * 601 rhythmdb_query_deserialize (RhythmDB *db, xmlNodePtr parent) 602 { 603 GPtrArray *query = g_ptr_array_new (); 604 xmlNodePtr child; 605 606 g_assert (!xmlStrcmp (parent->name, RB_PARSE_CONJ)); 607 608 for (child = parent->children; child; child = child->next) { 609 RhythmDBQueryData *data; 610 611 if (xmlNodeIsText (child)) 612 continue; 613 614 data = g_new0 (RhythmDBQueryData, 1); 615 616 if (!xmlStrcmp (child->name, RB_PARSE_SUBQUERY)) { 617 xmlNodePtr subquery; 618 data->type = RHYTHMDB_QUERY_SUBQUERY; 619 subquery = child->children; 620 while (xmlNodeIsText (subquery)) 621 subquery = subquery->next; 622 623 data->subquery = rhythmdb_query_deserialize (db, subquery); 624 } else if (!xmlStrcmp (child->name, RB_PARSE_DISJ)) { 625 data->type = RHYTHMDB_QUERY_DISJUNCTION; 626 } else if (!xmlStrcmp (child->name, RB_PARSE_LIKE)) { 627 data->type = RHYTHMDB_QUERY_PROP_LIKE; 628 } else if (!xmlStrcmp (child->name, RB_PARSE_NOT_LIKE)) { 629 data->type = RHYTHMDB_QUERY_PROP_NOT_LIKE; 630 } else if (!xmlStrcmp (child->name, RB_PARSE_PREFIX)) { 631 data->type = RHYTHMDB_QUERY_PROP_PREFIX; 632 } else if (!xmlStrcmp (child->name, RB_PARSE_SUFFIX)) { 633 data->type = RHYTHMDB_QUERY_PROP_SUFFIX; 634 } else if (!xmlStrcmp (child->name, RB_PARSE_EQUALS)) { 635 xmlChar* prop; 636 637 prop = xmlGetProp(child, RB_PARSE_PROP); 638 if (!xmlStrcmp(prop, (xmlChar *)"date")) 639 data->type = RHYTHMDB_QUERY_PROP_YEAR_EQUALS; 640 else 641 data->type = RHYTHMDB_QUERY_PROP_EQUALS; 642 xmlFree (prop); 643 } else if (!xmlStrcmp (child->name, RB_PARSE_NOT_EQUAL)) { 644 xmlChar* prop; 645 646 prop = xmlGetProp(child, RB_PARSE_PROP); 647 if (!xmlStrcmp(prop, (xmlChar *)"date")) 648 data->type = RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL; 649 else 650 data->type = RHYTHMDB_QUERY_PROP_NOT_EQUAL; 651 xmlFree (prop); 652 } else if (!xmlStrcmp (child->name, RB_PARSE_GREATER)) { 653 xmlChar* prop; 654 655 prop = xmlGetProp(child, RB_PARSE_PROP); 656 if (!xmlStrcmp(prop, (xmlChar *)"date")) 657 data->type = RHYTHMDB_QUERY_PROP_YEAR_GREATER; 658 else 659 data->type = RHYTHMDB_QUERY_PROP_GREATER; 660 xmlFree (prop); 661 } else if (!xmlStrcmp (child->name, RB_PARSE_LESS)) { 662 xmlChar* prop; 663 664 prop = xmlGetProp(child, RB_PARSE_PROP); 665 if (!xmlStrcmp(prop, (xmlChar *)"date")) 666 data->type = RHYTHMDB_QUERY_PROP_YEAR_LESS; 667 else 668 data->type = RHYTHMDB_QUERY_PROP_LESS; 669 xmlFree (prop); 670 } else if (!xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_WITHIN)) { 671 data->type = RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN; 672 } else if (!xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_NOT_WITHIN)) { 673 data->type = RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN; 674 } else 675 g_assert_not_reached (); 676 677 if (!xmlStrcmp (child->name, RB_PARSE_LIKE) 678 || !xmlStrcmp (child->name, RB_PARSE_NOT_LIKE) 679 || !xmlStrcmp (child->name, RB_PARSE_PREFIX) 680 || !xmlStrcmp (child->name, RB_PARSE_SUFFIX) 681 || !xmlStrcmp (child->name, RB_PARSE_EQUALS) 682 || !xmlStrcmp (child->name, RB_PARSE_NOT_EQUAL) 683 || !xmlStrcmp (child->name, RB_PARSE_GREATER) 684 || !xmlStrcmp (child->name, RB_PARSE_LESS) 685 || !xmlStrcmp (child->name, RB_PARSE_YEAR_EQUALS) 686 || !xmlStrcmp (child->name, RB_PARSE_YEAR_GREATER) 687 || !xmlStrcmp (child->name, RB_PARSE_YEAR_LESS) 688 || !xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_WITHIN) 689 || !xmlStrcmp (child->name, RB_PARSE_CURRENT_TIME_NOT_WITHIN)) { 690 char *content; 691 xmlChar *propstr = xmlGetProp (child, RB_PARSE_PROP); 692 gint propid = rhythmdb_propid_from_nice_elt_name (db, propstr); 693 g_free (propstr); 694 695 g_assert (propid >= 0 && propid < RHYTHMDB_NUM_PROPERTIES); 696 697 data->propid = propid; 698 data->val = g_new0 (GValue, 1); 699 700 content = (char *)xmlNodeGetContent (child); 701 rhythmdb_read_encoded_property (db, content, data->propid, data->val); 702 g_free (content); 703 } 704 705 g_ptr_array_add (query, data); 706 } 707 708 return query; 709 } 710 711 /* 712 * This is used to "process" queries, before using them. It is mainly used to two things: 713 * 714 * 1) performing expensive data transformations once per query, rather than 715 * once per entry we try to match against. e.g. RHYTHMDB_PROP_SEARCH_MATCH 716 * 717 * 2) defining criteria in terms of other lower-level ones that the db backend 718 * actually implements. e.g. RHYTHMDB_QUERY_YEAR_* 719 */ 720 721 /** 722 * rhythmdb_query_preprocess: 723 * @db: the #RhythmDB 724 * @query: query to preprocess 725 * 726 * Preprocesses a query to prepare it for execution. This has two main 727 * roles: to perform expensive data transformations once per query, rather 728 * than once per entry, and converting criteria to lower-level forms that 729 * are implemented by the database backend. 730 * 731 * For RHYTHMDB_PROP_SEARCH_MATCH, this converts the search terms into 732 * an array of case-folded words. 733 * 734 * When matching against case-folded properties such as 735 * #RHYTHMDB_PROP_TITLE_FOLDED, this case-folds the query value. 736 * 737 * When performing year-based criteria such as #RHYTHMDB_QUERY_PROP_YEAR_LESS, 738 * it converts the year into the Julian date such that a simple numeric 739 * comparison will work. 740 */ 741 void 742 rhythmdb_query_preprocess (RhythmDB *db, GPtrArray *query) 743 { 744 int i; 745 746 if (query == NULL) 747 return; 748 749 for (i = 0; i < query->len; i++) { 750 RhythmDBQueryData *data = g_ptr_array_index (query, i); 751 gboolean restart_criteria = FALSE; 752 753 if (data->subquery) { 754 rhythmdb_query_preprocess (db, data->subquery); 755 } else switch (data->propid) { 756 case RHYTHMDB_PROP_TITLE_FOLDED: 757 case RHYTHMDB_PROP_GENRE_FOLDED: 758 case RHYTHMDB_PROP_ARTIST_FOLDED: 759 case RHYTHMDB_PROP_ALBUM_FOLDED: 760 { 761 /* as we are matching against a folded property, the string needs to also be folded */ 762 const char *orig = g_value_get_string (data->val); 763 char *folded = rb_search_fold (orig); 764 765 g_value_reset (data->val); 766 g_value_take_string (data->val, folded); 767 break; 768 } 769 770 case RHYTHMDB_PROP_SEARCH_MATCH: 771 { 772 const char *orig = g_value_get_string (data->val); 773 char *folded = rb_search_fold (orig); 774 char **words = rb_string_split_words (folded); 775 776 g_free (folded); 777 g_value_unset (data->val); 778 g_value_init (data->val, G_TYPE_STRV); 779 g_value_take_boxed (data->val, words); 780 break; 781 } 782 783 case RHYTHMDB_PROP_DATE: 784 { 785 GDate date = {0,}; 786 gulong search_date; 787 gulong begin; 788 gulong end; 789 gulong year; 790 791 search_date = g_value_get_ulong (data->val); 792 793 /* GDate functions don't handle Year="0", so we need to special case this */ 794 if (search_date != 0) { 795 g_date_set_julian (&date, search_date); 796 year = g_date_get_year (&date); 797 g_date_clear (&date, 1); 798 799 /* get Julian dates for beginning and end of year */ 800 g_date_set_dmy (&date, 1, G_DATE_JANUARY, year); 801 begin = g_date_get_julian (&date); 802 g_date_clear (&date, 1); 803 804 /* and the day before the beginning of the next year */ 805 g_date_set_dmy (&date, 1, G_DATE_JANUARY, year + 1); 806 end = g_date_get_julian (&date) - 1; 807 } else { 808 begin = 0; 809 end = 0; 810 } 811 812 switch (data->type) 813 { 814 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS: 815 restart_criteria = TRUE; 816 data->type = RHYTHMDB_QUERY_SUBQUERY; 817 data->subquery = rhythmdb_query_parse (db, 818 RHYTHMDB_QUERY_PROP_GREATER, data->propid, begin, 819 RHYTHMDB_QUERY_PROP_LESS, data->propid, end, 820 RHYTHMDB_QUERY_END); 821 break; 822 case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL: 823 restart_criteria = TRUE; 824 data->type = RHYTHMDB_QUERY_SUBQUERY; 825 data->subquery = rhythmdb_query_parse (db, 826 RHYTHMDB_QUERY_PROP_LESS, data->propid, begin-1, 827 RHYTHMDB_QUERY_DISJUNCTION, 828 RHYTHMDB_QUERY_PROP_GREATER, data->propid, end+1, 829 RHYTHMDB_QUERY_END); 830 break; 831 832 case RHYTHMDB_QUERY_PROP_YEAR_LESS: 833 restart_criteria = TRUE; 834 data->type = RHYTHMDB_QUERY_PROP_LESS; 835 g_value_set_ulong (data->val, end); 836 break; 837 838 case RHYTHMDB_QUERY_PROP_YEAR_GREATER: 839 restart_criteria = TRUE; 840 data->type = RHYTHMDB_QUERY_PROP_GREATER; 841 g_value_set_ulong (data->val, begin); 842 break; 843 844 default: 845 break; 846 } 847 848 break; 849 } 850 851 default: 852 break; 853 } 854 855 /* re-do this criteria, in case it needs further transformation */ 856 if (restart_criteria) 857 i--; 858 } 859 } 860 861 /** 862 * rhythmdb_query_append_prop_multiple: 863 * @db: the #RhythmDB 864 * @query: the query to append to 865 * @propid: property ID to match 866 * @items: (element-type GObject.Value): #GList of values to match against 867 * 868 * Appends a set of criteria to a query to match against any of the values 869 * listed in @items. 870 */ 871 void 872 rhythmdb_query_append_prop_multiple (RhythmDB *db, GPtrArray *query, RhythmDBPropType propid, GList *items) 873 { 874 GPtrArray *subquery; 875 876 if (items == NULL) 877 return; 878 879 if (items->next == NULL) { 880 rhythmdb_query_append (db, 881 query, 882 RHYTHMDB_QUERY_PROP_EQUALS, 883 propid, 884 items->data, 885 RHYTHMDB_QUERY_END); 886 return; 887 } 888 889 subquery = g_ptr_array_new (); 890 891 rhythmdb_query_append (db, 892 subquery, 893 RHYTHMDB_QUERY_PROP_EQUALS, 894 propid, 895 items->data, 896 RHYTHMDB_QUERY_END); 897 items = items->next; 898 while (items) { 899 rhythmdb_query_append (db, 900 subquery, 901 RHYTHMDB_QUERY_DISJUNCTION, 902 RHYTHMDB_QUERY_PROP_EQUALS, 903 propid, 904 items->data, 905 RHYTHMDB_QUERY_END); 906 items = items->next; 907 } 908 rhythmdb_query_append (db, query, RHYTHMDB_QUERY_SUBQUERY, subquery, 909 RHYTHMDB_QUERY_END); 910 } 911 912 /** 913 * rhythmdb_query_is_time_relative: 914 * @db: the #RhythmDB 915 * @query: the query to check 916 * 917 * Checks if a query contains any time-relative criteria. 918 * 919 * Return value: %TRUE if time-relative criteria found 920 */ 921 gboolean 922 rhythmdb_query_is_time_relative (RhythmDB *db, GPtrArray *query) 923 { 924 int i; 925 if (query == NULL) 926 return FALSE; 927 928 for (i=0; i < query->len; i++) { 929 RhythmDBQueryData *data = g_ptr_array_index (query, i); 930 931 if (data->subquery) { 932 if (rhythmdb_query_is_time_relative (db, data->subquery)) 933 return TRUE; 934 else 935 continue; 936 } 937 938 switch (data->type) { 939 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN: 940 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN: 941 return TRUE; 942 default: 943 break; 944 } 945 } 946 947 return FALSE; 948 } 949 950 /** 951 * rhythmdb_query_to_string: 952 * @db: a #RhythmDB instance 953 * @query: a query. 954 * 955 * Returns a supposedly human-readable form of the query. 956 * This is only intended for debug usage. 957 * 958 * Returns: allocated string form of the query 959 **/ 960 char * 961 rhythmdb_query_to_string (RhythmDB *db, GPtrArray *query) 962 { 963 GString *buf; 964 int i; 965 966 buf = g_string_sized_new (100); 967 for (i = 0; i < query->len; i++) { 968 char *fmt = NULL; 969 RhythmDBQueryData *data = g_ptr_array_index (query, i); 970 971 switch (data->type) { 972 case RHYTHMDB_QUERY_SUBQUERY: 973 { 974 char *s; 975 976 s = rhythmdb_query_to_string (db, data->subquery); 977 g_string_append_printf (buf, "{ %s }", s); 978 g_free (s); 979 } 980 break; 981 case RHYTHMDB_QUERY_PROP_LIKE: 982 fmt = "(%s =~ %s)"; 983 break; 984 case RHYTHMDB_QUERY_PROP_NOT_LIKE: 985 fmt = "(%s !~ %s)"; 986 break; 987 case RHYTHMDB_QUERY_PROP_PREFIX: 988 fmt = "(%s |< %s)"; 989 break; 990 case RHYTHMDB_QUERY_PROP_SUFFIX: 991 fmt = "(%s >| %s)"; 992 break; 993 case RHYTHMDB_QUERY_PROP_EQUALS: 994 fmt = "(%s == %s)"; 995 break; 996 case RHYTHMDB_QUERY_PROP_NOT_EQUAL: 997 fmt = "(%s != %s)"; 998 break; 999 case RHYTHMDB_QUERY_PROP_YEAR_EQUALS: 1000 fmt = "(year(%s) == %s)"; 1001 break; 1002 case RHYTHMDB_QUERY_PROP_YEAR_NOT_EQUAL: 1003 fmt = "(year(%s) != %s)"; 1004 break; 1005 case RHYTHMDB_QUERY_DISJUNCTION: 1006 g_string_append_printf (buf, " || "); 1007 break; 1008 case RHYTHMDB_QUERY_END: 1009 break; 1010 case RHYTHMDB_QUERY_PROP_GREATER: 1011 fmt = "(%s > %s)"; 1012 break; 1013 case RHYTHMDB_QUERY_PROP_YEAR_GREATER: 1014 fmt = "(year(%s) > %s)"; 1015 break; 1016 case RHYTHMDB_QUERY_PROP_LESS: 1017 fmt = "(%s < %s)"; 1018 break; 1019 case RHYTHMDB_QUERY_PROP_YEAR_LESS: 1020 fmt = "(year(%s) < %s)"; 1021 break; 1022 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN: 1023 fmt = "(%s <> %s)"; 1024 break; 1025 case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN: 1026 fmt = "(%s >< %s)"; 1027 break; 1028 } 1029 1030 if (fmt) { 1031 char *value; 1032 1033 value = prop_gvalue_to_string (db, data->propid, data->val); 1034 g_string_append_printf (buf, fmt, 1035 rhythmdb_nice_elt_name_from_propid (db, data->propid), 1036 value); 1037 g_free (value); 1038 fmt = NULL; 1039 } 1040 } 1041 1042 return g_string_free (buf, FALSE); 1043 } 1044 1045 GType 1046 rhythmdb_query_get_type (void) 1047 { 1048 static GType type = 0; 1049 1050 if (G_UNLIKELY (type == 0)) { 1051 type = g_boxed_type_register_static ("RhythmDBQuery", 1052 (GBoxedCopyFunc)rhythmdb_query_copy, 1053 (GBoxedFreeFunc)rhythmdb_query_free); 1054 } 1055 1056 return type; 1057 }