]> git.sesse.net Git - vlc/blob - modules/media_library/sql_update.c
qt: epgview: const correctness
[vlc] / modules / media_library / sql_update.c
1 /*****************************************************************************
2  * sql_update.c: SQL-based media library: all database update functions
3  *****************************************************************************
4  * Copyright (C) 2008-2010 the VideoLAN team and AUTHORS
5  * $Id$
6  *
7  * Authors: Antoine Lejeune <phytos@videolan.org>
8  *          Jean-Philippe André <jpeg@videolan.org>
9  *          Rémi Duraffort <ivoire@videolan.org>
10  *          Adrien Maglo <magsoft@videolan.org>
11  *          Srikanth Raju <srikiraju at gmail dot com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later 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 Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #include "sql_media_library.h"
29
30
31 /**
32  * @brief Generic update in Media Library database
33  *
34  * @param p_ml the media library object
35  * @param selected_type the type of the element we're selecting
36  * @param where the list of ids or uri to change
37  * @param changes list of changes to make in the entries
38  * @return VLC_SUCCESS or VLC_EGENERIC
39  * @note This function is transactional
40  */
41 int Update( media_library_t *p_ml, ml_select_e selected_type,
42         const char* psz_lvalue, ml_ftree_t *where, vlc_array_t *changes )
43 {
44     int i_ret = VLC_EGENERIC;
45     char *psz_query = NULL;
46     char *psz_id_query = NULL;
47     char **pp_results = NULL;
48     int i_rows = 0, i_cols = 0;
49
50     i_ret = BuildUpdate( p_ml, &psz_query, &psz_id_query,
51                          psz_lvalue, selected_type, where, changes );
52
53     if( i_ret != VLC_SUCCESS )
54     {
55         msg_Err(p_ml,"Failed to generate update query" );
56         return i_ret;
57     }
58     i_ret = VLC_EGENERIC;
59
60     Begin( p_ml );
61     if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS )
62     {
63         msg_Err( p_ml, "Couldn't run the generated update query successfully" );
64         goto quitdelete;
65     }
66
67     /* Get the updated IDs to send events! */
68     if( Query( p_ml, &pp_results, &i_rows, &i_cols, psz_id_query )
69             != VLC_SUCCESS )
70         goto quitdelete;
71
72     i_ret = VLC_SUCCESS;
73 quitdelete:
74     if( i_ret != VLC_SUCCESS )
75         Rollback( p_ml );
76     else
77     {
78         Commit( p_ml );
79         if( i_rows > 0 )
80         {
81             for( int i = 0; i < i_rows; i++ )
82             {
83                 var_SetInteger( p_ml, "media-meta-change",
84                         atoi( pp_results[i*i_cols] ) );
85             }
86         }
87     }
88     FreeSQLResult( p_ml, pp_results );
89     free( psz_id_query );
90     free( psz_query );
91     return i_ret;
92 }
93
94 #define SET_STR( a )                                                        \
95 if( !psz_set[i_type] )                                                      \
96 {                                                                           \
97     psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.str ); \
98     if( !psz_set[i_type] )                                                  \
99         goto quit_buildupdate;                                              \
100 }                                                                           \
101 break;
102
103 #define SET_INT( a )                                                        \
104 if( !psz_set[i_type] )                                                      \
105 {                                                                           \
106     psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql, a, find->value.i );   \
107     if( !psz_set[i_type] )                                                  \
108         goto quit_buildupdate;                                              \
109 }                                                                           \
110 break;
111
112 /* TODO: Build smarter updates by using IN () */
113 static int BuildWhere( media_library_t* p_ml, char **ppsz_where, ml_ftree_t *tree )
114 {
115     assert( ppsz_where );
116     char* psz_left = NULL;
117     char* psz_right = NULL;
118     int i_ret = VLC_SUCCESS;
119     switch( tree->op )
120     {
121         case ML_OP_AND:
122         case ML_OP_OR:
123             i_ret = BuildWhere( p_ml, &psz_left, tree->left );
124             if( i_ret != VLC_SUCCESS )
125                 goto quit_buildwhere;
126             i_ret = BuildWhere( p_ml, &psz_right, tree->right );
127             if( i_ret != VLC_SUCCESS )
128                 goto quit_buildwhere;
129             if( psz_left == NULL || psz_right == NULL )
130             {
131                 msg_Err( p_ml, "Couldn't build AND/OR for Update statement" );
132                 i_ret = VLC_EGENERIC;
133                 goto quit_buildwhere;
134             }
135             if( asprintf( ppsz_where, "( %s %s %s )", psz_left,
136                 ( tree->op == ML_OP_AND ? "AND" : "OR" ), psz_right ) == -1 )
137             {
138                 i_ret = VLC_ENOMEM;
139                 goto quit_buildwhere;
140             }
141             break;
142         case ML_OP_NOT:
143             i_ret = BuildWhere( p_ml, &psz_left, tree->left );
144             if( i_ret != VLC_SUCCESS )
145                 goto quit_buildwhere;
146             if( psz_left == NULL )
147             {
148                 msg_Err( p_ml, "Couldn't build NOT for Update statement" );
149                 i_ret = VLC_EGENERIC;
150                 goto quit_buildwhere;
151             }
152             if( asprintf( ppsz_where, "( NOT %s )", psz_left ) == -1 )
153             {
154                 i_ret = VLC_ENOMEM;
155                 goto quit_buildwhere;
156             }
157             break;
158         case ML_OP_SPECIAL:
159             msg_Err( p_ml, "Couldn't build special for Update statement" );
160             break;
161         case ML_OP_NONE:
162             switch( tree->criteria )
163             {
164                 case ML_ID:
165                     assert( tree->comp == ML_COMP_EQUAL );
166                     *ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.id = %d",
167                                                 tree->value.i );
168                     if( *ppsz_where == NULL )
169                         goto quit_buildwhere;
170                     break;
171                 case ML_URI:
172                     assert( tree->comp == ML_COMP_EQUAL );
173                     *ppsz_where = sql_Printf( p_ml->p_sys->p_sql, "media.uri = %q",
174                                                 tree->value.str );
175                     if( *ppsz_where == NULL )
176                         goto quit_buildwhere;
177                     break;
178                 default:
179                     msg_Err( p_ml, "Trying to update with unsupported condition" );
180                     break;
181             }
182     }
183 quit_buildwhere:
184     return i_ret;
185 }
186
187 /**
188  * @brief Generic UPDATE query builder
189  *
190  * @param p_ml This media_library_t object
191  * @param ppsz_query *ppsz_query will contain query for update
192  * @param ppsz_id_query will contain query to get the ids of updated files
193  * @param selected_type the type of the element we're selecting
194  * @param where parse tree of where condition
195  * @param changes list of changes to make in the entries
196  * @return VLC_SUCCESS or VLC_EGENERIC
197  */
198 int BuildUpdate( media_library_t *p_ml,
199                  char **ppsz_query, char **ppsz_id_query,
200                  const char *psz_lvalue,
201                  ml_select_e selected_type,
202                  ml_ftree_t *where, vlc_array_t *changes )
203 {
204     assert( ppsz_query );
205     assert( ppsz_id_query );
206
207     *ppsz_query = NULL;
208     int i_type;
209     int i_index;
210     int i_ret = VLC_ENOMEM;
211
212     char *psz_table = NULL;
213     /* TODO: Hack? */
214     char *psz_set[ ML_DIRECTORY + 1 ] = { NULL };
215     char *psz_fullset = NULL;
216     char *psz_extra = NULL; /*<< For an update to extra table */
217
218     char *psz_where = NULL;
219     char *psz_tmp = NULL;
220
221     int *pi_padd_ids = NULL;
222     int i_people_add = 0;
223
224     int i_album_id = 0;
225     char *psz_album = NULL;
226     char *psz_cover = NULL;
227
228
229     if( !where )
230     {
231         msg_Warn( p_ml, "You're trying to update no rows."
232                "Trying to guess update based on uri" );
233     }
234
235     /* Create the id/uri lists for WHERE part of the query */
236     i_ret = BuildWhere( p_ml, &psz_where, where );
237     if( i_ret != VLC_SUCCESS )
238         goto quit_buildupdate;
239     i_ret = VLC_ENOMEM;
240
241     /** Firstly, choose the right table */
242     switch( selected_type )
243     {
244         case ML_ALBUM:
245             psz_table = strdup( "album" );
246             break;
247         case ML_PEOPLE:
248             psz_table = strdup( "people" );
249             break;
250         case ML_MEDIA:
251             psz_table = strdup( "media" );
252             break;
253         default:
254             msg_Err( p_ml, "Not a valid element to Update!" );
255             i_ret = VLC_EGENERIC;
256             goto quit_buildupdate;
257             break;
258     }
259
260     if( !psz_table )
261         return VLC_ENOMEM;
262
263     /** Secondly, build the SET part of the query */
264     for( i_index = 0; i_index < vlc_array_count( changes ); i_index++ )
265     {
266         ml_element_t *find = ( ml_element_t * )
267                 vlc_array_item_at_index( changes, i_index );
268         i_type = find->criteria;
269
270         switch( i_type )
271         {
272         case ML_ALBUM:
273             if( selected_type == ML_ALBUM )
274             {
275                 if( !psz_set[i_type] )
276                 {
277                     psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
278                                                   "title = %Q",
279                                                   find->value.str );
280                     if( !psz_set[i_type] )
281                     {
282                         msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
283                                 __FILE__, __LINE__ );
284                         goto quit_buildupdate;
285                     }
286                 }
287             }
288             else if( selected_type == ML_MEDIA )
289             {
290                 if( !psz_album )
291                     psz_album = find->value.str;
292             }
293             else
294                 assert( 0 );
295             break;
296         case ML_ALBUM_ID:
297             assert( selected_type != ML_ALBUM );
298             if( selected_type == ML_MEDIA )
299             {
300                 if( i_album_id <= 0 )
301                 {
302                     i_album_id = find->value.i;
303                     if( !psz_set[i_type] )
304                     {
305                         psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
306                                                       "album_id = '%d'",
307                                                       find->value.i );
308                         if( !psz_set[i_type] )
309                         {
310                             msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
311                                 __FILE__, __LINE__ );
312                             goto quit_buildupdate;
313                         }
314                     }
315                 }
316             }
317             break;
318         case ML_PEOPLE:
319             if( selected_type == ML_MEDIA )
320             {
321                 pi_padd_ids = (int*) realloc( pi_padd_ids , ( ++i_people_add * sizeof(int) ) );
322                 pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
323                                             find->lvalue.str, ML_PEOPLE, find->lvalue.str,
324                                             find->value.str );
325                 if( pi_padd_ids[ i_people_add - 1 ] <= 0 )
326                 {
327                     AddPeople( p_ml, find->value.str, find->lvalue.str );
328                     pi_padd_ids[ i_people_add - 1 ] = ml_GetInt( p_ml, ML_PEOPLE_ID,
329                                             find->lvalue.str, ML_PEOPLE, find->lvalue.str,
330                                             find->value.str );
331                 }
332             }
333             else if( strcmp( psz_lvalue, find->lvalue.str ) )
334             {
335                 msg_Err( p_ml, "Trying to update a different person type" );
336                 return VLC_EGENERIC;
337             }
338             else
339             {
340                 if( !psz_set[i_type] ) psz_set[i_type] =
341                     sql_Printf( p_ml->p_sys->p_sql, "name = %Q", find->value.str );
342             }
343             break;
344         case ML_PEOPLE_ID:
345             /* TODO: Implement smarter updates for this case? */
346             assert( selected_type == ML_MEDIA );
347             if( selected_type == ML_MEDIA )
348             {
349                 bool b_update = true;
350                 for( int i = 0; i < i_people_add; i++ )
351                 {
352                    if( pi_padd_ids[ i ] == find->value.i )
353                    {
354                       b_update = false;
355                       break;
356                    }
357                 }
358                 if( b_update )
359                 {
360                     pi_padd_ids = (int *)realloc( pi_padd_ids, ( ++i_people_add * sizeof(int) ) );
361                     pi_padd_ids[ i_people_add - 1 ] = find->value.i;
362                 }
363             }
364             break;
365         case ML_PEOPLE_ROLE:
366             msg_Dbg( p_ml, "Can't update role" );
367             break;
368         case ML_COMMENT:
369             assert( selected_type == ML_MEDIA );
370             SET_STR( "comment = %Q" );
371         case ML_COVER:
372             assert( selected_type == ML_ALBUM || selected_type == ML_MEDIA );
373             psz_cover = find->value.str;
374             SET_STR( "cover = %Q" );
375         case ML_DISC_NUMBER:
376             assert( selected_type == ML_MEDIA );
377             SET_INT( "disc = '%d'" );
378         case ML_DURATION:
379             assert( selected_type == ML_MEDIA );
380             SET_INT( "duration = '%d'" );
381         case ML_EXTRA:
382             assert( selected_type == ML_MEDIA );
383             SET_STR( "extra = %Q" );
384         case ML_FIRST_PLAYED:
385             assert( selected_type == ML_MEDIA );
386             SET_INT( "first_played =='%d'" );
387         case ML_GENRE:
388             assert( selected_type == ML_MEDIA );
389             SET_STR( "genre = %Q" );
390         /* ID cannot be updated */
391         /* Import time can't be updated */
392         case ML_LAST_PLAYED:
393             assert( selected_type == ML_MEDIA );
394             SET_INT( "last_played = '%d'" );
395         case ML_ORIGINAL_TITLE:
396             assert( selected_type == ML_MEDIA );
397             SET_STR( "original_title = %Q" );
398         case ML_PLAYED_COUNT:
399             assert( selected_type == ML_MEDIA );
400             SET_INT( "played_count = '%d'" );
401         case ML_PREVIEW:
402             assert( selected_type == ML_MEDIA );
403             SET_STR( "preview = %Q" );
404         case ML_SKIPPED_COUNT:
405             assert( selected_type == ML_MEDIA );
406             SET_INT( "skipped_count = '%d'" );
407         case ML_SCORE:
408             assert( selected_type == ML_MEDIA );
409             SET_INT( "score = '%d'" );
410         case ML_TITLE:
411             assert( selected_type == ML_MEDIA );
412             SET_STR( "title = %Q" );
413         case ML_TRACK_NUMBER:
414             assert( selected_type == ML_MEDIA );
415             SET_INT( "track = '%d'" );
416         case ML_TYPE:
417             assert( selected_type == ML_MEDIA );
418             if( !psz_set[i_type] ) psz_set[i_type] =
419                 sql_Printf( p_ml->p_sys->p_sql, "type = '%d'", find->value.i );
420             break;
421         case ML_URI:
422             assert( selected_type == ML_MEDIA );
423             if( !psz_set[i_type] )
424             {
425                 psz_set[i_type] = sql_Printf( p_ml->p_sys->p_sql,
426                                               "uri = %Q",
427                                               find->value.str );
428             }
429             break;
430         case ML_VOTE:
431             assert( selected_type == ML_MEDIA );
432             SET_INT( "vote = '%d'" );
433         case ML_YEAR:
434             assert( selected_type == ML_MEDIA );
435             SET_INT( "year = '%d'" );
436         case ML_END:
437             goto exitfor;
438         default:
439             msg_Warn( p_ml, "Invalid type for update : %d", i_type );
440         }
441     }
442 exitfor:
443
444     /* TODO: Album artist. Verify albumart */
445     if( i_album_id <= 0 || ( psz_album && *psz_album ) )
446     {
447         i_album_id = ml_GetAlbumId( p_ml, psz_album );
448         if( i_album_id < 0 ) //0 is Unknown
449         {
450             i_ret = AddAlbum( p_ml, psz_album, psz_cover, 0 );
451             if( i_ret != VLC_SUCCESS )
452             {
453
454                 msg_Err( p_ml, "Couldn't AddAlbum at BuildUpdate():(%s, %d)",
455                         __FILE__, __LINE__ );
456                 goto quit_buildupdate;
457             }
458             i_album_id = ml_GetAlbumId( p_ml, psz_album );
459             if( i_album_id < 0 )
460                 goto quit_buildupdate;
461         }
462         psz_set[ML_ALBUM_ID] = sql_Printf( p_ml->p_sys->p_sql,
463                                       "album_id = '%d'", i_album_id );
464         if( !psz_set[ML_ALBUM_ID] )
465         {
466             msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
467                     __FILE__, __LINE__ );
468             goto quit_buildupdate;
469         }
470     }
471
472     for( unsigned i = 0; i <= ML_DIRECTORY; i++ )
473     {
474         if( psz_set[i] )
475         {
476             if( i == ML_EXTRA || i == ML_LANGUAGE )
477             {
478                 free( psz_tmp );
479                 if( asprintf( &psz_tmp, "%s%s%s", psz_extra ? psz_extra : "",
480                                psz_extra ? ", ": "",  psz_set[i] ) == -1 )
481                 {
482                     msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
483                             __FILE__, __LINE__ );
484                     goto quit_buildupdate;
485                 }
486                 free( psz_extra );
487                 psz_extra = strdup( psz_tmp );
488             }
489             else
490             {
491                 free( psz_tmp );
492                 if( asprintf( &psz_tmp, "%s%s%s", psz_fullset ? psz_fullset : "",
493                                psz_fullset ? ", ": "",  psz_set[i] ) == -1 )
494                 {
495                     msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
496                             __FILE__, __LINE__ );
497                     goto quit_buildupdate;
498                 }
499                 free( psz_fullset );
500                 psz_fullset = strdup( psz_tmp );
501             }
502         }
503     }
504     i_ret = VLC_SUCCESS;
505
506     /** Now build the right WHERE condition */
507     assert( psz_where && *psz_where );
508
509     /** Finally build the full query */
510     /** Pass if we have some people to add - Indirect update */
511     if( !psz_fullset && i_people_add == 0 )
512     {
513         i_ret = VLC_EGENERIC;
514         msg_Err( p_ml, "Nothing found to create update at BuildUpdate():(%s, %d)",
515                         __FILE__, __LINE__ );
516         goto quit_buildupdate;
517     }
518
519     if( psz_fullset ){
520         if( asprintf( ppsz_query, "UPDATE %s SET %s WHERE %s", psz_table,
521                       psz_fullset, psz_where ) == -1 )
522         {
523             msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
524                             __FILE__, __LINE__ );
525             goto quit_buildupdate;
526         }
527     }
528
529     if( selected_type == ML_MEDIA )
530     {
531         if( psz_extra )
532         {
533             if( asprintf( &psz_tmp, "%s; UPDATE extra SET %s WHERE %s",
534                         *ppsz_query, psz_extra, psz_where ) == -1 )
535             {
536                 msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
537                             __FILE__, __LINE__ );
538                 goto quit_buildupdate;
539             }
540             free( *ppsz_query );
541             *ppsz_query = psz_tmp;
542             psz_tmp = NULL;
543         }
544         char* psz_idstring = NULL;
545         if( i_people_add > 0 )
546         {
547             for( int i = 0; i < i_people_add; i++ )
548             {
549                 if( asprintf( &psz_tmp, "%s%s%d", psz_idstring == NULL? "" : psz_idstring,
550                             psz_idstring == NULL ? "" : ",", pi_padd_ids[i] ) == -1 )
551                 {
552                     free( psz_tmp );
553                     free( psz_idstring );
554                     msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
555                             __FILE__, __LINE__ );
556                     goto quit_buildupdate;
557                 }
558                 free( psz_idstring );
559                 psz_idstring = psz_tmp;
560                 psz_tmp = NULL;
561             }
562             /* Delete all connections with people whom we will update now! */
563             if( asprintf( &psz_tmp, "%s;DELETE FROM media_to_people WHERE EXISTS "
564                     "(SELECT media.id, people.id FROM media JOIN media_to_people "
565                     "AS temp ON media.id = temp.media_id "
566                     "JOIN people ON temp.people_id = people.id "
567                     "WHERE %s AND people.role IN "
568                     "(SELECT people.role FROM people WHERE people.id IN (%s)) "
569                     "AND people.id NOT IN (%s) "
570                     "AND temp.media_id = media_to_people.media_id AND "
571                     "temp.people_id = media_to_people.people_id )",
572                     *ppsz_query == NULL ? "": *ppsz_query, psz_where,
573                     psz_idstring, psz_idstring ) == -1 )
574             {
575                 free( psz_idstring );
576                 msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
577                                 __FILE__, __LINE__ );
578                 goto quit_buildupdate;
579             }
580             free( *ppsz_query );
581             *ppsz_query = psz_tmp;
582             psz_tmp = NULL;
583             free( psz_idstring );
584         }
585         for( int i = 0; i < i_people_add ; i++ )
586         {
587             if( pi_padd_ids[i] > 0 )
588             {
589                 /* OR IGNORE will avoid errors from collisions from old media
590                  * Perhaps this hack can be fixed...FIXME */
591                 if( asprintf( &psz_tmp, "%s;INSERT OR IGNORE into media_to_people "
592                 "(media_id,people_id) SELECT media.id, %d FROM media WHERE %s",
593                 *ppsz_query == NULL ? "" : *ppsz_query, pi_padd_ids[i],
594                 psz_where ) == -1 )
595                 {
596                     msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
597                                     __FILE__, __LINE__ );
598                     goto quit_buildupdate;
599                 }
600                 FREENULL( *ppsz_query );
601                 *ppsz_query = psz_tmp;
602                 psz_tmp = NULL;
603             }
604         }
605     }
606
607     if( asprintf( ppsz_id_query, "SELECT id AS %s_id FROM %s WHERE %s",
608                 psz_table, psz_table, psz_where ) == -1 )
609     {
610         msg_Err( p_ml, "Couldn't create string at BuildUpdate():(%s, %d)",
611                         __FILE__, __LINE__ );
612         goto quit_buildupdate;
613     }
614 #ifndef NDEBUG
615     msg_Dbg( p_ml, "updated media where %s", psz_where );
616 #endif
617     goto quit_buildupdate_success;
618
619 quit_buildupdate:
620     msg_Warn( p_ml, "BuildUpdate() could not generate update sql query" );
621 quit_buildupdate_success:
622     free( psz_tmp );
623     free( psz_table );
624     free( psz_fullset );
625     free( psz_extra );
626     free( pi_padd_ids );
627     for( int i = 0; i <= ML_DIRECTORY; i++ )
628         free( psz_set[ i ] );
629
630     return i_ret;
631 }
632
633 #undef SET_STR
634 #undef SET_INT
635
636 /**
637  * @brief Update a ml_media_t
638  *
639  * @param p_ml the media library object
640  * @param p_media media to synchronise in the database
641  * @return VLC_SUCCESS or VLC_EGENERIC
642  * @note: the media id may be 0, in this case, the update is based
643  *        on the url (less powerful). This function is threadsafe
644  *
645  * This synchronises all non NULL and non zero fields of p_media
646  * Synchronization of album and people is TODO
647  */
648 int UpdateMedia( media_library_t *p_ml, ml_media_t *p_media )
649 {
650     assert( p_media->i_id || ( p_media->psz_uri && *p_media->psz_uri ) );
651     vlc_array_t *changes = vlc_array_new();
652     ml_element_t *find = NULL;
653     int i_ret = VLC_EGENERIC;
654
655     ml_LockMedia( p_media );
656 #define APPEND_ICHANGES( cond, crit ) \
657     if( cond ) { \
658         find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); \
659         find->criteria = crit; \
660         find->value.i = cond; \
661         vlc_array_append( changes, find ); \
662     }
663 #define APPEND_SCHANGES( cond, crit ) \
664     if( cond ) { \
665         find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); \
666         find->criteria = crit; \
667         find->value.str = cond; \
668         vlc_array_append( changes, find ); \
669     }
670
671     APPEND_SCHANGES( p_media->psz_title, ML_TITLE );
672     APPEND_ICHANGES( p_media->i_type, ML_TYPE );
673     APPEND_ICHANGES( p_media->i_duration, ML_DURATION );
674     APPEND_SCHANGES( p_media->psz_preview, ML_PREVIEW );
675     APPEND_SCHANGES( p_media->psz_cover, ML_COVER );
676     APPEND_ICHANGES( p_media->i_disc_number, ML_DISC_NUMBER );
677     APPEND_ICHANGES( p_media->i_track_number, ML_TRACK_NUMBER );
678     APPEND_ICHANGES( p_media->i_year, ML_YEAR);
679     APPEND_SCHANGES( p_media->psz_genre, ML_GENRE );
680     APPEND_ICHANGES( p_media->i_album_id, ML_ALBUM_ID );
681     APPEND_SCHANGES( p_media->psz_album, ML_ALBUM );
682     APPEND_ICHANGES( p_media->i_skipped_count, ML_SKIPPED_COUNT );
683     APPEND_ICHANGES( p_media->i_last_skipped, ML_LAST_SKIPPED );
684     APPEND_ICHANGES( p_media->i_played_count, ML_PLAYED_COUNT );
685     APPEND_ICHANGES( p_media->i_last_played, ML_LAST_PLAYED );
686     APPEND_ICHANGES( p_media->i_first_played, ML_FIRST_PLAYED );
687     APPEND_ICHANGES( p_media->i_vote, ML_VOTE );
688     APPEND_ICHANGES( p_media->i_score, ML_SCORE );
689     APPEND_SCHANGES( p_media->psz_comment, ML_COMMENT );
690     APPEND_SCHANGES( p_media->psz_extra, ML_EXTRA );
691     APPEND_SCHANGES( p_media->psz_language, ML_LANGUAGE );
692
693     if( p_media->psz_uri && p_media->i_id )
694     {
695         find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
696         find->criteria = ML_URI;
697         find->value.str = p_media->psz_uri;
698         vlc_array_append( changes, find );
699     }
700     /*TODO: implement extended meta */
701     /* We're not taking import time! Good */
702
703 #undef APPEND_ICHANGES
704 #undef APPEND_SCHANGES
705     ml_person_t* person = p_media->p_people;
706     while( person )
707     {
708         if( person->i_id > 0 )
709         {
710             find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
711             find->criteria = ML_PEOPLE_ID;
712             find->lvalue.str = person->psz_role;
713             find->value.i = person->i_id;
714             vlc_array_append( changes, find );
715         }
716         else if( person->psz_name && *person->psz_name )
717         {
718             find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) );
719             find->criteria = ML_PEOPLE;
720             find->lvalue.str = person->psz_role;
721             find->value.str = person->psz_name;
722             vlc_array_append( changes, find );
723         }
724         person = person->p_next;
725     }
726
727     ml_ftree_t* p_where = NULL;
728     ml_ftree_t* p_where_elt = ( ml_ftree_t* ) calloc( 1, sizeof( ml_ftree_t ) );
729     if( p_media->i_id )
730     {
731         p_where_elt->criteria = ML_ID;
732         p_where_elt->value.i = p_media->i_id ;
733         p_where_elt->comp = ML_COMP_EQUAL;
734         p_where = ml_FtreeFastAnd( p_where, p_where_elt );
735     }
736     else if( p_media->psz_uri )
737     {
738         p_where_elt->criteria = ML_URI;
739         p_where_elt->value.str = p_media->psz_uri;
740         p_where_elt->comp = ML_COMP_EQUAL;
741         p_where = ml_FtreeFastAnd( p_where, p_where_elt );
742     }
743     else
744     {
745         goto quit1;
746     }
747     i_ret = Update( p_ml, ML_MEDIA, NULL, p_where, changes );
748
749 quit1:
750     ml_FreeFindTree( p_where );
751     for( int i = 0; i < vlc_array_count( changes ); i++ )
752         /* Note: DO NOT free the strings because
753          * they belong to the ml_media_t object */
754         free( vlc_array_item_at_index( changes, i ) );
755     vlc_array_destroy( changes );
756     ml_UnlockMedia( p_media );
757     return i_ret;
758 }
759
760 /**
761  * @brief Update an album's cover art
762  * @param p_ml The Media Library
763  * @param i_album_id Album's ID
764  * @param psz_cover New cover art
765  * @return VLC success/error code
766  **/
767 int SetArtCover( media_library_t *p_ml,
768                    int i_album_id, const char *psz_cover )
769 {
770     assert( i_album_id != 0 );
771     assert( psz_cover != NULL );
772
773     char *psz_query = sql_Printf( p_ml->p_sys->p_sql,
774               "UPDATE album SET cover = %Q WHERE id = '%d'",
775               psz_cover, i_album_id );
776
777     if( !psz_query )
778         return VLC_ENOMEM;
779
780     if( QuerySimple( p_ml, "%s", psz_query ) != VLC_SUCCESS )
781     {
782         msg_Warn( p_ml, "Could not update the album's cover art "
783                 "(Database error)" );
784         free( psz_query );
785         return VLC_EGENERIC;
786     }
787
788     free( psz_query );
789     return VLC_SUCCESS;
790 }