2009-06-07 12:56:35 -04:00
/*
2010-01-05 13:34:00 -05:00
* Copyright ( C ) 2009 - 2010 Julien BLACHE < jb @ jblache . org >
2009-06-07 12:56:35 -04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
# include <stdint.h>
# include <inttypes.h>
# include <pthread.h>
# include <sqlite3.h>
# include "logger.h"
2010-01-04 12:00:40 -05:00
# include "daap_query.h"
2009-06-07 12:56:35 -04:00
# include "db.h"
# define STR(x) ((x) ? (x) : "")
2009-06-12 05:09:58 -04:00
# define DB_PATH STATEDIR " / cache / " PACKAGE " / songs3.db"
2009-06-07 12:56:35 -04:00
2009-06-10 12:11:11 -04:00
/* Inotify cookies are uint32_t */
# define INOTIFY_FAKE_COOKIE ((int64_t)1 << 32)
2009-06-07 12:56:35 -04:00
# define DB_TYPE_CHAR 1
# define DB_TYPE_INT 2
# define DB_TYPE_INT64 3
# define DB_TYPE_STRING 4
struct col_type_map {
ssize_t offset ;
short type ;
} ;
/* This list must be kept in sync with
2009-06-11 17:03:53 -04:00
* - the order of the columns in the files table
2009-06-07 12:56:35 -04:00
* - the type and name of the fields in struct media_file_info
*/
static struct col_type_map mfi_cols_map [ ] =
{
2009-12-26 02:44:55 -05:00
{ mfi_offsetof ( id ) , DB_TYPE_INT } ,
{ mfi_offsetof ( path ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( fname ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( title ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( artist ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( album ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( genre ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( comment ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( type ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( composer ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( orchestra ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( conductor ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( grouping ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( url ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( bitrate ) , DB_TYPE_INT } ,
{ mfi_offsetof ( samplerate ) , DB_TYPE_INT } ,
{ mfi_offsetof ( song_length ) , DB_TYPE_INT } ,
{ mfi_offsetof ( file_size ) , DB_TYPE_INT64 } ,
{ mfi_offsetof ( year ) , DB_TYPE_INT } ,
{ mfi_offsetof ( track ) , DB_TYPE_INT } ,
{ mfi_offsetof ( total_tracks ) , DB_TYPE_INT } ,
{ mfi_offsetof ( disc ) , DB_TYPE_INT } ,
{ mfi_offsetof ( total_discs ) , DB_TYPE_INT } ,
{ mfi_offsetof ( bpm ) , DB_TYPE_INT } ,
{ mfi_offsetof ( compilation ) , DB_TYPE_CHAR } ,
{ mfi_offsetof ( rating ) , DB_TYPE_INT } ,
{ mfi_offsetof ( play_count ) , DB_TYPE_INT } ,
{ mfi_offsetof ( data_kind ) , DB_TYPE_INT } ,
{ mfi_offsetof ( item_kind ) , DB_TYPE_INT } ,
{ mfi_offsetof ( description ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( time_added ) , DB_TYPE_INT } ,
{ mfi_offsetof ( time_modified ) , DB_TYPE_INT } ,
{ mfi_offsetof ( time_played ) , DB_TYPE_INT } ,
{ mfi_offsetof ( db_timestamp ) , DB_TYPE_INT } ,
{ mfi_offsetof ( disabled ) , DB_TYPE_INT } ,
{ mfi_offsetof ( sample_count ) , DB_TYPE_INT64 } ,
{ mfi_offsetof ( codectype ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( index ) , DB_TYPE_INT } ,
{ mfi_offsetof ( has_video ) , DB_TYPE_INT } ,
{ mfi_offsetof ( contentrating ) , DB_TYPE_INT } ,
{ mfi_offsetof ( bits_per_sample ) , DB_TYPE_INT } ,
{ mfi_offsetof ( album_artist ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( media_kind ) , DB_TYPE_INT } ,
{ mfi_offsetof ( tv_series_name ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( tv_episode_num_str ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( tv_network_name ) , DB_TYPE_STRING } ,
{ mfi_offsetof ( tv_episode_sort ) , DB_TYPE_INT } ,
{ mfi_offsetof ( tv_season_num ) , DB_TYPE_INT } ,
2009-06-07 12:56:35 -04:00
} ;
/* This list must be kept in sync with
* - the order of the columns in the playlists table
* - the type and name of the fields in struct playlist_info
*/
static struct col_type_map pli_cols_map [ ] =
{
{ pli_offsetof ( id ) , DB_TYPE_INT } ,
{ pli_offsetof ( title ) , DB_TYPE_STRING } ,
{ pli_offsetof ( type ) , DB_TYPE_INT } ,
{ pli_offsetof ( query ) , DB_TYPE_STRING } ,
{ pli_offsetof ( db_timestamp ) , DB_TYPE_INT } ,
2009-06-10 09:23:02 -04:00
{ pli_offsetof ( disabled ) , DB_TYPE_INT } ,
2009-06-07 12:56:35 -04:00
{ pli_offsetof ( path ) , DB_TYPE_STRING } ,
{ pli_offsetof ( index ) , DB_TYPE_INT } ,
2009-06-11 12:28:01 -04:00
/* items is computed on the fly */
2009-06-07 12:56:35 -04:00
} ;
/* This list must be kept in sync with
2009-06-11 17:03:53 -04:00
* - the order of the columns in the files table
2009-06-07 12:56:35 -04:00
* - the name of the fields in struct db_media_file_info
*/
static ssize_t dbmfi_cols_map [ ] =
{
dbmfi_offsetof ( id ) ,
dbmfi_offsetof ( path ) ,
dbmfi_offsetof ( fname ) ,
dbmfi_offsetof ( title ) ,
dbmfi_offsetof ( artist ) ,
dbmfi_offsetof ( album ) ,
dbmfi_offsetof ( genre ) ,
dbmfi_offsetof ( comment ) ,
dbmfi_offsetof ( type ) ,
dbmfi_offsetof ( composer ) ,
dbmfi_offsetof ( orchestra ) ,
dbmfi_offsetof ( conductor ) ,
dbmfi_offsetof ( grouping ) ,
dbmfi_offsetof ( url ) ,
dbmfi_offsetof ( bitrate ) ,
dbmfi_offsetof ( samplerate ) ,
dbmfi_offsetof ( song_length ) ,
dbmfi_offsetof ( file_size ) ,
dbmfi_offsetof ( year ) ,
dbmfi_offsetof ( track ) ,
dbmfi_offsetof ( total_tracks ) ,
dbmfi_offsetof ( disc ) ,
dbmfi_offsetof ( total_discs ) ,
dbmfi_offsetof ( bpm ) ,
dbmfi_offsetof ( compilation ) ,
dbmfi_offsetof ( rating ) ,
dbmfi_offsetof ( play_count ) ,
dbmfi_offsetof ( data_kind ) ,
dbmfi_offsetof ( item_kind ) ,
dbmfi_offsetof ( description ) ,
dbmfi_offsetof ( time_added ) ,
dbmfi_offsetof ( time_modified ) ,
dbmfi_offsetof ( time_played ) ,
dbmfi_offsetof ( db_timestamp ) ,
dbmfi_offsetof ( disabled ) ,
dbmfi_offsetof ( sample_count ) ,
dbmfi_offsetof ( codectype ) ,
dbmfi_offsetof ( idx ) ,
dbmfi_offsetof ( has_video ) ,
dbmfi_offsetof ( contentrating ) ,
dbmfi_offsetof ( bits_per_sample ) ,
dbmfi_offsetof ( album_artist ) ,
2009-12-26 02:39:39 -05:00
dbmfi_offsetof ( media_kind ) ,
dbmfi_offsetof ( tv_series_name ) ,
dbmfi_offsetof ( tv_episode_num_str ) ,
dbmfi_offsetof ( tv_network_name ) ,
dbmfi_offsetof ( tv_episode_sort ) ,
dbmfi_offsetof ( tv_season_num ) ,
2009-06-07 12:56:35 -04:00
} ;
/* This list must be kept in sync with
* - the order of the columns in the playlists table
* - the name of the fields in struct playlist_info
*/
static ssize_t dbpli_cols_map [ ] =
{
dbpli_offsetof ( id ) ,
dbpli_offsetof ( title ) ,
dbpli_offsetof ( type ) ,
dbpli_offsetof ( query ) ,
dbpli_offsetof ( db_timestamp ) ,
2009-06-10 09:23:02 -04:00
dbpli_offsetof ( disabled ) ,
2009-06-07 12:56:35 -04:00
dbpli_offsetof ( path ) ,
dbpli_offsetof ( index ) ,
2009-06-11 12:28:01 -04:00
/* items is computed on the fly */
2009-06-07 12:56:35 -04:00
} ;
2010-01-10 06:26:48 -05:00
/* This list must be kept in sync with
* - the order of fields in the Q_GROUPS query
* - the name of the fields in struct group_info
*/
static ssize_t dbgri_cols_map [ ] =
{
dbgri_offsetof ( itemcount ) ,
dbgri_offsetof ( persistentid ) ,
dbgri_offsetof ( songalbumartist ) ,
dbgri_offsetof ( itemname ) ,
} ;
2009-06-10 12:11:11 -04:00
/* This list must be kept in sync with
* - the order of the columns in the inotify table
* - the name and type of the fields in struct watch_info
*/
static struct col_type_map wi_cols_map [ ] =
{
2009-12-26 02:44:55 -05:00
{ wi_offsetof ( wd ) , DB_TYPE_INT } ,
2009-06-10 12:11:11 -04:00
{ wi_offsetof ( cookie ) , DB_TYPE_INT } ,
2009-12-26 02:44:55 -05:00
{ wi_offsetof ( path ) , DB_TYPE_STRING } ,
2009-06-10 12:11:11 -04:00
{ wi_offsetof ( libidx ) , DB_TYPE_INT } ,
} ;
2009-06-07 12:56:35 -04:00
static __thread sqlite3 * hdl ;
2009-06-11 12:28:01 -04:00
/* Forward */
static int
db_pl_count_items ( int id ) ;
2009-06-07 12:56:35 -04:00
char *
db_escape_string ( const char * str )
{
char * escaped ;
char * ret ;
escaped = sqlite3_mprintf ( " %q " , str ) ;
if ( ! escaped )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for escaped string \n " ) ;
return NULL ;
}
ret = strdup ( escaped ) ;
sqlite3_free ( escaped ) ;
return ret ;
}
void
free_mfi ( struct media_file_info * mfi , int content_only )
{
if ( mfi - > path )
free ( mfi - > path ) ;
if ( mfi - > fname )
free ( mfi - > fname ) ;
if ( mfi - > title )
free ( mfi - > title ) ;
if ( mfi - > artist )
free ( mfi - > artist ) ;
if ( mfi - > album )
free ( mfi - > album ) ;
if ( mfi - > genre )
free ( mfi - > genre ) ;
if ( mfi - > comment )
free ( mfi - > comment ) ;
if ( mfi - > type )
free ( mfi - > type ) ;
if ( mfi - > composer )
free ( mfi - > composer ) ;
if ( mfi - > orchestra )
free ( mfi - > orchestra ) ;
if ( mfi - > conductor )
free ( mfi - > conductor ) ;
if ( mfi - > grouping )
free ( mfi - > grouping ) ;
if ( mfi - > description )
free ( mfi - > description ) ;
if ( mfi - > codectype )
free ( mfi - > codectype ) ;
if ( mfi - > album_artist )
free ( mfi - > album_artist ) ;
2009-12-26 02:28:28 -05:00
if ( mfi - > tv_series_name )
free ( mfi - > tv_series_name ) ;
if ( mfi - > tv_episode_num_str )
free ( mfi - > tv_episode_num_str ) ;
if ( mfi - > tv_network_name )
free ( mfi - > tv_network_name ) ;
2009-06-07 12:56:35 -04:00
if ( ! content_only )
free ( mfi ) ;
}
void
free_pli ( struct playlist_info * pli , int content_only )
{
if ( pli - > title )
free ( pli - > title ) ;
if ( pli - > query )
free ( pli - > query ) ;
if ( pli - > path )
free ( pli - > path ) ;
if ( ! content_only )
free ( pli ) ;
}
2009-06-07 14:49:13 -04:00
void
db_purge_cruft ( time_t ref )
{
char * errmsg ;
int i ;
int ret ;
2009-06-11 14:27:31 -04:00
char * queries [ 3 ] = { NULL , NULL , NULL } ;
char * queries_tmpl [ 3 ] =
2009-06-07 14:49:13 -04:00
{
" DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists WHERE id <> 1 AND db_timestamp < % " PRIi64 " ); " ,
" DELETE FROM playlists WHERE id <> 1 AND db_timestamp < % " PRIi64 " ; " ,
2009-06-11 17:03:53 -04:00
" DELETE FROM files WHERE db_timestamp < % " PRIi64 " ; "
2009-06-07 14:49:13 -04:00
} ;
if ( sizeof ( queries ) ! = sizeof ( queries_tmpl ) )
{
DPRINTF ( E_LOG , L_DB , " db_purge_cruft(): queries out of sync with queries_tmpl \n " ) ;
return ;
}
for ( i = 0 ; i < ( sizeof ( queries_tmpl ) / sizeof ( queries_tmpl [ 0 ] ) ) ; i + + )
{
queries [ i ] = sqlite3_mprintf ( queries_tmpl [ i ] , ( int64_t ) ref ) ;
if ( ! queries [ i ] )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
goto purge_fail ;
}
}
for ( i = 0 ; i < ( sizeof ( queries ) / sizeof ( queries [ 0 ] ) ) ; i + + )
{
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , queries [ i ] ) ;
ret = sqlite3_exec ( hdl , queries [ i ] , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Purge query %d error: %s \n " , i , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
else
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
}
purge_fail :
for ( i = 0 ; i < ( sizeof ( queries ) / sizeof ( queries [ 0 ] ) ) ; i + + )
{
sqlite3_free ( queries [ i ] ) ;
}
}
2009-06-07 12:56:35 -04:00
static int
2009-06-11 09:45:49 -04:00
db_get_count ( char * query )
2009-06-07 12:56:35 -04:00
{
sqlite3_stmt * stmt ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
return - 1 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
return ret ;
}
2009-06-11 09:45:49 -04:00
/* Queries */
2009-06-07 12:56:35 -04:00
static int
db_build_query_index_clause ( struct query_params * qp , char * * i )
{
char * idx ;
switch ( qp - > idx_type )
{
case I_FIRST :
idx = sqlite3_mprintf ( " LIMIT %d " , qp - > limit ) ;
break ;
case I_LAST :
idx = sqlite3_mprintf ( " LIMIT -1 OFFSET %d " , qp - > limit , qp - > results - qp - > limit ) ;
break ;
case I_SUB :
idx = sqlite3_mprintf ( " LIMIT %d OFFSET %d " , qp - > limit , qp - > offset ) ;
break ;
case I_NONE :
* i = NULL ;
return 0 ;
default :
DPRINTF ( E_LOG , L_DB , " Unknown index type \n " ) ;
return - 1 ;
}
if ( ! idx )
{
DPRINTF ( E_LOG , L_DB , " Could not build index string; out of memory " ) ;
return - 1 ;
}
* i = idx ;
return 0 ;
}
static int
db_build_query_items ( struct query_params * qp , char * * q )
{
char * query ;
char * count ;
char * idx ;
int ret ;
if ( qp - > filter )
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files WHERE disabled = 0 AND %s; " , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files WHERE disabled = 0; " ) ;
2009-06-07 12:56:35 -04:00
if ( ! count )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for count query string \n " ) ;
return - 1 ;
}
2009-06-11 09:45:49 -04:00
qp - > results = db_get_count ( count ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( count ) ;
if ( qp - > results < 0 )
return - 1 ;
/* Get index clause */
ret = db_build_query_index_clause ( qp , & idx ) ;
if ( ret < 0 )
return - 1 ;
if ( idx & & qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT * FROM files WHERE disabled = 0 AND %s %s; " , qp - > filter , idx ) ;
2009-06-07 12:56:35 -04:00
else if ( idx )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT * FROM files WHERE disabled = 0 %s; " , idx ) ;
2009-06-07 12:56:35 -04:00
else if ( qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT * FROM files WHERE disabled = 0 AND %s; " , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT * FROM files WHERE disabled = 0; " ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
* q = query ;
return 0 ;
}
static int
db_build_query_pls ( struct query_params * qp , char * * q )
{
char * query ;
char * idx ;
int ret ;
2009-06-11 09:45:49 -04:00
qp - > results = db_get_count ( " SELECT COUNT(*) FROM playlists WHERE disabled = 0; " ) ;
2009-06-07 12:56:35 -04:00
if ( qp - > results < 0 )
return - 1 ;
/* Get index clause */
ret = db_build_query_index_clause ( qp , & idx ) ;
if ( ret < 0 )
return - 1 ;
2009-12-30 12:53:55 -05:00
if ( idx & & qp - > filter )
query = sqlite3_mprintf ( " SELECT * FROM playlists WHERE disabled = 0 AND %s %s; " , qp - > filter , idx ) ;
else if ( idx )
2009-06-10 09:23:02 -04:00
query = sqlite3_mprintf ( " SELECT * FROM playlists WHERE disabled = 0 %s; " , idx ) ;
2009-12-30 12:53:55 -05:00
else if ( qp - > filter )
query = sqlite3_mprintf ( " SELECT * FROM playlists WHERE disabled = 0 AND %s; " , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-10 09:23:02 -04:00
query = sqlite3_mprintf ( " SELECT * FROM playlists WHERE disabled = 0; " ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
* q = query ;
return 0 ;
}
static int
db_build_query_plitems ( struct query_params * qp , char * * q )
{
char * query ;
char * count ;
char * idx ;
int ret ;
if ( qp - > pl_id < = 0 )
{
DPRINTF ( E_LOG , L_DB , " No playlist id specified in playlist items query \n " ) ;
return - 1 ;
}
if ( qp - > pl_id = = 1 )
{
if ( qp - > filter )
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files WHERE disabled = 0 AND %s; " , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files WHERE disabled = 0; " ) ;
2009-06-07 12:56:35 -04:00
}
else
{
if ( qp - > filter )
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0 AND %s; " , qp - > pl_id , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0; " , qp - > pl_id ) ;
2009-06-07 12:56:35 -04:00
}
if ( ! count )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for count query string \n " ) ;
return - 1 ;
}
2009-06-11 09:45:49 -04:00
qp - > results = db_get_count ( count ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( count ) ;
if ( qp - > results < 0 )
return - 1 ;
/* Get index clause */
ret = db_build_query_index_clause ( qp , & idx ) ;
if ( ret < 0 )
return - 1 ;
if ( idx & & qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT files.* FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0 AND %s ORDER BY playlistitems.id ASC %s; " ,
2009-06-07 12:56:35 -04:00
qp - > pl_id , qp - > filter , idx ) ;
else if ( idx )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT files.* FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0 ORDER BY playlistitems.id ASC %s; " ,
2009-06-07 12:56:35 -04:00
qp - > pl_id , idx ) ;
else if ( qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT files.* FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0 AND %s ORDER BY playlistitems.id ASC; " ,
2009-06-07 12:56:35 -04:00
qp - > pl_id , qp - > filter ) ;
else
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT files.* FROM files JOIN playlistitems ON files.path = playlistitems.filepath "
" WHERE playlistitems.playlistid = %d AND files.disabled = 0 ORDER BY playlistitems.id ASC; " ,
2009-06-07 12:56:35 -04:00
qp - > pl_id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
* q = query ;
return 0 ;
}
2010-01-10 06:26:48 -05:00
static int
db_build_query_groups ( struct query_params * qp , char * * q )
{
char * query ;
char * idx ;
int ret ;
qp - > results = db_get_count ( " SELECT COUNT(DISTINCT daap_songalbumid(album_artist, album)) FROM files WHERE disabled = 0; " ) ;
if ( qp - > results < 0 )
return - 1 ;
/* Get index clause */
ret = db_build_query_index_clause ( qp , & idx ) ;
if ( ret < 0 )
return - 1 ;
if ( idx & & qp - > filter )
query = sqlite3_mprintf ( " SELECT COUNT(*) AS items, daap_songalbumid(album_artist, album) AS persistentid, album_artist, album FROM files GROUP BY album_artist, album HAVING disabled = 0 AND %s %s; " , qp - > filter , idx ) ;
else if ( idx )
query = sqlite3_mprintf ( " SELECT COUNT(*) AS items, daap_songalbumid(album_artist, album) AS persistentid, album_artist, album FROM files GROUP BY album_artist, album HAVING disabled = 0 %s; " , idx ) ;
else if ( qp - > filter )
query = sqlite3_mprintf ( " SELECT COUNT(*) AS items, daap_songalbumid(album_artist, album) AS persistentid, album_artist, album FROM files GROUP BY album_artist, album HAVING disabled = 0 AND %s; " , qp - > filter ) ;
else
query = sqlite3_mprintf ( " SELECT COUNT(*) AS items, daap_songalbumid(album_artist, album) AS persistentid, album_artist, album FROM files GROUP BY album_artist, album HAVING disabled = 0; " ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
* q = query ;
return 0 ;
}
2009-06-07 12:56:35 -04:00
static int
db_build_query_browse ( struct query_params * qp , char * field , char * * q )
{
char * query ;
char * count ;
char * idx ;
int ret ;
if ( qp - > filter )
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT %s) FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != '' AND %s; " ,
2009-06-10 09:23:02 -04:00
field , field , qp - > filter ) ;
2009-06-07 12:56:35 -04:00
else
2009-06-11 17:03:53 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT %s) FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != ''; " ,
2009-06-10 09:23:02 -04:00
field , field ) ;
2009-06-07 12:56:35 -04:00
if ( ! count )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for count query string \n " ) ;
return - 1 ;
}
2009-06-11 09:45:49 -04:00
qp - > results = db_get_count ( count ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( count ) ;
if ( qp - > results < 0 )
return - 1 ;
/* Get index clause */
ret = db_build_query_index_clause ( qp , & idx ) ;
if ( ret < 0 )
return - 1 ;
if ( idx & & qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT DISTINCT %s FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != '' "
2009-06-07 12:56:35 -04:00
" AND %s %s; " , field , field , qp - > filter , idx ) ;
else if ( idx )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT DISTINCT %s FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != '' "
2009-06-07 12:56:35 -04:00
" %s; " , field , field , idx ) ;
else if ( qp - > filter )
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT DISTINCT %s FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != '' "
2009-06-07 12:56:35 -04:00
" AND %s; " , field , field , qp - > filter ) ;
else
2009-06-11 17:03:53 -04:00
query = sqlite3_mprintf ( " SELECT DISTINCT %s FROM files WHERE data_kind = 0 AND disabled = 0 AND %s != '' " ,
2009-06-07 12:56:35 -04:00
field , field ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
* q = query ;
return 0 ;
}
int
db_query_start ( struct query_params * qp )
{
char * query ;
int ret ;
qp - > stmt = NULL ;
switch ( qp - > type )
{
case Q_ITEMS :
ret = db_build_query_items ( qp , & query ) ;
break ;
case Q_PL :
ret = db_build_query_pls ( qp , & query ) ;
break ;
case Q_PLITEMS :
ret = db_build_query_plitems ( qp , & query ) ;
break ;
2010-01-10 06:26:48 -05:00
case Q_GROUPS :
ret = db_build_query_groups ( qp , & query ) ;
break ;
2009-06-07 12:56:35 -04:00
case Q_BROWSE_ALBUMS :
ret = db_build_query_browse ( qp , " album " , & query ) ;
break ;
case Q_BROWSE_ARTISTS :
ret = db_build_query_browse ( qp , " artist " , & query ) ;
break ;
case Q_BROWSE_GENRES :
ret = db_build_query_browse ( qp , " genre " , & query ) ;
break ;
case Q_BROWSE_COMPOSERS :
ret = db_build_query_browse ( qp , " composer " , & query ) ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " Unknown query type \n " ) ;
return - 1 ;
}
if ( ret < 0 )
return - 1 ;
DPRINTF ( E_DBG , L_DB , " Starting query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & qp - > stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return 0 ;
}
void
db_query_end ( struct query_params * qp )
{
if ( ! qp - > stmt )
return ;
qp - > results = - 1 ;
sqlite3_finalize ( qp - > stmt ) ;
qp - > stmt = NULL ;
}
int
db_query_fetch_file ( struct query_params * qp , struct db_media_file_info * dbmfi )
{
int ncols ;
char * * strcol ;
int i ;
int ret ;
memset ( dbmfi , 0 , sizeof ( struct db_media_file_info ) ) ;
if ( ! qp - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Query not started! \n " ) ;
return - 1 ;
}
if ( ( qp - > type ! = Q_ITEMS ) & & ( qp - > type ! = Q_PLITEMS ) )
{
DPRINTF ( E_LOG , L_DB , " Not an items or playlist items query! \n " ) ;
return - 1 ;
}
ret = sqlite3_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_INFO , L_DB , " End of query results \n " ) ;
dbmfi - > id = NULL ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ncols = sqlite3_column_count ( qp - > stmt ) ;
if ( sizeof ( dbmfi_cols_map ) / sizeof ( dbmfi_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: dbmfi column map out of sync with schema \n " ) ;
return - 1 ;
}
for ( i = 0 ; i < ncols ; i + + )
{
strcol = ( char * * ) ( ( char * ) dbmfi + dbmfi_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
return 0 ;
}
int
db_query_fetch_pl ( struct query_params * qp , struct db_playlist_info * dbpli )
{
int ncols ;
char * * strcol ;
2009-06-11 12:28:01 -04:00
int id ;
2009-06-07 12:56:35 -04:00
int i ;
int ret ;
memset ( dbpli , 0 , sizeof ( struct db_playlist_info ) ) ;
if ( ! qp - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Query not started! \n " ) ;
return - 1 ;
}
if ( qp - > type ! = Q_PL )
{
DPRINTF ( E_LOG , L_DB , " Not a playlist query! \n " ) ;
return - 1 ;
}
ret = sqlite3_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_INFO , L_DB , " End of query results \n " ) ;
dbpli - > id = NULL ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ncols = sqlite3_column_count ( qp - > stmt ) ;
if ( sizeof ( dbpli_cols_map ) / sizeof ( dbpli_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: dbpli column map out of sync with schema \n " ) ;
return - 1 ;
}
for ( i = 0 ; i < ncols ; i + + )
{
strcol = ( char * * ) ( ( char * ) dbpli + dbpli_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
2009-06-11 12:28:01 -04:00
id = sqlite3_column_int ( qp - > stmt , 0 ) ;
i = db_pl_count_items ( id ) ;
dbpli - > items = qp - > buf ;
ret = snprintf ( qp - > buf , sizeof ( qp - > buf ) , " %d " , i ) ;
if ( ( ret < 0 ) | | ( ret > = sizeof ( qp - > buf ) ) )
{
DPRINTF ( E_LOG , L_DB , " Could not convert items, buffer too small \n " ) ;
strcpy ( qp - > buf , " 0 " ) ;
}
2009-06-07 12:56:35 -04:00
return 0 ;
}
2010-01-10 06:26:48 -05:00
int
db_query_fetch_group ( struct query_params * qp , struct db_group_info * dbgri )
{
int ncols ;
char * * strcol ;
int i ;
int ret ;
memset ( dbgri , 0 , sizeof ( struct db_group_info ) ) ;
if ( ! qp - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Query not started! \n " ) ;
return - 1 ;
}
if ( qp - > type ! = Q_GROUPS )
{
DPRINTF ( E_LOG , L_DB , " Not a groups query! \n " ) ;
return - 1 ;
}
ret = sqlite3_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_INFO , L_DB , " End of query results \n " ) ;
return 1 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ncols = sqlite3_column_count ( qp - > stmt ) ;
if ( sizeof ( dbgri_cols_map ) / sizeof ( dbgri_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: dbgri column map out of sync with schema \n " ) ;
return - 1 ;
}
for ( i = 0 ; i < ncols ; i + + )
{
strcol = ( char * * ) ( ( char * ) dbgri + dbgri_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
return 0 ;
}
2009-06-07 12:56:35 -04:00
int
db_query_fetch_string ( struct query_params * qp , char * * string )
{
int ret ;
* string = NULL ;
if ( ! qp - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Query not started! \n " ) ;
return - 1 ;
}
if ( ! ( qp - > type & Q_F_BROWSE ) )
{
DPRINTF ( E_LOG , L_DB , " Not a browse query! \n " ) ;
return - 1 ;
}
ret = sqlite3_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_INFO , L_DB , " End of query results \n " ) ;
* string = NULL ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
* string = ( char * ) sqlite3_column_text ( qp - > stmt , 0 ) ;
return 0 ;
}
/* Files */
int
2009-06-11 12:41:50 -04:00
db_files_get_count ( void )
2009-06-07 12:56:35 -04:00
{
2009-06-11 17:03:53 -04:00
return db_get_count ( " SELECT COUNT(*) FROM files WHERE disabled = 0 ; " );
2009-06-07 12:56:35 -04:00
}
void
db_file_inc_playcount ( int id )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 " WHERE id = %d;"
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error incrementing play count on %d: %s \n " , id , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
void
2009-06-11 14:44:22 -04:00
db_file_ping ( char * path )
2009-06-07 12:56:35 -04:00
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET db_timestamp = %" PRIi64 ", disabled = 0 WHERE path = '%q';"
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
2009-06-11 14:44:22 -04:00
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , path ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
2009-06-11 14:44:22 -04:00
DPRINTF ( E_LOG , L_DB , " Error pinging file '%s': %s \n " , path , errmsg ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
2009-11-22 09:39:38 -05:00
static int
db_file_id_byquery ( char * query )
2009-06-07 12:56:35 -04:00
{
sqlite3_stmt * stmt ;
int ret ;
if ( ! query )
2009-11-22 09:39:38 -05:00
return 0 ;
2009-06-07 12:56:35 -04:00
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , strlen ( query ) + 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2009-06-11 14:44:22 -04:00
return 0 ;
2009-06-07 12:56:35 -04:00
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No results \n " ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
2009-06-11 14:44:22 -04:00
return 0 ;
2009-06-07 12:56:35 -04:00
}
2009-06-11 14:44:22 -04:00
ret = sqlite3_column_int ( stmt , 0 ) ;
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
2009-11-22 09:39:38 -05:00
return ret ;
}
int
db_file_id_bypath ( char * path )
{
# define Q_TMPL "SELECT id FROM files WHERE path = '%q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
2009-11-22 10:30:45 -05:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
int
db_file_id_byfilebase ( char * filename , char * base )
{
# define Q_TMPL "SELECT id FROM files WHERE path LIKE '%q / %% / %q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , base , filename ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
int
db_file_id_byfile ( char * filename )
{
# define Q_TMPL "SELECT id FROM files WHERE fname = '%q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , filename ) ;
2009-11-22 09:39:38 -05:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
int
db_file_id_byurl ( char * url )
{
# define Q_TMPL "SELECT id FROM files WHERE url = '%q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , url ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( query ) ;
2009-06-11 14:44:22 -04:00
return ret ;
# undef Q_TMPL
}
time_t
db_file_stamp_bypath ( char * path )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "SELECT db_timestamp FROM files WHERE path = '%q';"
2009-06-11 14:44:22 -04:00
char * query ;
sqlite3_stmt * stmt ;
time_t stamp ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , strlen ( query ) + 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return 0 ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No results \n " ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
}
stamp = ( time_t ) sqlite3_column_int64 ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return stamp ;
2009-06-07 12:56:35 -04:00
# undef Q_TMPL
}
static struct media_file_info *
db_file_fetch_byquery ( char * query )
{
struct media_file_info * mfi ;
sqlite3_stmt * stmt ;
int ncols ;
char * cval ;
uint32_t * ival ;
uint64_t * i64val ;
char * * strval ;
2009-06-10 09:23:02 -04:00
uint64_t disabled ;
2009-06-07 12:56:35 -04:00
int i ;
int ret ;
if ( ! query )
return NULL ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
mfi = ( struct media_file_info * ) malloc ( sizeof ( struct media_file_info ) ) ;
if ( ! mfi )
{
DPRINTF ( E_LOG , L_DB , " Could not allocate struct media_file_info, out of memory \n " ) ;
return NULL ;
}
memset ( mfi , 0 , sizeof ( struct media_file_info ) ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
free ( mfi ) ;
return NULL ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No results \n " ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
free ( mfi ) ;
return NULL ;
}
ncols = sqlite3_column_count ( stmt ) ;
if ( sizeof ( mfi_cols_map ) / sizeof ( mfi_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: mfi column map out of sync with schema \n " ) ;
sqlite3_finalize ( stmt ) ;
/* Can't risk free()ing what's inside the mfi in this case... */
free ( mfi ) ;
return NULL ;
}
for ( i = 0 ; i < ncols ; i + + )
{
switch ( mfi_cols_map [ i ] . type )
{
case DB_TYPE_CHAR :
cval = ( char * ) mfi + mfi_cols_map [ i ] . offset ;
* cval = sqlite3_column_int ( stmt , i ) ;
break ;
case DB_TYPE_INT :
ival = ( uint32_t * ) ( ( char * ) mfi + mfi_cols_map [ i ] . offset ) ;
2009-06-10 09:23:02 -04:00
if ( mfi_cols_map [ i ] . offset = = mfi_offsetof ( disabled ) )
{
disabled = sqlite3_column_int64 ( stmt , i ) ;
* ival = ( disabled ! = 0 ) ;
}
else
* ival = sqlite3_column_int ( stmt , i ) ;
2009-06-07 12:56:35 -04:00
break ;
case DB_TYPE_INT64 :
i64val = ( uint64_t * ) ( ( char * ) mfi + mfi_cols_map [ i ] . offset ) ;
* i64val = sqlite3_column_int64 ( stmt , i ) ;
break ;
case DB_TYPE_STRING :
strval = ( char * * ) ( ( char * ) mfi + mfi_cols_map [ i ] . offset ) ;
cval = ( char * ) sqlite3_column_text ( stmt , i ) ;
if ( cval )
* strval = strdup ( cval ) ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " BUG: Unknown type %d in mfi column map \n " , mfi_cols_map [ i ] . type ) ;
free_mfi ( mfi , 0 ) ;
sqlite3_finalize ( stmt ) ;
return NULL ;
}
}
sqlite3_finalize ( stmt ) ;
return mfi ;
}
struct media_file_info *
db_file_fetch_byid ( int id )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "SELECT * FROM files WHERE id = %d;"
2009-06-07 12:56:35 -04:00
struct media_file_info * mfi ;
char * query ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return NULL ;
}
mfi = db_file_fetch_byquery ( query ) ;
sqlite3_free ( query ) ;
return mfi ;
# undef Q_TMPL
}
int
db_file_add ( struct media_file_info * mfi )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "INSERT INTO files (id, path, fname, title, artist, album, genre, comment, type, composer," \
2009-06-07 12:56:35 -04:00
" orchestra, conductor, grouping, url, bitrate, samplerate, song_length, file_size, year, track, " \
" total_tracks, disc, total_discs, bpm, compilation, rating, play_count, data_kind, item_kind, " \
" description, time_added, time_modified, time_played, db_timestamp, disabled, sample_count, " \
2009-12-26 02:39:39 -05:00
" codectype, idx, has_video, contentrating, bits_per_sample, album_artist, " \
" media_kind, tv_series_name, tv_episode_num_str, tv_network_name, tv_episode_sort, tv_season_num " \
" ) " \
2009-06-07 12:56:35 -04:00
" VALUES (NULL, '%q', '%q', %Q, %Q, %Q, %Q, %Q, %Q, %Q, " \
" %Q, %Q, %Q, %Q, %d, %d, %d, % " PRIi64 " , %d, %d, " \
" %d, %d, %d, %d, %d, %d, %d, %d, %d, " \
" %Q, % " PRIi64 " , % " PRIi64 " , % " PRIi64 " , % " PRIi64 " , %d, % " PRIi64 " , " \
2009-12-26 02:39:39 -05:00
" %Q, %d, %d, %d, %d, %Q, %d, %Q, %Q, %Q, %d, %d); "
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
if ( mfi - > id ! = 0 )
{
DPRINTF ( E_WARN , L_DB , " Trying to update file with id > 0; use db_file_update()? \n " ) ;
return - 1 ;
}
mfi - > db_timestamp = ( uint64_t ) time ( NULL ) ;
mfi - > time_added = mfi - > db_timestamp ;
if ( mfi - > time_modified = = 0 )
mfi - > time_modified = mfi - > db_timestamp ;
query = sqlite3_mprintf ( Q_TMPL ,
STR ( mfi - > path ) , STR ( mfi - > fname ) , mfi - > title , mfi - > artist , mfi - > album ,
mfi - > genre , mfi - > comment , mfi - > type , mfi - > composer ,
mfi - > orchestra , mfi - > conductor , mfi - > grouping , mfi - > url , mfi - > bitrate ,
mfi - > samplerate , mfi - > song_length , mfi - > file_size , mfi - > year , mfi - > track ,
mfi - > total_tracks , mfi - > disc , mfi - > total_discs , mfi - > bpm , mfi - > compilation ,
mfi - > rating , mfi - > play_count , mfi - > data_kind , mfi - > item_kind ,
mfi - > description , ( int64_t ) mfi - > time_added , ( int64_t ) mfi - > time_modified ,
( int64_t ) mfi - > time_played , ( int64_t ) mfi - > db_timestamp , mfi - > disabled , mfi - > sample_count ,
2009-06-11 13:15:22 -04:00
mfi - > codectype , mfi - > index , mfi - > has_video ,
2009-12-26 02:39:39 -05:00
mfi - > contentrating , mfi - > bits_per_sample , mfi - > album_artist ,
mfi - > media_kind , mfi - > tv_series_name , mfi - > tv_episode_num_str ,
mfi - > tv_network_name , mfi - > tv_episode_sort , mfi - > tv_season_num ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
int
db_file_update ( struct media_file_info * mfi )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET path = '%q', fname = '%q', title = %Q, artist = %Q, album = %Q, genre = %Q," \
2009-06-07 12:56:35 -04:00
" comment = %Q, type = %Q, composer = %Q, orchestra = %Q, conductor = %Q, grouping = %Q, " \
" url = %Q, bitrate = %d, samplerate = %d, song_length = %d, file_size = % " PRIi64 " , " \
" year = %d, track = %d, total_tracks = %d, disc = %d, total_discs = %d, bpm = %d, " \
" compilation = %d, rating = %d, data_kind = %d, item_kind = %d, " \
" description = %Q, time_modified = % " PRIi64 " , " \
2009-06-10 09:23:02 -04:00
" db_timestamp = % " PRIi64 " , sample_count = %d, " \
2009-06-11 13:15:22 -04:00
" codectype = %Q, idx = %d, has_video = %d, " \
2009-12-26 02:39:39 -05:00
" bits_per_sample = %d, album_artist = %Q, " \
" media_kind = %d, tv_series_name = %Q, tv_episode_num_str = %Q, " \
2009-12-27 02:19:16 -05:00
" tv_network_name = %Q, tv_episode_sort = %d, tv_season_num = %d " \
2009-12-26 02:39:39 -05:00
" WHERE id = %d; "
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
if ( mfi - > id = = 0 )
{
DPRINTF ( E_WARN , L_DB , " Trying to update file with id 0; use db_file_add()? \n " ) ;
return - 1 ;
}
mfi - > db_timestamp = ( uint64_t ) time ( NULL ) ;
if ( mfi - > time_modified = = 0 )
mfi - > time_modified = mfi - > db_timestamp ;
query = sqlite3_mprintf ( Q_TMPL ,
STR ( mfi - > path ) , STR ( mfi - > fname ) , mfi - > title , mfi - > artist , mfi - > album , mfi - > genre ,
mfi - > comment , mfi - > type , mfi - > composer , mfi - > orchestra , mfi - > conductor , mfi - > grouping ,
mfi - > url , mfi - > bitrate , mfi - > samplerate , mfi - > song_length , mfi - > file_size ,
mfi - > year , mfi - > track , mfi - > total_tracks , mfi - > disc , mfi - > total_discs , mfi - > bpm ,
mfi - > compilation , mfi - > rating , mfi - > data_kind , mfi - > item_kind ,
mfi - > description , ( int64_t ) mfi - > time_modified ,
2009-06-10 09:23:02 -04:00
( int64_t ) mfi - > db_timestamp , mfi - > sample_count ,
2009-06-11 13:15:22 -04:00
mfi - > codectype , mfi - > index , mfi - > has_video ,
2009-12-26 02:39:39 -05:00
mfi - > bits_per_sample , mfi - > album_artist ,
mfi - > media_kind , mfi - > tv_series_name , mfi - > tv_episode_num_str ,
mfi - > tv_network_name , mfi - > tv_episode_sort , mfi - > tv_season_num ,
mfi - > id ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2009-06-10 15:36:43 -04:00
void
db_file_delete_bypath ( char * path )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "DELETE FROM files WHERE path = '%q';"
2009-06-10 15:36:43 -04:00
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error deleting file: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
2009-06-11 08:42:55 -04:00
static void
db_file_disable_byquery ( char * query )
{
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error disabling file: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
2009-06-10 16:11:24 -04:00
void
db_file_disable_bypath ( char * path , char * strip , uint32_t cookie )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET path = substr(path, %d), disabled = %" PRIi64 " WHERE path = '%q';"
2009-06-10 16:11:24 -04:00
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2009-06-11 08:42:55 -04:00
db_file_disable_byquery ( query ) ;
2009-06-10 16:11:24 -04:00
2009-06-11 08:42:55 -04:00
sqlite3_free ( query ) ;
# undef Q_TMPL
}
void
db_file_disable_bymatch ( char * path , char * strip , uint32_t cookie )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET path = substr(path, %d), disabled = %" PRIi64 " WHERE path LIKE '%q / %%';"
2009-06-11 08:42:55 -04:00
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
db_file_disable_byquery ( query ) ;
2009-06-10 16:11:24 -04:00
sqlite3_free ( query ) ;
# undef Q_TMPL
}
int
db_file_enable_bycookie ( uint32_t cookie , char * path )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "UPDATE files SET path = '%q' || path, disabled = 0 WHERE disabled = %" PRIi64 ";"
2009-06-10 16:11:24 -04:00
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path , ( int64_t ) cookie ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Error enabling files: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return sqlite3_changes ( hdl ) ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
/* Playlists */
int
2009-06-11 12:41:50 -04:00
db_pl_get_count ( void )
2009-06-07 12:56:35 -04:00
{
2009-06-11 12:41:50 -04:00
return db_get_count ( " SELECT COUNT(*) FROM playlists WHERE disabled = 0 ; " );
2009-06-07 12:56:35 -04:00
}
2009-06-11 12:28:01 -04:00
static int
db_pl_count_items ( int id )
{
2009-06-11 17:03:53 -04:00
# define Q_TMPL "SELECT COUNT(*) FROM playlistitems JOIN files" \
" ON playlistitems.filepath = files.path WHERE files.disabled = 0 AND playlistitems.playlistid = %d; "
2009-06-11 12:28:01 -04:00
char * query ;
int ret ;
if ( id = = 1 )
2009-06-11 12:41:50 -04:00
return db_files_get_count ( ) ;
2009-06-11 12:28:01 -04:00
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
ret = db_get_count ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
void
db_pl_ping ( int id )
{
2009-06-10 09:23:02 -04:00
# define Q_TMPL "UPDATE playlists SET db_timestamp = %" PRIi64 ", disabled = 0 WHERE id = %d;"
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error pinging playlist %d: %s \n " , id , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
2009-06-10 15:36:43 -04:00
static int
db_pl_id_bypath ( char * path , int * id )
{
# define Q_TMPL "SELECT id FROM playlists WHERE path = '%q';"
char * query ;
sqlite3_stmt * stmt ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No results \n " ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
* id = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
static struct playlist_info *
db_pl_fetch_byquery ( char * query )
{
struct playlist_info * pli ;
sqlite3_stmt * stmt ;
int ncols ;
char * cval ;
uint32_t * ival ;
char * * strval ;
2009-06-10 09:23:02 -04:00
uint64_t disabled ;
2009-06-07 12:56:35 -04:00
int i ;
int ret ;
if ( ! query )
return NULL ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
pli = ( struct playlist_info * ) malloc ( sizeof ( struct playlist_info ) ) ;
if ( ! pli )
{
DPRINTF ( E_LOG , L_DB , " Could not allocate struct playlist_info, out of memory \n " ) ;
return NULL ;
}
memset ( pli , 0 , sizeof ( struct playlist_info ) ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
free ( pli ) ;
return NULL ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No results \n " ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
free ( pli ) ;
return NULL ;
}
ncols = sqlite3_column_count ( stmt ) ;
if ( sizeof ( pli_cols_map ) / sizeof ( pli_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: pli column map out of sync with schema \n " ) ;
sqlite3_finalize ( stmt ) ;
/* Can't risk free()ing what's inside the pli in this case... */
free ( pli ) ;
return NULL ;
}
for ( i = 0 ; i < ncols ; i + + )
{
switch ( pli_cols_map [ i ] . type )
{
case DB_TYPE_INT :
ival = ( uint32_t * ) ( ( char * ) pli + pli_cols_map [ i ] . offset ) ;
2009-06-10 09:23:02 -04:00
if ( pli_cols_map [ i ] . offset = = pli_offsetof ( disabled ) )
{
disabled = sqlite3_column_int64 ( stmt , i ) ;
* ival = ( disabled ! = 0 ) ;
}
else
* ival = sqlite3_column_int ( stmt , i ) ;
2009-06-07 12:56:35 -04:00
break ;
case DB_TYPE_STRING :
strval = ( char * * ) ( ( char * ) pli + pli_cols_map [ i ] . offset ) ;
cval = ( char * ) sqlite3_column_text ( stmt , i ) ;
if ( cval )
* strval = strdup ( cval ) ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " BUG: Unknown type %d in pli column map \n " , pli_cols_map [ i ] . type ) ;
sqlite3_finalize ( stmt ) ;
free_pli ( pli , 0 ) ;
return NULL ;
}
}
2009-11-22 06:30:29 -05:00
ret = sqlite3_step ( stmt ) ;
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
2009-11-22 06:30:29 -05:00
if ( ret ! = SQLITE_DONE )
{
DPRINTF ( E_WARN , L_DB , " Query had more than a single result! \n " ) ;
free_pli ( pli , 0 ) ;
return NULL ;
}
2009-06-07 12:56:35 -04:00
/* Playlist 1: all files */
if ( pli - > id = = 1 )
2009-06-11 12:41:50 -04:00
pli - > items = db_files_get_count ( ) ;
2009-06-11 12:28:01 -04:00
else
pli - > items = db_pl_count_items ( pli - > id ) ;
2009-06-07 12:56:35 -04:00
return pli ;
}
struct playlist_info *
db_pl_fetch_bypath ( char * path )
{
# define Q_TMPL "SELECT * FROM playlists WHERE path = '%q';"
struct playlist_info * pli ;
char * query ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
2009-11-22 06:34:37 -05:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return NULL ;
}
pli = db_pl_fetch_byquery ( query ) ;
sqlite3_free ( query ) ;
return pli ;
# undef Q_TMPL
}
struct playlist_info *
db_pl_fetch_bytitlepath ( char * title , char * path )
{
# define Q_TMPL "SELECT * FROM playlists WHERE title = '%q' AND path = '%q';"
struct playlist_info * pli ;
char * query ;
query = sqlite3_mprintf ( Q_TMPL , title , path ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return NULL ;
}
pli = db_pl_fetch_byquery ( query ) ;
sqlite3_free ( query ) ;
return pli ;
# undef Q_TMPL
}
int
db_pl_add ( char * title , char * path , int * id )
{
2009-11-22 06:19:19 -05:00
# define QDUP_TMPL "SELECT COUNT(*) FROM playlists WHERE title = '%q' AND path = '%q';"
2009-06-11 12:28:01 -04:00
# define QADD_TMPL "INSERT INTO playlists (title, type, query, db_timestamp, disabled, path, idx)" \
" VALUES ('%q', 0, NULL, % " PRIi64 " , 0, '%q', 0); "
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
/* Check duplicates */
query = sqlite3_mprintf ( QDUP_TMPL , title , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
2009-06-11 12:41:50 -04:00
ret = db_get_count ( query ) ;
2009-06-07 12:56:35 -04:00
sqlite3_free ( query ) ;
if ( ret > 0 )
{
DPRINTF ( E_WARN , L_DB , " Duplicate playlist with title '%s' path '%s' \n " , title , path ) ;
return - 1 ;
}
/* Add */
query = sqlite3_mprintf ( QADD_TMPL , title , ( int64_t ) time ( NULL ) , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
* id = ( int ) sqlite3_last_insert_rowid ( hdl ) ;
if ( * id = = 0 )
{
DPRINTF ( E_LOG , L_DB , " Successful insert but no last_insert_rowid! \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Added playlist %s (path %s) with id %d \n " , title , path , * id ) ;
return 0 ;
# undef QDUP_TMPL
# undef QADD_TMPL
}
int
2009-11-22 06:21:21 -05:00
db_pl_add_item_bypath ( int plid , char * path )
2009-06-07 12:56:35 -04:00
{
2009-06-11 14:27:31 -04:00
# define Q_TMPL "INSERT INTO playlistitems (playlistid, filepath) VALUES (%d, '%q');"
2009-06-07 12:56:35 -04:00
char * query ;
char * errmsg ;
int ret ;
2009-06-11 14:27:31 -04:00
query = sqlite3_mprintf ( Q_TMPL , plid , path ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
2009-11-22 06:17:28 -05:00
return 0 ;
# undef Q_TMPL
}
int
db_pl_add_item_byid ( int plid , int fileid )
{
# define Q_TMPL "INSERT INTO playlistitems (playlistid, filepath) VALUES (%d, (SELECT path FROM files WHERE id = %d));"
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , plid , fileid ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
2009-06-07 12:56:35 -04:00
return 0 ;
2009-06-11 14:27:31 -04:00
# undef Q_TMPL
2009-06-07 12:56:35 -04:00
}
2009-06-11 13:04:21 -04:00
void
2009-06-10 15:36:43 -04:00
db_pl_clear_items ( int id )
{
# define Q_TMPL "DELETE FROM playlistitems WHERE playlistid = %d;"
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error clearing playlist %d items: %s \n " , id , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
void
db_pl_delete ( int id )
{
2009-06-10 15:36:43 -04:00
# define Q_TMPL "DELETE FROM playlists WHERE id = %d;"
char * query ;
2009-06-07 12:56:35 -04:00
char * errmsg ;
int ret ;
if ( id = = 1 )
return ;
2009-06-10 15:36:43 -04:00
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
2009-06-07 12:56:35 -04:00
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2009-06-10 15:36:43 -04:00
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
2009-06-07 12:56:35 -04:00
2009-06-10 15:36:43 -04:00
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error deleting playlist %d: %s \n " , id , errmsg ) ;
2009-06-07 12:56:35 -04:00
2009-06-10 15:36:43 -04:00
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
db_pl_clear_items ( id ) ;
# undef Q_TMPL
}
void
db_pl_delete_bypath ( char * path )
{
int id ;
int ret ;
ret = db_pl_id_bypath ( path , & id ) ;
if ( ret < 0 )
return ;
2009-06-07 12:56:35 -04:00
2009-06-10 15:36:43 -04:00
db_pl_delete ( id ) ;
2009-06-07 12:56:35 -04:00
}
2009-06-11 08:42:55 -04:00
static void
db_pl_disable_byquery ( char * query )
{
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error disabling playlist: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
2009-06-10 16:11:24 -04:00
void
db_pl_disable_bypath ( char * path , char * strip , uint32_t cookie )
{
# define Q_TMPL "UPDATE playlists SET path = substr(path, %d), disabled = %" PRIi64 " WHERE path = '%q';"
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2009-06-11 08:42:55 -04:00
db_pl_disable_byquery ( query ) ;
2009-06-10 16:11:24 -04:00
2009-06-11 08:42:55 -04:00
sqlite3_free ( query ) ;
# undef Q_TMPL
}
void
db_pl_disable_bymatch ( char * path , char * strip , uint32_t cookie )
{
# define Q_TMPL "UPDATE playlists SET path = substr(path, %d), disabled = %" PRIi64 " WHERE path LIKE '%q / %%';"
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
db_pl_disable_byquery ( query ) ;
2009-06-10 16:11:24 -04:00
sqlite3_free ( query ) ;
# undef Q_TMPL
}
int
db_pl_enable_bycookie ( uint32_t cookie , char * path )
{
# define Q_TMPL "UPDATE playlists SET path = '%q' || path, disabled = 0 WHERE disabled = %" PRIi64 ";"
char * query ;
char * errmsg ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path , ( int64_t ) cookie ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Error enabling playlists: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return sqlite3_changes ( hdl ) ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
2009-06-10 12:11:11 -04:00
/* Inotify */
int
db_watch_clear ( void )
{
char * query = " DELETE FROM inotify; " ;
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Query error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
return 0 ;
}
int
db_watch_add ( struct watch_info * wi )
{
2009-06-11 11:36:13 -04:00
# define Q_TMPL "INSERT INTO inotify (wd, cookie, path, libidx) VALUES (%d, 0, '%q', %d);"
2009-06-10 12:11:11 -04:00
char * query ;
char * errmsg ;
int ret ;
2009-06-11 11:36:13 -04:00
query = sqlite3_mprintf ( Q_TMPL , wi - > wd , wi - > path , wi - > libidx ) ;
2009-06-10 12:11:11 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Error adding watch: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2009-06-11 10:12:17 -04:00
static int
db_watch_delete_byquery ( char * query )
{
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Error deleting watch: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
return 0 ;
}
2009-06-10 12:11:11 -04:00
int
2009-06-11 10:47:15 -04:00
db_watch_delete_bywd ( uint32_t wd )
2009-06-10 12:11:11 -04:00
{
# define Q_TMPL "DELETE FROM inotify WHERE wd = %d;"
char * query ;
int ret ;
2009-06-11 10:47:15 -04:00
query = sqlite3_mprintf ( Q_TMPL , wd ) ;
2009-06-10 12:11:11 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
2009-06-11 10:12:17 -04:00
ret = db_watch_delete_byquery ( query ) ;
2009-06-10 12:11:11 -04:00
2009-06-11 10:12:17 -04:00
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
int
db_watch_delete_bypath ( char * path )
{
# define Q_TMPL "DELETE FROM inotify WHERE path = '%q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
2009-06-10 12:11:11 -04:00
{
2009-06-11 10:12:17 -04:00
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
ret = db_watch_delete_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
int
db_watch_delete_bymatch ( char * path )
{
# define Q_TMPL "DELETE FROM inotify WHERE path LIKE '%q / %%';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
2009-06-10 12:11:11 -04:00
return - 1 ;
}
2009-06-11 10:12:17 -04:00
ret = db_watch_delete_byquery ( query ) ;
2009-06-10 12:11:11 -04:00
sqlite3_free ( query ) ;
2009-06-11 10:12:17 -04:00
return ret ;
# undef Q_TMPL
}
int
db_watch_delete_bycookie ( uint32_t cookie )
{
# define Q_TMPL "DELETE FROM inotify WHERE cookie = %" PRIi64 ";"
char * query ;
int ret ;
if ( cookie = = 0 )
return - 1 ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) cookie ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
ret = db_watch_delete_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
2009-06-10 12:11:11 -04:00
# undef Q_TMPL
}
int
db_watch_get_bywd ( struct watch_info * wi )
{
# define Q_TMPL "SELECT * FROM inotify WHERE wd = %d;"
char * query ;
sqlite3_stmt * stmt ;
char * * strval ;
char * cval ;
uint32_t * ival ;
int64_t cookie ;
int ncols ;
int i ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , wi - > wd ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Watch wd %d not found \n " , wi - > wd ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ncols = sqlite3_column_count ( stmt ) ;
if ( sizeof ( wi_cols_map ) / sizeof ( wi_cols_map [ 0 ] ) ! = ncols )
{
DPRINTF ( E_LOG , L_DB , " BUG: wi column map out of sync with schema \n " ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
for ( i = 0 ; i < ncols ; i + + )
{
switch ( wi_cols_map [ i ] . type )
{
case DB_TYPE_INT :
ival = ( uint32_t * ) ( ( char * ) wi + wi_cols_map [ i ] . offset ) ;
if ( wi_cols_map [ i ] . offset = = wi_offsetof ( cookie ) )
{
cookie = sqlite3_column_int64 ( stmt , i ) ;
* ival = ( cookie = = INOTIFY_FAKE_COOKIE ) ? 0 : cookie ;
}
else
* ival = sqlite3_column_int ( stmt , i ) ;
break ;
case DB_TYPE_STRING :
strval = ( char * * ) ( ( char * ) wi + wi_cols_map [ i ] . offset ) ;
cval = ( char * ) sqlite3_column_text ( stmt , i ) ;
if ( cval )
* strval = strdup ( cval ) ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " BUG: Unknown type %d in wi column map \n " , wi_cols_map [ i ] . type ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
}
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2009-06-11 09:24:10 -04:00
static void
db_watch_mark_byquery ( char * query )
{
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error marking watch: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
void
db_watch_mark_bypath ( char * path , char * strip , uint32_t cookie )
{
# define Q_TMPL "UPDATE inotify SET path = substr(path, %d), cookie = %" PRIi64 " WHERE path = '%q';"
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
db_watch_mark_byquery ( query ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
void
db_watch_mark_bymatch ( char * path , char * strip , uint32_t cookie )
{
# define Q_TMPL "UPDATE inotify SET path = substr(path, %d), cookie = %" PRIi64 " WHERE path LIKE '%q / %%';"
char * query ;
int64_t disabled ;
int striplen ;
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
striplen = strlen ( strip ) + 1 ;
query = sqlite3_mprintf ( Q_TMPL , striplen , disabled , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
db_watch_mark_byquery ( query ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
2009-06-10 12:11:11 -04:00
2009-06-11 09:42:51 -04:00
void
db_watch_move_bycookie ( uint32_t cookie , char * path )
{
# define Q_TMPL "UPDATE inotify SET path = '%q' || path, cookie = 0 WHERE cookie = %" PRIi64 ";"
char * query ;
char * errmsg ;
int ret ;
if ( cookie = = 0 )
return ;
query = sqlite3_mprintf ( Q_TMPL , path , ( int64_t ) cookie ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
errmsg = NULL ;
ret = sqlite3_exec ( hdl , query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error moving watch: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL
}
int
db_watch_cookie_known ( uint32_t cookie )
{
# define Q_TMPL "SELECT COUNT(*) FROM inotify WHERE cookie = %" PRIi64 ";"
char * query ;
int ret ;
if ( cookie = = 0 )
return 0 ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) cookie ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return 0 ;
}
2009-06-11 09:45:49 -04:00
ret = db_get_count ( query ) ;
2009-06-11 09:42:51 -04:00
sqlite3_free ( query ) ;
return ( ret > 0 ) ;
# undef Q_TMPL
}
2009-06-11 10:44:49 -04:00
int
db_watch_enum_start ( struct watch_enum * we )
{
# define Q_MATCH_TMPL "SELECT wd FROM inotify WHERE path LIKE '%q / %%';"
# define Q_COOKIE_TMPL "SELECT wd FROM inotify WHERE cookie = %" PRIi64 ";"
char * query ;
int ret ;
we - > stmt = NULL ;
if ( we - > match )
query = sqlite3_mprintf ( Q_MATCH_TMPL , we - > match ) ;
else if ( we - > cookie ! = 0 )
query = sqlite3_mprintf ( Q_COOKIE_TMPL , we - > cookie ) ;
else
{
DPRINTF ( E_LOG , L_DB , " Could not start enum, no parameter given \n " ) ;
return - 1 ;
}
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Starting enum '%s' \n " , query ) ;
ret = sqlite3_prepare_v2 ( hdl , query , - 1 , & we - > stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
sqlite3_free ( query ) ;
return 0 ;
# undef Q_MATCH_TMPL
# undef Q_COOKIE_TMPL
}
void
db_watch_enum_end ( struct watch_enum * we )
{
if ( ! we - > stmt )
return ;
sqlite3_finalize ( we - > stmt ) ;
we - > stmt = NULL ;
}
int
db_watch_enum_fetchwd ( struct watch_enum * we , uint32_t * wd )
{
int ret ;
* wd = 0 ;
if ( ! we - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Watch enum not started! \n " ) ;
return - 1 ;
}
ret = sqlite3_step ( we - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_INFO , L_DB , " End of watch enum results \n " ) ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
* wd = ( uint32_t ) sqlite3_column_int ( we - > stmt , 0 ) ;
return 0 ;
}
2009-06-10 12:11:11 -04:00
2010-01-04 12:00:40 -05:00
static void
db_daap_songalbumid_xfunc ( sqlite3_context * pv , int n , sqlite3_value * * ppv )
{
const char * album_artist ;
const char * album ;
sqlite3_int64 result ;
if ( n ! = 2 )
{
sqlite3_result_error ( pv , " daap_songalbumid() requires 2 parameters, album_artist and album " , - 1 ) ;
return ;
}
if ( ( sqlite3_value_type ( ppv [ 0 ] ) ! = SQLITE_TEXT )
| | ( sqlite3_value_type ( ppv [ 1 ] ) ! = SQLITE_TEXT ) )
{
sqlite3_result_error ( pv , " daap_songalbumid() requires 2 text parameters " , - 1 ) ;
return ;
}
album_artist = ( const char * ) sqlite3_value_text ( ppv [ 0 ] ) ;
album = ( const char * ) sqlite3_value_text ( ppv [ 1 ] ) ;
result = daap_songalbumid ( album_artist , album ) ;
sqlite3_result_int64 ( pv , result ) ;
}
2009-06-07 12:56:35 -04:00
int
db_perthread_init ( void )
{
int ret ;
ret = sqlite3_open ( DB_PATH , & hdl ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not open database: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_close ( hdl ) ;
return - 1 ;
}
2010-01-04 12:00:40 -05:00
ret = sqlite3_create_function ( hdl , " daap_songalbumid " , 2 , SQLITE_UTF8 , NULL , db_daap_songalbumid_xfunc , NULL , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not create daap_songalbumid function: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_close ( hdl ) ;
return - 1 ;
}
2009-06-07 12:56:35 -04:00
return 0 ;
}
void
db_perthread_deinit ( void )
{
sqlite3_stmt * stmt ;
/* Tear down anything that's in flight */
while ( ( stmt = sqlite3_next_stmt ( hdl , 0 ) ) )
sqlite3_finalize ( stmt ) ;
sqlite3_close ( hdl ) ;
}
# define T_ADMIN \
" CREATE TABLE IF NOT EXISTS admin( " \
" key VARCHAR(32) NOT NULL, " \
" value VARCHAR(32) NOT NULL " \
" ); "
2009-12-26 02:44:55 -05:00
# define T_FILES \
2009-06-11 17:03:53 -04:00
" CREATE TABLE IF NOT EXISTS files ( " \
2009-12-26 02:44:55 -05:00
" id INTEGER PRIMARY KEY NOT NULL, " \
" path VARCHAR(4096) NOT NULL, " \
" fname VARCHAR(255) NOT NULL, " \
" title VARCHAR(1024) DEFAULT NULL, " \
" artist VARCHAR(1024) DEFAULT NULL, " \
2010-01-05 13:23:52 -05:00
" album VARCHAR(1024) NOT NULL, " \
2009-12-26 02:44:55 -05:00
" genre VARCHAR(255) DEFAULT NULL, " \
" comment VARCHAR(4096) DEFAULT NULL, " \
" type VARCHAR(255) DEFAULT NULL, " \
" composer VARCHAR(1024) DEFAULT NULL, " \
" orchestra VARCHAR(1024) DEFAULT NULL, " \
" conductor VARCHAR(1024) DEFAULT NULL, " \
" grouping VARCHAR(1024) DEFAULT NULL, " \
" url VARCHAR(1024) DEFAULT NULL, " \
" bitrate INTEGER DEFAULT 0, " \
" samplerate INTEGER DEFAULT 0, " \
" song_length INTEGER DEFAULT 0, " \
" file_size INTEGER DEFAULT 0, " \
" year INTEGER DEFAULT 0, " \
" track INTEGER DEFAULT 0, " \
" total_tracks INTEGER DEFAULT 0, " \
" disc INTEGER DEFAULT 0, " \
" total_discs INTEGER DEFAULT 0, " \
" bpm INTEGER DEFAULT 0, " \
" compilation INTEGER DEFAULT 0, " \
" rating INTEGER DEFAULT 0, " \
" play_count INTEGER DEFAULT 0, " \
" data_kind INTEGER DEFAULT 0, " \
" item_kind INTEGER DEFAULT 0, " \
" description INTEGER DEFAULT 0, " \
" time_added INTEGER DEFAULT 0, " \
" time_modified INTEGER DEFAULT 0, " \
" time_played INTEGER DEFAULT 0, " \
" db_timestamp INTEGER DEFAULT 0, " \
" disabled INTEGER DEFAULT 0, " \
" sample_count INTEGER DEFAULT 0, " \
" codectype VARCHAR(5) DEFAULT NULL, " \
" idx INTEGER NOT NULL, " \
" has_video INTEGER DEFAULT 0, " \
" contentrating INTEGER DEFAULT 0, " \
" bits_per_sample INTEGER DEFAULT 0, " \
2010-01-05 13:23:52 -05:00
" album_artist VARCHAR(1024) NOT NULL, " \
2009-12-26 02:44:55 -05:00
" media_kind INTEGER NOT NULL, " \
" tv_series_name VARCHAR(1024) DEFAULT NULL, " \
2009-12-26 02:39:39 -05:00
" tv_episode_num_str VARCHAR(1024) DEFAULT NULL, " \
2009-12-26 02:44:55 -05:00
" tv_network_name VARCHAR(1024) DEFAULT NULL, " \
" tv_episode_sort INTEGER NOT NULL, " \
" tv_season_num INTEGER NOT NULL " \
2009-06-07 12:56:35 -04:00
" ); "
# define T_PL \
" CREATE TABLE IF NOT EXISTS playlists ( " \
" id INTEGER PRIMARY KEY NOT NULL, " \
" title VARCHAR(255) NOT NULL, " \
" type INTEGER NOT NULL, " \
" query VARCHAR(1024), " \
" db_timestamp INTEGER NOT NULL, " \
2009-06-10 09:23:02 -04:00
" disabled INTEGER DEFAULT 0, " \
2009-06-07 12:56:35 -04:00
" path VARCHAR(4096), " \
" idx INTEGER NOT NULL " \
" ); "
# define T_PLITEMS \
" CREATE TABLE IF NOT EXISTS playlistitems ( " \
" id INTEGER PRIMARY KEY NOT NULL, " \
" playlistid INTEGER NOT NULL, " \
2009-06-11 14:27:31 -04:00
" filepath VARCHAR(4096) NOT NULL " \
2009-06-07 12:56:35 -04:00
" ); "
2009-06-10 12:11:11 -04:00
# define T_INOTIFY \
" CREATE TABLE IF NOT EXISTS inotify ( " \
" wd INTEGER PRIMARY KEY NOT NULL, " \
" cookie INTEGER NOT NULL, " \
" path VARCHAR(4096) NOT NULL, " \
" libidx INTEGER NOT NULL " \
" ); "
2009-06-07 12:56:35 -04:00
# define Q_PL1 \
2009-06-11 12:28:01 -04:00
" INSERT INTO playlists (id, title, type, query, db_timestamp, path, idx) " \
" VALUES(1, 'Library', 1, '1', 0, '', 0); "
2009-06-07 12:56:35 -04:00
# define I_PATH \
2009-06-11 17:03:53 -04:00
" CREATE INDEX IF NOT EXISTS idx_path ON files(path, idx); "
2009-06-07 12:56:35 -04:00
2009-06-11 14:27:31 -04:00
# define I_FILEPATH \
" CREATE INDEX IF NOT EXISTS idx_filepath ON playlistitems(filepath ASC); "
2009-06-07 12:56:35 -04:00
# define I_PLITEMID \
2009-06-11 14:27:31 -04:00
" CREATE INDEX IF NOT EXISTS idx_playlistid ON playlistitems(playlistid, filepath); "
2009-06-07 12:56:35 -04:00
2009-06-08 12:41:59 -04:00
2010-01-04 12:10:59 -05:00
# define SCHEMA_VERSION 3
2009-06-08 12:41:59 -04:00
# define Q_SCVER \
2010-01-04 12:10:59 -05:00
" INSERT INTO admin (key, value) VALUES ('schema_version', '3'); "
2009-06-08 12:41:59 -04:00
2009-06-07 12:56:35 -04:00
struct db_init_query {
char * query ;
char * desc ;
} ;
static struct db_init_query db_init_queries [ ] =
{
{ T_ADMIN , " create table admin " } ,
2009-06-11 17:03:53 -04:00
{ T_FILES , " create table files " } ,
2009-06-07 12:56:35 -04:00
{ T_PL , " create table playlists " } ,
{ T_PLITEMS , " create table playlistitems " } ,
2009-06-10 12:11:11 -04:00
{ T_INOTIFY , " create table inotify " } ,
2009-06-07 12:56:35 -04:00
{ I_PATH , " create file path index " } ,
2009-06-11 14:27:31 -04:00
{ I_FILEPATH , " create file path index " } ,
2009-06-07 12:56:35 -04:00
{ I_PLITEMID , " create playlist id index " } ,
} ;
static int
db_create_tables ( void )
{
char * errmsg ;
int i ;
int ret ;
for ( i = 0 ; i < ( sizeof ( db_init_queries ) / sizeof ( db_init_queries [ 0 ] ) ) ; i + + )
{
DPRINTF ( E_DBG , L_DB , " DB init query: %s \n " , db_init_queries [ i ] . desc ) ;
ret = sqlite3_exec ( hdl , db_init_queries [ i ] . query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " DB init error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
}
2009-06-11 09:45:49 -04:00
ret = db_get_count ( " SELECT COUNT(*) FROM playlists WHERE id = 1; " ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = 1 )
{
DPRINTF ( E_DBG , L_DB , " Creating default playlist \n " ) ;
ret = sqlite3_exec ( hdl , Q_PL1 , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not add default playlist: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
}
2009-06-11 09:45:49 -04:00
ret = db_get_count ( " SELECT COUNT(*) FROM admin WHERE key = 'schema_version'; " ) ;
2009-06-08 12:41:59 -04:00
if ( ret ! = 1 )
{
DPRINTF ( E_DBG , L_DB , " Setting schema version \n " ) ;
ret = sqlite3_exec ( hdl , Q_SCVER , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not set schema version: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
}
2009-06-07 12:56:35 -04:00
return 0 ;
}
2010-01-05 13:18:30 -05:00
static int
db_generic_upgrade ( struct db_init_query * queries , int nqueries )
{
char * errmsg ;
int i ;
int ret ;
for ( i = 0 ; i < nqueries ; i + + , queries + + )
{
DPRINTF ( E_DBG , L_DB , " DB upgrade query: %s \n " , queries - > desc ) ;
ret = sqlite3_exec ( hdl , queries - > query , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " DB upgrade error: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
}
return 0 ;
}
2009-12-26 03:18:56 -05:00
/* Upgrade from schema v1 to v2 */
# define U_V2_FILES \
" ALTER TABLE files ADD COLUMN media_kind INTEGER NOT NULL DEFAULT 0; " \
" ALTER TABLE files ADD COLUMN tv_series_name VARCHAR(1024) DEFAULT NULL; " \
" ALTER TABLE files ADD COLUMN tv_episode_num_str VARCHAR(1024) DEFAULT NULL; " \
" ALTER TABLE files ADD COLUMN tv_network_name VARCHAR(1024) DEFAULT NULL; " \
" ALTER TABLE files ADD COLUMN tv_episode_sort INTEGER NOT NULL DEFAULT 0; " \
" ALTER TABLE files ADD COLUMN tv_season_num INTEGER NOT NULL DEFAULT 0; "
2009-12-27 02:17:25 -05:00
# define U_V2_RESCAN \
" UPDATE files SET db_timestamp = 1; "
2009-12-26 03:18:56 -05:00
# define U_V2_SCVER \
" UPDATE admin SET value = '2' WHERE key = 'schema_version'; "
static struct db_init_query db_upgrade_v2_queries [ ] =
{
{ U_V2_FILES , " upgrade table files " } ,
2009-12-27 02:17:25 -05:00
{ U_V2_RESCAN , " force library rescan " } ,
2009-12-26 03:18:56 -05:00
{ U_V2_SCVER , " set schema_version to 2 " } ,
} ;
2010-01-04 12:10:59 -05:00
/* Upgrade from schema v2 to v3 */
# define U_V3_FILES \
" UPDATE files SET album_artist = COALESCE(artist, '') WHERE album_artist IS NULL; "
# define U_V3_SCVER \
" UPDATE admin SET value = '3' WHERE key = 'schema_version'; "
static struct db_init_query db_upgrade_v3_queries [ ] =
{
{ U_V3_FILES , " upgrade table files " } ,
{ U_V3_SCVER , " set schema_version to 3 " } ,
} ;
2009-12-26 03:18:56 -05:00
2009-06-08 12:41:59 -04:00
static int
db_check_version ( void )
{
# define Q_VER "SELECT value FROM admin WHERE key = 'schema_version';"
2009-12-26 03:18:56 -05:00
# define Q_VACUUM "VACUUM;"
2009-06-08 12:41:59 -04:00
sqlite3_stmt * stmt ;
2009-12-26 03:18:56 -05:00
char * errmsg ;
int cur_ver ;
2009-06-08 12:41:59 -04:00
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , Q_VER ) ;
ret = sqlite3_prepare_v2 ( hdl , Q_VER , strlen ( Q_VER ) + 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s %d \n " , sqlite3_errmsg ( hdl ) , ret ) ;
return - 1 ;
}
ret = sqlite3_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s %d \n " , sqlite3_errmsg ( hdl ) , ret ) ;
sqlite3_finalize ( stmt ) ;
return - 1 ;
}
2009-12-26 03:18:56 -05:00
cur_ver = sqlite3_column_int ( stmt , 0 ) ;
2009-06-08 12:41:59 -04:00
sqlite3_finalize ( stmt ) ;
2009-12-26 03:18:56 -05:00
if ( cur_ver < SCHEMA_VERSION )
2009-06-08 12:41:59 -04:00
{
2009-12-26 03:18:56 -05:00
DPRINTF ( E_LOG , L_DB , " Database schema outdated, schema upgrade needed v%d -> v%d \n " , cur_ver , SCHEMA_VERSION ) ;
switch ( cur_ver )
{
case 1 :
2010-01-05 13:18:30 -05:00
ret = db_generic_upgrade ( db_upgrade_v2_queries , sizeof ( db_upgrade_v2_queries ) / sizeof ( db_upgrade_v2_queries [ 0 ] ) ) ;
2010-01-04 12:10:59 -05:00
if ( ret < 0 )
return - 1 ;
/* FALLTHROUGH */
case 2 :
2010-01-05 13:18:30 -05:00
ret = db_generic_upgrade ( db_upgrade_v3_queries , sizeof ( db_upgrade_v3_queries ) / sizeof ( db_upgrade_v3_queries [ 0 ] ) ) ;
2009-12-26 03:18:56 -05:00
if ( ret < 0 )
return - 1 ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " No upgrade path from DB schema v%d to v%d \n " , cur_ver , SCHEMA_VERSION ) ;
return - 1 ;
}
/* What about some housekeeping work, eh? */
DPRINTF ( E_INFO , L_DB , " Now vacuuming database, this may take some time... \n " ) ;
ret = sqlite3_exec ( hdl , Q_VACUUM , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not VACUUM database: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
return - 1 ;
}
2009-06-08 12:41:59 -04:00
}
2009-12-26 03:18:56 -05:00
else if ( cur_ver > SCHEMA_VERSION )
2009-06-08 12:41:59 -04:00
{
DPRINTF ( E_LOG , L_DB , " Database schema is newer than the supported version \n " ) ;
return - 1 ;
}
return 0 ;
# undef Q_VER
2009-12-26 03:18:56 -05:00
# undef Q_VACUUM
2009-06-08 12:41:59 -04:00
}
2009-06-07 12:56:35 -04:00
int
db_init ( void )
{
int files ;
int pls ;
int ret ;
if ( ! sqlite3_threadsafe ( ) )
{
DPRINTF ( E_FATAL , L_DB , " The SQLite3 library is not built with a threadsafe configuration \n " ) ;
return - 1 ;
}
ret = db_perthread_init ( ) ;
if ( ret < 0 )
return ret ;
ret = db_create_tables ( ) ;
if ( ret < 0 )
{
DPRINTF ( E_FATAL , L_DB , " Could not create tables \n " ) ;
db_perthread_deinit ( ) ;
return - 1 ;
}
2009-06-08 12:41:59 -04:00
ret = db_check_version ( ) ;
if ( ret < 0 )
{
DPRINTF ( E_FATAL , L_DB , " Could not check database version \n " ) ;
db_perthread_deinit ( ) ;
return - 1 ;
}
2009-06-11 12:41:50 -04:00
files = db_files_get_count ( ) ;
pls = db_pl_get_count ( ) ;
2009-06-07 12:56:35 -04:00
db_perthread_deinit ( ) ;
2009-06-10 09:23:02 -04:00
DPRINTF ( E_INFO , L_DB , " Database OK with %d active files and %d active playlists \n " , files , pls ) ;
2009-06-07 12:56:35 -04:00
return 0 ;
}