2009-06-07 12:56:35 -04:00
/*
2011-03-08 12:54:20 -05:00
* Copyright ( C ) 2009 - 2011 Julien BLACHE < jb @ jblache . org >
2010-08-29 04:21:35 -04:00
* Copyright ( C ) 2010 Kai Elwert < elwertk @ googlemail . com >
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>
2018-09-23 16:24:24 -04:00
# include <stdbool.h>
2009-06-07 12:56:35 -04:00
# include <inttypes.h>
2010-08-29 04:21:35 -04:00
# include <errno.h>
2011-03-15 16:26:17 -04:00
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2018-03-09 13:03:43 -05:00
# include <unictype.h>
# include <uninorm.h>
# include <unistr.h>
2011-03-15 16:26:17 -04:00
# include <sys/mman.h>
2014-12-21 14:41:44 -05:00
# include <limits.h>
2011-03-15 16:26:17 -04:00
2009-06-07 12:56:35 -04:00
# include <sqlite3.h>
2010-03-21 06:33:05 -04:00
# include "conffile.h"
2009-06-07 12:56:35 -04:00
# include "logger.h"
2014-11-10 16:53:08 -05:00
# include "cache.h"
2016-11-04 10:34:00 -04:00
# include "listener.h"
2017-01-27 17:23:25 -05:00
# include "library.h"
2010-03-06 11:21:54 -05:00
# include "misc.h"
2009-06-07 12:56:35 -04:00
# include "db.h"
2016-11-04 02:10:36 -04:00
# include "db_init.h"
2015-12-19 01:51:10 -05:00
# include "db_upgrade.h"
2016-11-04 10:34:00 -04:00
# include "rng.h"
2009-06-07 12:56:35 -04:00
# define STR(x) ((x) ? (x) : "")
2018-12-28 13:10:17 -05:00
// Inotify cookies are uint32_t
2009-06-10 12:11:11 -04:00
# define INOTIFY_FAKE_COOKIE ((int64_t)1 << 32)
2009-06-07 12:56:35 -04:00
2019-01-30 17:34:00 -05:00
# define DB_TYPE_INT 1
# define DB_TYPE_INT64 2
# define DB_TYPE_STRING 3
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
// Flags that the field will not be bound to prepared statements, which is relevant if the field has no
// matching column, or if the the column value is set automatically by the db, e.g. by a trigger
# define DB_FLAG_NO_BIND (1 << 0)
2018-12-30 15:55:55 -05:00
// Flags that we will only update column value if we have non-zero value (to avoid zeroing e.g. rating)
# define DB_FLAG_NO_ZERO (1 << 1)
2018-09-23 16:24:24 -04:00
2019-05-28 17:40:41 -04:00
// The two last columns of playlist_info are calculated fields, so all playlist retrieval functions must use this query
# define Q_PL_SELECT "SELECT f.*, COUNT(pi.id), SUM(pi.filepath NOT NULL AND pi.filepath LIKE 'http%%')" \
" FROM playlists f LEFT JOIN playlistitems pi ON (f.id = pi.playlistid) "
2010-03-06 12:56:30 -05:00
enum group_type {
G_ALBUMS = 1 ,
2013-12-17 14:58:55 -05:00
G_ARTISTS = 2 ,
2010-03-06 12:56:30 -05:00
} ;
2018-12-28 13:10:17 -05:00
enum fixup_type {
DB_FIXUP_STANDARD = 0 ,
DB_FIXUP_NO_SANITIZE ,
DB_FIXUP_TITLE ,
DB_FIXUP_ARTIST ,
DB_FIXUP_ALBUM ,
DB_FIXUP_ALBUM_ARTIST ,
DB_FIXUP_GENRE ,
DB_FIXUP_COMPOSER ,
DB_FIXUP_TYPE ,
DB_FIXUP_CODECTYPE ,
DB_FIXUP_MEDIA_KIND ,
2018-12-30 10:13:57 -05:00
DB_FIXUP_ITEM_KIND ,
2018-12-28 13:10:17 -05:00
DB_FIXUP_TITLE_SORT ,
DB_FIXUP_ARTIST_SORT ,
DB_FIXUP_ALBUM_SORT ,
DB_FIXUP_ALBUM_ARTIST_SORT ,
DB_FIXUP_COMPOSER_SORT ,
DB_FIXUP_TIME_MODIFIED ,
2019-05-12 17:28:38 -04:00
DB_FIXUP_SONGARTISTID ,
DB_FIXUP_SONGALBUMID ,
2018-12-28 13:10:17 -05:00
} ;
2010-04-26 12:24:09 -04:00
struct db_unlock {
int proceed ;
pthread_cond_t cond ;
pthread_mutex_t lck ;
} ;
2018-09-23 16:24:24 -04:00
struct db_statements
{
sqlite3_stmt * files_insert ;
sqlite3_stmt * files_update ;
sqlite3_stmt * files_ping ;
2019-12-27 18:04:43 -05:00
sqlite3_stmt * playlists_insert ;
sqlite3_stmt * playlists_update ;
2018-09-23 16:24:24 -04:00
} ;
2009-06-07 12:56:35 -04:00
struct col_type_map {
2018-09-23 16:24:24 -04:00
char * name ;
2018-12-28 13:10:17 -05:00
size_t offset ;
2009-06-07 12:56:35 -04:00
short type ;
2018-12-28 13:10:17 -05:00
enum fixup_type fixup ;
2018-09-23 16:24:24 -04:00
short flag ;
2009-06-07 12:56:35 -04:00
} ;
2018-12-28 13:10:17 -05:00
struct fixup_ctx
{
const struct col_type_map * map ;
size_t map_size ;
void * data ;
struct media_file_info * mfi ;
struct playlist_info * pli ;
struct db_queue_item * queue_item ;
} ;
2017-01-04 16:29:47 -05:00
struct query_clause {
char * where ;
2018-01-20 18:22:21 -05:00
char * group ;
2018-03-17 08:27:59 -04:00
char * having ;
2018-01-20 18:22:21 -05:00
char * order ;
2017-01-04 16:29:47 -05:00
char * index ;
} ;
2018-01-20 18:22:21 -05:00
struct browse_clause {
char * select ;
char * where ;
char * group ;
} ;
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 type and name of the fields in struct media_file_info
*/
2010-03-23 13:55:39 -04:00
static const struct col_type_map mfi_cols_map [ ] =
2009-06-07 12:56:35 -04:00
{
2019-12-27 18:04:43 -05:00
{ " id " , mfi_offsetof ( id ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_BIND } ,
2018-12-28 13:10:17 -05:00
{ " path " , mfi_offsetof ( path ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
2018-12-29 18:27:47 -05:00
{ " virtual_path " , mfi_offsetof ( virtual_path ) , DB_TYPE_STRING } ,
2018-12-28 13:10:17 -05:00
{ " fname " , mfi_offsetof ( fname ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
2018-12-29 18:27:47 -05:00
{ " directory_id " , mfi_offsetof ( directory_id ) , DB_TYPE_INT } ,
2018-12-28 13:10:17 -05:00
{ " title " , mfi_offsetof ( title ) , DB_TYPE_STRING , DB_FIXUP_TITLE } ,
{ " artist " , mfi_offsetof ( artist ) , DB_TYPE_STRING , DB_FIXUP_ARTIST } ,
{ " album " , mfi_offsetof ( album ) , DB_TYPE_STRING , DB_FIXUP_ALBUM } ,
2018-12-29 18:27:47 -05:00
{ " album_artist " , mfi_offsetof ( album_artist ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_ARTIST } ,
2018-12-28 13:10:17 -05:00
{ " genre " , mfi_offsetof ( genre ) , DB_TYPE_STRING , DB_FIXUP_GENRE } ,
2018-09-23 16:24:24 -04:00
{ " comment " , mfi_offsetof ( comment ) , DB_TYPE_STRING } ,
2018-12-28 13:10:17 -05:00
{ " type " , mfi_offsetof ( type ) , DB_TYPE_STRING , DB_FIXUP_TYPE } ,
{ " composer " , mfi_offsetof ( composer ) , DB_TYPE_STRING , DB_FIXUP_COMPOSER } ,
2018-09-23 16:24:24 -04:00
{ " orchestra " , mfi_offsetof ( orchestra ) , DB_TYPE_STRING } ,
{ " conductor " , mfi_offsetof ( conductor ) , DB_TYPE_STRING } ,
{ " grouping " , mfi_offsetof ( grouping ) , DB_TYPE_STRING } ,
{ " url " , mfi_offsetof ( url ) , DB_TYPE_STRING } ,
{ " bitrate " , mfi_offsetof ( bitrate ) , DB_TYPE_INT } ,
{ " samplerate " , mfi_offsetof ( samplerate ) , DB_TYPE_INT } ,
{ " song_length " , mfi_offsetof ( song_length ) , DB_TYPE_INT } ,
{ " file_size " , mfi_offsetof ( file_size ) , DB_TYPE_INT64 } ,
{ " year " , mfi_offsetof ( year ) , DB_TYPE_INT } ,
2018-12-29 18:27:47 -05:00
{ " date_released " , mfi_offsetof ( date_released ) , DB_TYPE_INT } ,
2018-09-23 16:24:24 -04:00
{ " track " , mfi_offsetof ( track ) , DB_TYPE_INT } ,
{ " total_tracks " , mfi_offsetof ( total_tracks ) , DB_TYPE_INT } ,
{ " disc " , mfi_offsetof ( disc ) , DB_TYPE_INT } ,
{ " total_discs " , mfi_offsetof ( total_discs ) , DB_TYPE_INT } ,
{ " bpm " , mfi_offsetof ( bpm ) , DB_TYPE_INT } ,
2019-01-30 17:34:00 -05:00
{ " compilation " , mfi_offsetof ( compilation ) , DB_TYPE_INT } ,
{ " artwork " , mfi_offsetof ( artwork ) , DB_TYPE_INT } ,
2018-12-30 15:55:55 -05:00
{ " rating " , mfi_offsetof ( rating ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
{ " play_count " , mfi_offsetof ( play_count ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
{ " skip_count " , mfi_offsetof ( skip_count ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
{ " seek " , mfi_offsetof ( seek ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
2018-09-23 16:24:24 -04:00
{ " data_kind " , mfi_offsetof ( data_kind ) , DB_TYPE_INT } ,
2018-12-29 18:27:47 -05:00
{ " media_kind " , mfi_offsetof ( media_kind ) , DB_TYPE_INT , DB_FIXUP_MEDIA_KIND } ,
2018-12-30 10:13:57 -05:00
{ " item_kind " , mfi_offsetof ( item_kind ) , DB_TYPE_INT , DB_FIXUP_ITEM_KIND } ,
2018-09-23 16:24:24 -04:00
{ " description " , mfi_offsetof ( description ) , DB_TYPE_STRING } ,
2018-12-29 18:27:47 -05:00
{ " db_timestamp " , mfi_offsetof ( db_timestamp ) , DB_TYPE_INT } ,
2019-12-03 12:24:39 -05:00
{ " time_added " , mfi_offsetof ( time_added ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
2018-12-28 13:10:17 -05:00
{ " time_modified " , mfi_offsetof ( time_modified ) , DB_TYPE_INT , DB_FIXUP_TIME_MODIFIED } ,
2018-12-30 15:55:55 -05:00
{ " time_played " , mfi_offsetof ( time_played ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
{ " time_skipped " , mfi_offsetof ( time_skipped ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_ZERO } ,
2018-09-23 16:24:24 -04:00
{ " disabled " , mfi_offsetof ( disabled ) , DB_TYPE_INT } ,
{ " sample_count " , mfi_offsetof ( sample_count ) , DB_TYPE_INT64 } ,
2018-12-28 13:10:17 -05:00
{ " codectype " , mfi_offsetof ( codectype ) , DB_TYPE_STRING , DB_FIXUP_CODECTYPE } ,
2018-12-29 18:27:47 -05:00
{ " idx " , mfi_offsetof ( idx ) , DB_TYPE_INT } ,
2018-09-23 16:24:24 -04:00
{ " has_video " , mfi_offsetof ( has_video ) , DB_TYPE_INT } ,
{ " contentrating " , mfi_offsetof ( contentrating ) , DB_TYPE_INT } ,
{ " bits_per_sample " , mfi_offsetof ( bits_per_sample ) , DB_TYPE_INT } ,
{ " tv_series_name " , mfi_offsetof ( tv_series_name ) , DB_TYPE_STRING } ,
{ " tv_episode_num_str " , mfi_offsetof ( tv_episode_num_str ) , DB_TYPE_STRING } ,
{ " tv_network_name " , mfi_offsetof ( tv_network_name ) , DB_TYPE_STRING } ,
{ " tv_episode_sort " , mfi_offsetof ( tv_episode_sort ) , DB_TYPE_INT } ,
{ " tv_season_num " , mfi_offsetof ( tv_season_num ) , DB_TYPE_INT } ,
2019-05-12 17:28:38 -04:00
{ " songartistid " , mfi_offsetof ( songartistid ) , DB_TYPE_INT64 , DB_FIXUP_SONGARTISTID } ,
{ " songalbumid " , mfi_offsetof ( songalbumid ) , DB_TYPE_INT64 , DB_FIXUP_SONGALBUMID } ,
2018-12-28 13:10:17 -05:00
{ " title_sort " , mfi_offsetof ( title_sort ) , DB_TYPE_STRING , DB_FIXUP_TITLE_SORT } ,
{ " artist_sort " , mfi_offsetof ( artist_sort ) , DB_TYPE_STRING , DB_FIXUP_ARTIST_SORT } ,
{ " album_sort " , mfi_offsetof ( album_sort ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_SORT } ,
{ " album_artist_sort " , mfi_offsetof ( album_artist_sort ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_ARTIST_SORT } ,
2018-12-29 18:27:47 -05:00
{ " composer_sort " , mfi_offsetof ( composer_sort ) , DB_TYPE_STRING , DB_FIXUP_COMPOSER_SORT } ,
2019-06-07 04:53:37 -04:00
{ " channels " , mfi_offsetof ( channels ) , 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
*/
2010-03-23 13:55:39 -04:00
static const struct col_type_map pli_cols_map [ ] =
2009-06-07 12:56:35 -04:00
{
2019-12-27 18:04:43 -05:00
{ " id " , pli_offsetof ( id ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_BIND } ,
2018-12-28 13:10:17 -05:00
{ " title " , pli_offsetof ( title ) , DB_TYPE_STRING , DB_FIXUP_TITLE } ,
{ " type " , pli_offsetof ( type ) , DB_TYPE_INT } ,
{ " query " , pli_offsetof ( query ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " db_timestamp " , pli_offsetof ( db_timestamp ) , DB_TYPE_INT } ,
{ " disabled " , pli_offsetof ( disabled ) , DB_TYPE_INT } ,
{ " path " , pli_offsetof ( path ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " idx " , pli_offsetof ( index ) , DB_TYPE_INT } ,
{ " special_id " , pli_offsetof ( special_id ) , DB_TYPE_INT } ,
{ " virtual_path " , pli_offsetof ( virtual_path ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " parent_id " , pli_offsetof ( parent_id ) , DB_TYPE_INT } ,
{ " directory_id " , pli_offsetof ( directory_id ) , DB_TYPE_INT } ,
{ " query_order " , pli_offsetof ( query_order ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " query_limit " , pli_offsetof ( query_limit ) , DB_TYPE_INT } ,
2019-12-27 18:04:43 -05:00
{ " media_kind " , pli_offsetof ( media_kind ) , DB_TYPE_INT , DB_FIXUP_MEDIA_KIND } ,
2009-06-11 12:28:01 -04:00
2019-05-28 17:40:41 -04:00
// Not in the database, but returned via the query's COUNT()/SUM()
2019-12-27 18:04:43 -05:00
{ " items " , pli_offsetof ( items ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_BIND } ,
{ " streams " , pli_offsetof ( streams ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_BIND } ,
2009-06-07 12:56:35 -04:00
} ;
2018-12-28 13:10:17 -05:00
/* This list must be kept in sync with
* - the order of the columns in the queue table
* - the type and name of the fields in struct db_queue_item
*/
static const struct col_type_map qi_cols_map [ ] =
{
2019-12-27 18:04:43 -05:00
{ " id " , qi_offsetof ( id ) , DB_TYPE_INT , DB_FIXUP_STANDARD , DB_FLAG_NO_BIND } ,
2018-12-28 13:10:17 -05:00
{ " file_id " , qi_offsetof ( id ) , DB_TYPE_INT } ,
{ " pos " , qi_offsetof ( pos ) , DB_TYPE_INT } ,
{ " shuffle_pos " , qi_offsetof ( shuffle_pos ) , DB_TYPE_INT } ,
{ " data_kind " , qi_offsetof ( data_kind ) , DB_TYPE_INT } ,
{ " media_kind " , qi_offsetof ( media_kind ) , DB_TYPE_INT , DB_FIXUP_MEDIA_KIND } ,
{ " song_length " , qi_offsetof ( song_length ) , DB_TYPE_INT } ,
{ " path " , qi_offsetof ( path ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " virtual_path " , qi_offsetof ( virtual_path ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " title " , qi_offsetof ( title ) , DB_TYPE_STRING , DB_FIXUP_TITLE } ,
{ " artist " , qi_offsetof ( artist ) , DB_TYPE_STRING , DB_FIXUP_ARTIST } ,
{ " album_artist " , qi_offsetof ( album_artist ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_ARTIST } ,
{ " album " , qi_offsetof ( album ) , DB_TYPE_STRING , DB_FIXUP_ALBUM } ,
{ " genre " , qi_offsetof ( genre ) , DB_TYPE_STRING , DB_FIXUP_GENRE } ,
{ " songalbumid " , qi_offsetof ( songalbumid ) , DB_TYPE_INT64 } ,
{ " time_modified " , qi_offsetof ( time_modified ) , DB_TYPE_INT } ,
{ " artist_sort " , qi_offsetof ( artist_sort ) , DB_TYPE_STRING , DB_FIXUP_ARTIST_SORT } ,
{ " album_sort " , qi_offsetof ( album_sort ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_SORT } ,
{ " album_artist_sort " , qi_offsetof ( album_artist_sort ) , DB_TYPE_STRING , DB_FIXUP_ALBUM_ARTIST_SORT } ,
{ " year " , qi_offsetof ( year ) , DB_TYPE_INT } ,
{ " track " , qi_offsetof ( track ) , DB_TYPE_INT } ,
{ " disc " , qi_offsetof ( disc ) , DB_TYPE_INT } ,
{ " artwork_url " , qi_offsetof ( artwork_url ) , DB_TYPE_STRING , DB_FIXUP_NO_SANITIZE } ,
{ " queue_version " , qi_offsetof ( queue_version ) , DB_TYPE_INT } ,
{ " composer " , qi_offsetof ( composer ) , DB_TYPE_STRING , DB_FIXUP_COMPOSER } ,
2019-02-01 10:39:10 -05:00
{ " songartistid " , qi_offsetof ( songartistid ) , DB_TYPE_INT64 } ,
2019-06-04 16:51:44 -04:00
{ " type " , qi_offsetof ( type ) , DB_TYPE_STRING , DB_FIXUP_CODECTYPE } ,
{ " bitrate " , qi_offsetof ( bitrate ) , DB_TYPE_INT } ,
{ " samplerate " , qi_offsetof ( samplerate ) , DB_TYPE_INT } ,
2019-12-27 18:04:43 -05:00
{ " channels " , qi_offsetof ( channels ) , DB_TYPE_INT } ,
2018-12-28 13:10:17 -05:00
} ;
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
*/
2010-03-23 13:55:39 -04:00
static const ssize_t dbmfi_cols_map [ ] =
2009-06-07 12:56:35 -04:00
{
dbmfi_offsetof ( id ) ,
dbmfi_offsetof ( path ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( virtual_path ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( fname ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( directory_id ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( title ) ,
dbmfi_offsetof ( artist ) ,
dbmfi_offsetof ( album ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( album_artist ) ,
2009-06-07 12:56:35 -04:00
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 ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( date_released ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( track ) ,
dbmfi_offsetof ( total_tracks ) ,
dbmfi_offsetof ( disc ) ,
dbmfi_offsetof ( total_discs ) ,
dbmfi_offsetof ( bpm ) ,
dbmfi_offsetof ( compilation ) ,
2014-01-11 17:05:29 -05:00
dbmfi_offsetof ( artwork ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( rating ) ,
dbmfi_offsetof ( play_count ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( skip_count ) ,
2013-12-17 14:58:55 -05:00
dbmfi_offsetof ( seek ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( data_kind ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( media_kind ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( item_kind ) ,
dbmfi_offsetof ( description ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( db_timestamp ) ,
2009-06-07 12:56:35 -04:00
dbmfi_offsetof ( time_added ) ,
dbmfi_offsetof ( time_modified ) ,
dbmfi_offsetof ( time_played ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( time_skipped ) ,
2009-06-07 12:56:35 -04:00
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 ) ,
2009-12-26 02:39:39 -05:00
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 ) ,
2013-12-17 14:58:55 -05:00
dbmfi_offsetof ( songartistid ) ,
2010-03-06 11:13:56 -05:00
dbmfi_offsetof ( songalbumid ) ,
2010-09-21 07:16:38 -04:00
dbmfi_offsetof ( title_sort ) ,
dbmfi_offsetof ( artist_sort ) ,
dbmfi_offsetof ( album_sort ) ,
dbmfi_offsetof ( album_artist_sort ) ,
2018-12-29 18:27:47 -05:00
dbmfi_offsetof ( composer_sort ) ,
2019-06-07 04:53:37 -04:00
dbmfi_offsetof ( channels ) ,
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
*/
2010-03-23 13:55:39 -04:00
static const ssize_t dbpli_cols_map [ ] =
2009-06-07 12:56:35 -04:00
{
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 ) ,
2010-01-21 11:52:51 -05:00
dbpli_offsetof ( special_id ) ,
2015-02-23 14:49:53 -05:00
dbpli_offsetof ( virtual_path ) ,
2015-03-14 18:35:19 -04:00
dbpli_offsetof ( parent_id ) ,
2015-12-27 01:16:50 -05:00
dbpli_offsetof ( directory_id ) ,
2018-04-15 02:03:42 -04:00
dbpli_offsetof ( query_order ) ,
2018-04-13 10:29:06 -04:00
dbpli_offsetof ( query_limit ) ,
2019-11-02 19:16:20 -04:00
dbpli_offsetof ( media_kind ) ,
2009-06-11 12:28:01 -04:00
2019-05-28 17:40:41 -04:00
dbpli_offsetof ( items ) ,
dbpli_offsetof ( streams ) ,
2009-06-07 12:56:35 -04:00
} ;
2010-01-10 06:26:48 -05:00
/* This list must be kept in sync with
2013-08-29 16:00:37 -04:00
* - the order of fields in the Q_GROUP_ALBUMS and Q_GROUP_ARTISTS query
2010-01-10 06:26:48 -05:00
* - the name of the fields in struct group_info
*/
2010-03-23 13:55:39 -04:00
static const ssize_t dbgri_cols_map [ ] =
2010-01-10 06:26:48 -05:00
{
2010-03-06 12:56:30 -05:00
dbgri_offsetof ( id ) ,
2010-01-10 06:26:48 -05:00
dbgri_offsetof ( persistentid ) ,
dbgri_offsetof ( itemname ) ,
2013-12-18 13:14:39 -05:00
dbgri_offsetof ( itemname_sort ) ,
2013-12-15 15:47:49 -05:00
dbgri_offsetof ( itemcount ) ,
dbgri_offsetof ( groupalbumcount ) ,
dbgri_offsetof ( songalbumartist ) ,
2014-02-21 14:39:57 -05:00
dbgri_offsetof ( songartistid ) ,
2016-06-11 17:10:13 -04:00
dbgri_offsetof ( song_length ) ,
2010-01-10 06:26:48 -05:00
} ;
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
*/
2010-03-23 13:55:39 -04:00
static const struct col_type_map wi_cols_map [ ] =
2009-06-10 12:11:11 -04:00
{
2019-12-27 18:04:43 -05:00
{ " wd " , wi_offsetof ( wd ) , DB_TYPE_INT , DB_FLAG_NO_BIND } ,
2018-09-23 16:24:24 -04:00
{ " cookie " , wi_offsetof ( cookie ) , DB_TYPE_INT } ,
{ " path " , wi_offsetof ( path ) , DB_TYPE_STRING } ,
2009-06-10 12:11:11 -04:00
} ;
2018-01-20 18:22:21 -05:00
/* Sort clauses, used for ORDER BY */
/* Keep in sync with enum sort_type and indices */
2010-08-05 12:24:50 -04:00
static const char * sort_clause [ ] =
{
" " ,
2018-01-20 18:22:21 -05:00
" f.title_sort " ,
" f.album_sort, f.disc, f.track " ,
" f.album_artist_sort, f.album_sort, f.disc, f.track " ,
" f.type, f.parent_id, f.special_id, f.title " ,
" f.year " ,
" f.genre " ,
" f.composer_sort " ,
" f.disc " ,
" f.track " ,
2019-08-21 16:54:34 -04:00
" f.virtual_path COLLATE NOCASE " ,
2018-01-20 18:22:21 -05:00
" pos " ,
" shuffle_pos " ,
} ;
/* Browse clauses, used for SELECT, WHERE, GROUP BY and for default ORDER BY
* Keep in sync with enum query_type and indices
* Col 1 : for SELECT , Col 2 : for WHERE , Col 3 : for GROUP BY / ORDER BY
*/
static const struct browse_clause browse_clause [ ] =
{
{ " " , " " , " " } ,
{ " f.album_artist, f.album_artist_sort " , " f.album_artist " , " f.album_artist_sort, f.album_artist " } ,
{ " f.album, f.album_sort " , " f.album " , " f.album_sort, f.album " } ,
{ " f.genre, f.genre " , " f.genre " , " f.genre " } ,
{ " f.composer, f.composer_sort " , " f.composer " , " f.composer_sort, f.composer " } ,
{ " f.year, f.year " , " f.year " , " f.year " } ,
{ " f.disc, f.disc " , " f.disc " , " f.disc " } ,
{ " f.track, f.track " , " f.track " , " f.track " } ,
{ " f.virtual_path, f.virtual_path " , " f.virtual_path " , " f.virtual_path " } ,
{ " f.path, f.path " , " f.path " , " f.path " } ,
2010-08-05 12:24:50 -04:00
} ;
2018-02-24 05:28:42 -05:00
struct media_kind_label {
enum media_kind type ;
const char * label ;
} ;
2018-12-28 13:10:17 -05:00
2018-02-24 05:28:42 -05:00
/* Keep in sync with enum media_kind */
static const struct media_kind_label media_kind_labels [ ] =
{
{ MEDIA_KIND_MUSIC , " music " } ,
{ MEDIA_KIND_MOVIE , " movie " } ,
{ MEDIA_KIND_PODCAST , " podcast " } ,
{ MEDIA_KIND_AUDIOBOOK , " audiobook " } ,
{ MEDIA_KIND_MUSICVIDEO , " musicvideo " } ,
{ MEDIA_KIND_TVSHOW , " tvshow " } ,
} ;
const char *
db_media_kind_label ( enum media_kind media_kind )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( media_kind_labels ) ; i + + )
{
if ( media_kind = = media_kind_labels [ i ] . type )
return media_kind_labels [ i ] . label ;
}
return NULL ;
}
enum media_kind
db_media_kind_enum ( const char * label )
{
int i ;
if ( ! label )
return 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( media_kind_labels ) ; i + + )
{
if ( strcmp ( label , media_kind_labels [ i ] . label ) = = 0 )
return media_kind_labels [ i ] . type ;
}
return 0 ;
}
/* Keep in sync with enum data_kind */
static char * data_kind_label [ ] = { " file " , " url " , " spotify " , " pipe " } ;
const char *
db_data_kind_label ( enum data_kind data_kind )
{
if ( data_kind < ARRAY_SIZE ( data_kind_label ) )
{
return data_kind_label [ data_kind ] ;
}
return NULL ;
}
2016-11-04 10:34:00 -04:00
/* Shuffle RNG state */
struct rng_ctx shuffle_rng ;
2010-03-21 06:33:05 -04:00
static char * db_path ;
2018-08-19 03:45:35 -04:00
static bool db_rating_updates ;
2018-09-23 16:24:24 -04:00
2009-06-07 12:56:35 -04:00
static __thread sqlite3 * hdl ;
2018-09-23 16:24:24 -04:00
static __thread struct db_statements db_statements ;
2009-06-07 12:56:35 -04:00
2009-06-11 12:28:01 -04:00
/* Forward */
2014-11-10 16:53:08 -05:00
static enum group_type
db_group_type_bypersistentid ( int64_t persistentid ) ;
2016-11-12 06:35:22 -05:00
static int
2017-12-09 11:12:13 -05:00
db_query_run ( char * query , int free , short update_events ) ;
2016-11-12 06:35:22 -05:00
2009-06-11 12:28:01 -04:00
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 ;
}
2017-10-14 04:14:09 -04:00
// Basically a wrapper for sqlite3_mprintf()
char *
db_mprintf ( const char * fmt , . . . )
{
char * query ;
char * ret ;
va_list va ;
va_start ( va , fmt ) ;
ret = sqlite3_vmprintf ( fmt , va ) ;
if ( ! ret )
{
DPRINTF ( E_FATAL , L_MISC , " Out of memory for db_mprintf \n " ) ;
abort ( ) ;
}
va_end ( va ) ;
query = strdup ( ret ) ;
sqlite3_free ( ret ) ;
return query ;
}
int
db_snprintf ( char * s , int n , const char * fmt , . . . )
{
char * ret ;
va_list va ;
if ( n < 2 )
return - 1 ;
2017-10-14 07:17:21 -04:00
// For size check since sqlite3_vsnprintf does not seem to support it
s [ n - 2 ] = ' \0 ' ;
2017-10-14 04:14:09 -04:00
va_start ( va , fmt ) ;
ret = sqlite3_vsnprintf ( n , s , fmt , va ) ;
va_end ( va ) ;
2017-10-14 07:17:21 -04:00
if ( ! ret | | ( s [ n - 2 ] ! = ' \0 ' ) )
2017-10-14 04:14:09 -04:00
return - 1 ;
return 0 ;
}
2010-01-30 11:01:27 -05:00
void
free_pi ( struct pairing_info * pi , int content_only )
{
2017-01-22 17:10:15 -05:00
if ( ! pi )
return ;
2010-01-30 11:01:27 -05:00
2017-01-22 17:10:15 -05:00
free ( pi - > remote_id ) ;
free ( pi - > name ) ;
free ( pi - > guid ) ;
2010-01-30 11:01:27 -05:00
if ( ! content_only )
free ( pi ) ;
2014-04-18 07:08:31 -04:00
else
memset ( pi , 0 , sizeof ( struct pairing_info ) ) ;
2010-01-30 11:01:27 -05:00
}
2009-06-07 12:56:35 -04:00
void
free_mfi ( struct media_file_info * mfi , int content_only )
{
2017-01-22 17:10:15 -05:00
if ( ! mfi )
return ;
2010-09-21 07:16:38 -04:00
2017-01-22 17:10:15 -05:00
free ( mfi - > path ) ;
free ( mfi - > fname ) ;
free ( mfi - > title ) ;
free ( mfi - > artist ) ;
free ( mfi - > album ) ;
free ( mfi - > genre ) ;
free ( mfi - > comment ) ;
free ( mfi - > type ) ;
free ( mfi - > composer ) ;
free ( mfi - > orchestra ) ;
free ( mfi - > conductor ) ;
free ( mfi - > grouping ) ;
free ( mfi - > description ) ;
free ( mfi - > codectype ) ;
free ( mfi - > album_artist ) ;
free ( mfi - > tv_series_name ) ;
free ( mfi - > tv_episode_num_str ) ;
free ( mfi - > tv_network_name ) ;
free ( mfi - > title_sort ) ;
free ( mfi - > artist_sort ) ;
free ( mfi - > album_sort ) ;
free ( mfi - > composer_sort ) ;
free ( mfi - > album_artist_sort ) ;
free ( mfi - > virtual_path ) ;
2014-12-21 14:41:44 -05:00
2009-06-07 12:56:35 -04:00
if ( ! content_only )
free ( mfi ) ;
2014-04-18 07:08:31 -04:00
else
memset ( mfi , 0 , sizeof ( struct media_file_info ) ) ;
2009-06-07 12:56:35 -04:00
}
2017-10-25 17:32:16 -04:00
void
free_pli ( struct playlist_info * pli , int content_only )
{
if ( ! pli )
return ;
free ( pli - > title ) ;
free ( pli - > query ) ;
free ( pli - > path ) ;
free ( pli - > virtual_path ) ;
2018-04-15 03:33:41 -04:00
free ( pli - > query_order ) ;
2017-10-25 17:32:16 -04:00
if ( ! content_only )
free ( pli ) ;
else
memset ( pli , 0 , sizeof ( struct playlist_info ) ) ;
}
void
free_di ( struct directory_info * di , int content_only )
{
if ( ! di )
return ;
free ( di - > virtual_path ) ;
if ( ! content_only )
free ( di ) ;
else
memset ( di , 0 , sizeof ( struct directory_info ) ) ;
}
void
free_query_params ( struct query_params * qp , int content_only )
{
if ( ! qp )
return ;
free ( qp - > filter ) ;
2018-03-17 08:27:59 -04:00
free ( qp - > having ) ;
2018-04-15 02:03:42 -04:00
free ( qp - > order ) ;
2017-10-25 17:32:16 -04:00
if ( ! content_only )
free ( qp ) ;
else
memset ( qp , 0 , sizeof ( struct query_params ) ) ;
}
void
free_queue_item ( struct db_queue_item * queue_item , int content_only )
{
if ( ! queue_item )
return ;
free ( queue_item - > path ) ;
free ( queue_item - > virtual_path ) ;
free ( queue_item - > title ) ;
free ( queue_item - > artist ) ;
free ( queue_item - > album_artist ) ;
2018-11-21 07:52:13 -05:00
free ( queue_item - > composer ) ;
2017-10-25 17:32:16 -04:00
free ( queue_item - > album ) ;
free ( queue_item - > genre ) ;
free ( queue_item - > artist_sort ) ;
free ( queue_item - > album_sort ) ;
free ( queue_item - > album_artist_sort ) ;
free ( queue_item - > artwork_url ) ;
2019-06-04 16:51:44 -04:00
free ( queue_item - > type ) ;
2017-10-25 17:32:16 -04:00
if ( ! content_only )
free ( queue_item ) ;
else
memset ( queue_item , 0 , sizeof ( struct db_queue_item ) ) ;
}
2018-03-09 13:03:43 -05:00
static void
2018-12-28 13:10:17 -05:00
sort_tag_create ( char * * sort_tag , const char * src_tag )
2018-03-09 13:03:43 -05:00
{
const uint8_t * i_ptr ;
const uint8_t * n_ptr ;
const uint8_t * number ;
uint8_t out [ 1024 ] ;
uint8_t * o_ptr ;
int append_number ;
ucs4_t puc ;
int numlen ;
size_t len ;
int charlen ;
/* Note: include terminating NUL in string length for u8_normalize */
if ( * sort_tag )
{
2019-05-12 17:28:38 -04:00
DPRINTF ( E_DBG , L_DB , " Existing sort tag will be normalized: %s \n " , * sort_tag ) ;
2018-03-09 13:03:43 -05:00
o_ptr = u8_normalize ( UNINORM_NFD , ( uint8_t * ) * sort_tag , strlen ( * sort_tag ) + 1 , NULL , & len ) ;
free ( * sort_tag ) ;
* sort_tag = ( char * ) o_ptr ;
return ;
}
if ( ! src_tag | | ( ( len = strlen ( src_tag ) ) = = 0 ) )
{
* sort_tag = NULL ;
return ;
}
// Set input pointer past article if present
if ( ( strncasecmp ( src_tag , " a " , 2 ) = = 0 ) & & ( len > 2 ) )
i_ptr = ( uint8_t * ) ( src_tag + 2 ) ;
else if ( ( strncasecmp ( src_tag , " an " , 3 ) = = 0 ) & & ( len > 3 ) )
i_ptr = ( uint8_t * ) ( src_tag + 3 ) ;
else if ( ( strncasecmp ( src_tag , " the " , 4 ) = = 0 ) & & ( len > 4 ) )
i_ptr = ( uint8_t * ) ( src_tag + 4 ) ;
else
i_ptr = ( uint8_t * ) src_tag ;
// Poor man's natural sort. Makes sure we sort like this: a1, a2, a10, a11, a21, a111
// We do this by padding zeroes to (short) numbers. As an alternative we could have
// made a proper natural sort algorithm in sqlext.c, but we don't, since we don't
// want any risk of hurting response times
memset ( & out , 0 , sizeof ( out ) ) ;
o_ptr = ( uint8_t * ) & out ;
number = NULL ;
append_number = 0 ;
do
{
n_ptr = u8_next ( & puc , i_ptr ) ;
if ( uc_is_digit ( puc ) )
{
if ( ! number ) // We have encountered the beginning of a number
number = i_ptr ;
append_number = ( n_ptr = = NULL ) ; // If last char in string append number now
}
else
{
if ( number )
append_number = 1 ; // A number has ended so time to append it
else
{
charlen = u8_strmblen ( i_ptr ) ;
if ( charlen > = 0 )
o_ptr = u8_stpncpy ( o_ptr , i_ptr , charlen ) ; // No numbers in sight, just append char
}
}
// Break if less than 100 bytes remain (prevent buffer overflow)
if ( sizeof ( out ) - u8_strlen ( out ) < 100 )
break ;
// Break if number is very large (prevent buffer overflow)
if ( number & & ( i_ptr - number > 50 ) )
break ;
if ( append_number )
{
numlen = i_ptr - number ;
if ( numlen < 5 ) // Max pad width
{
u8_strcpy ( o_ptr , ( uint8_t * ) " 00000 " ) ;
o_ptr + = ( 5 - numlen ) ;
}
o_ptr = u8_stpncpy ( o_ptr , number , numlen + u8_strmblen ( i_ptr ) ) ;
number = NULL ;
append_number = 0 ;
}
i_ptr = n_ptr ;
}
while ( n_ptr ) ;
* sort_tag = ( char * ) u8_normalize ( UNINORM_NFD , ( uint8_t * ) & out , u8_strlen ( out ) + 1 , NULL , & len ) ;
}
2018-12-28 13:10:17 -05:00
static void
fixup_sanitize ( char * * tag , enum fixup_type fixup , struct fixup_ctx * ctx )
2018-03-09 13:03:43 -05:00
{
2018-12-28 13:10:17 -05:00
char * ret ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
if ( ! tag | | ! * tag )
return ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
switch ( fixup )
2018-03-09 13:03:43 -05:00
{
2018-12-28 13:10:17 -05:00
case DB_FIXUP_NO_SANITIZE :
case DB_FIXUP_CODECTYPE :
break ; // Don't touch the above
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
default :
trim ( * tag ) ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
// By default we set empty strings to NULL
if ( * tag [ 0 ] = = ' \0 ' )
{
free ( * tag ) ;
* tag = NULL ;
break ;
}
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
ret = unicode_fixup_string ( * tag , " ascii " ) ;
if ( ret ! = * tag )
{
free ( * tag ) ;
* tag = ret ;
}
2018-03-09 13:03:43 -05:00
}
2018-12-28 13:10:17 -05:00
}
static void
fixup_defaults ( char * * tag , enum fixup_type fixup , struct fixup_ctx * ctx )
{
char * ca ;
switch ( fixup )
2018-03-09 13:03:43 -05:00
{
2019-05-12 17:28:38 -04:00
case DB_FIXUP_SONGARTISTID :
if ( ctx - > mfi & & ctx - > mfi - > songartistid = = 0 )
ctx - > mfi - > songartistid = two_str_hash ( ctx - > mfi - > album_artist , NULL ) ;
break ;
case DB_FIXUP_SONGALBUMID :
if ( ctx - > mfi & & ctx - > mfi - > songalbumid = = 0 )
ctx - > mfi - > songalbumid = two_str_hash ( ctx - > mfi - > album_artist , ctx - > mfi - > album ) ;
break ;
2018-12-28 13:10:17 -05:00
case DB_FIXUP_TITLE :
if ( * tag )
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
// fname is left untouched by fixup_sanitize() for obvious reasons, so ensure it is proper UTF-8
if ( ctx - > mfi & & ctx - > mfi - > fname )
{
* tag = unicode_fixup_string ( ctx - > mfi - > fname , " ascii " ) ;
if ( * tag = = ctx - > mfi - > fname )
* tag = strdup ( ctx - > mfi - > fname ) ;
}
else if ( ctx - > pli & & ctx - > pli - > path )
* tag = strdup ( ctx - > pli - > path ) ;
else if ( ctx - > queue_item & & ctx - > queue_item - > path )
* tag = strdup ( ctx - > queue_item - > path ) ;
else
* tag = strdup ( " Unknown title " ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ARTIST :
if ( * tag )
break ;
2018-03-09 13:03:43 -05:00
2018-12-29 19:11:11 -05:00
if ( ctx - > mfi & & ctx - > mfi - > album_artist )
* tag = strdup ( ctx - > mfi - > album_artist ) ;
else if ( ctx - > mfi & & ctx - > mfi - > orchestra & & ctx - > mfi - > conductor )
2018-12-28 13:10:17 -05:00
* tag = safe_asprintf ( " %s - %s " , ctx - > mfi - > orchestra , ctx - > mfi - > conductor ) ;
else if ( ctx - > mfi & & ctx - > mfi - > orchestra )
* tag = strdup ( ctx - > mfi - > orchestra ) ;
else if ( ctx - > mfi & & ctx - > mfi - > conductor )
* tag = strdup ( ctx - > mfi - > conductor ) ;
else if ( ctx - > mfi & & ctx - > mfi - > tv_series_name )
* tag = strdup ( ctx - > mfi - > tv_series_name ) ;
else
* tag = strdup ( " Unknown artist " ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ALBUM :
if ( * tag )
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
if ( ctx - > mfi & & ctx - > mfi - > tv_series_name )
* tag = safe_asprintf ( " %s, Season %u " , ctx - > mfi - > tv_series_name , ctx - > mfi - > tv_season_num ) ;
else
* tag = strdup ( " Unknown album " ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ALBUM_ARTIST : // Will be set after artist, because artist (must) come first in the col_maps
if ( ctx - > mfi & & ctx - > mfi - > media_kind = = MEDIA_KIND_PODCAST )
{
free ( * tag ) ;
* tag = strdup ( " " ) ;
}
if ( * tag )
break ;
2018-03-09 13:03:43 -05:00
2018-12-30 04:54:05 -05:00
if ( ctx - > mfi & & ctx - > mfi - > compilation & & ( ca = cfg_getstr ( cfg_getsec ( cfg , " library " ) , " compilation_artist " ) ) )
* tag = strdup ( ca ) ; // If ca is empty string then the artist will not be shown in artist view
else if ( ctx - > mfi & & ctx - > mfi - > artist )
2018-12-28 13:10:17 -05:00
* tag = strdup ( ctx - > mfi - > artist ) ;
else if ( ctx - > queue_item & & ctx - > queue_item - > artist )
* tag = strdup ( ctx - > queue_item - > artist ) ;
else
2018-12-30 04:54:05 -05:00
* tag = strdup ( " Unknown artist " ) ;
2018-12-28 13:10:17 -05:00
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_GENRE :
if ( * tag )
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
* tag = strdup ( " Unknown genre " ) ;
break ;
case DB_FIXUP_MEDIA_KIND :
if ( ctx - > mfi & & ctx - > mfi - > tv_series_name )
ctx - > mfi - > media_kind = MEDIA_KIND_TVSHOW ;
2018-12-30 10:13:57 -05:00
else if ( ctx - > mfi & & ! ctx - > mfi - > media_kind )
ctx - > mfi - > media_kind = MEDIA_KIND_MUSIC ;
2019-12-27 18:04:43 -05:00
else if ( ctx - > pli & & ! ctx - > pli - > media_kind )
ctx - > pli - > media_kind = MEDIA_KIND_MUSIC ;
2018-12-30 10:13:57 -05:00
else if ( ctx - > queue_item & & ! ctx - > queue_item - > media_kind )
ctx - > queue_item - > media_kind = MEDIA_KIND_MUSIC ;
break ;
case DB_FIXUP_ITEM_KIND :
if ( ctx - > mfi & & ! ctx - > mfi - > item_kind )
ctx - > mfi - > item_kind = 2 ; // music
2018-12-28 13:10:17 -05:00
break ;
case DB_FIXUP_TIME_MODIFIED :
if ( ctx - > mfi & & ctx - > mfi - > time_modified = = 0 )
ctx - > mfi - > time_modified = ctx - > mfi - > db_timestamp ;
break ;
case DB_FIXUP_CODECTYPE :
case DB_FIXUP_TYPE :
// Default to mpeg4 video/audio for unknown file types in an attempt to allow streaming of DRM-afflicted files
if ( ctx - > mfi & & ctx - > mfi - > codectype & & strcmp ( ctx - > mfi - > codectype , " unkn " ) = = 0 )
{
if ( ctx - > mfi - > has_video )
{
strcpy ( ctx - > mfi - > codectype , " mp4v " ) ;
strcpy ( ctx - > mfi - > type , " m4v " ) ;
}
else
{
strcpy ( ctx - > mfi - > codectype , " mp4a " ) ;
strcpy ( ctx - > mfi - > type , " m4a " ) ;
}
}
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
default :
break ;
}
2018-03-09 13:03:43 -05:00
}
static void
2018-12-28 13:10:17 -05:00
fixup_sort_tags ( char * * tag , enum fixup_type fixup , struct fixup_ctx * ctx )
2018-03-09 13:03:43 -05:00
{
2018-12-28 13:10:17 -05:00
switch ( fixup )
2018-03-09 13:03:43 -05:00
{
2018-12-28 13:10:17 -05:00
case DB_FIXUP_TITLE_SORT :
if ( ctx - > mfi )
sort_tag_create ( tag , ctx - > mfi - > title ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ARTIST_SORT :
if ( ctx - > mfi )
sort_tag_create ( tag , ctx - > mfi - > artist ) ;
else if ( ctx - > queue_item )
sort_tag_create ( tag , ctx - > queue_item - > artist ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ALBUM_SORT :
if ( ctx - > mfi )
sort_tag_create ( tag , ctx - > mfi - > album ) ;
else if ( ctx - > queue_item )
sort_tag_create ( tag , ctx - > queue_item - > album ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_ALBUM_ARTIST_SORT :
if ( ctx - > mfi )
sort_tag_create ( tag , ctx - > mfi - > album_artist ) ;
else if ( ctx - > queue_item )
sort_tag_create ( tag , ctx - > queue_item - > album_artist ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
case DB_FIXUP_COMPOSER_SORT :
if ( ctx - > mfi )
sort_tag_create ( tag , ctx - > mfi - > composer ) ;
break ;
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
default :
break ;
2018-03-09 13:03:43 -05:00
}
2018-12-28 13:10:17 -05:00
}
static void
fixup_tags ( struct fixup_ctx * ctx )
{
void ( * fixup_func [ ] ) ( char * * , enum fixup_type , struct fixup_ctx * ) = { fixup_sanitize , fixup_defaults , fixup_sort_tags } ;
char * * tag ;
int i ;
int j ;
for ( i = 0 ; i < ARRAY_SIZE ( fixup_func ) ; i + + )
2018-03-09 13:03:43 -05:00
{
2018-12-28 13:10:17 -05:00
for ( j = 0 ; j < ctx - > map_size ; j + + )
{
switch ( ctx - > map [ j ] . type )
{
case DB_TYPE_STRING :
tag = ( char * * ) ( ( char * ) ctx - > data + ctx - > map [ j ] . offset ) ;
fixup_func [ i ] ( tag , ctx - > map [ j ] . fixup , ctx ) ;
break ;
case DB_TYPE_INT :
case DB_TYPE_INT64 :
fixup_func [ i ] ( NULL , ctx - > map [ j ] . fixup , ctx ) ;
break ;
}
}
2018-03-09 13:03:43 -05:00
}
2018-12-28 13:10:17 -05:00
}
2018-03-09 13:03:43 -05:00
2018-12-28 13:10:17 -05:00
static void
fixup_tags_mfi ( struct media_file_info * mfi )
{
struct fixup_ctx ctx = { 0 } ;
ctx . data = mfi ;
ctx . mfi = mfi ;
ctx . map = mfi_cols_map ;
ctx . map_size = ARRAY_SIZE ( mfi_cols_map ) ;
fixup_tags ( & ctx ) ;
}
static void
fixup_tags_pli ( struct playlist_info * pli )
{
struct fixup_ctx ctx = { 0 } ;
ctx . data = pli ;
ctx . pli = pli ;
ctx . map = pli_cols_map ;
ctx . map_size = ARRAY_SIZE ( pli_cols_map ) ;
fixup_tags ( & ctx ) ;
}
static void
fixup_tags_queue_item ( struct db_queue_item * queue_item )
{
struct fixup_ctx ctx = { 0 } ;
ctx . data = queue_item ;
ctx . queue_item = queue_item ;
ctx . map = qi_cols_map ;
ctx . map_size = ARRAY_SIZE ( qi_cols_map ) ;
fixup_tags ( & ctx ) ;
2018-03-09 13:03:43 -05:00
}
2018-09-23 16:24:24 -04:00
static int
2019-12-27 18:04:43 -05:00
bind_generic ( sqlite3_stmt * stmt , void * data , const struct col_type_map * map , size_t map_size , int id )
2018-09-23 16:24:24 -04:00
{
char * * strptr ;
char * ptr ;
int i ;
int n ;
2019-12-27 18:04:43 -05:00
for ( i = 0 , n = 1 ; i < map_size ; i + + )
2018-09-23 16:24:24 -04:00
{
2019-12-27 18:04:43 -05:00
if ( map [ i ] . flag & DB_FLAG_NO_BIND )
2018-09-23 16:24:24 -04:00
continue ;
2019-12-27 18:04:43 -05:00
ptr = data + map [ i ] . offset ;
strptr = ( char * * ) ( data + map [ i ] . offset ) ;
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
switch ( map [ i ] . type )
2018-09-23 16:24:24 -04:00
{
case DB_TYPE_INT :
2019-01-30 17:34:00 -05:00
sqlite3_bind_int64 ( stmt , n , * ( ( uint32_t * ) ptr ) ) ; // Use _int64 because _int is for signed int32
2018-09-23 16:24:24 -04:00
break ;
case DB_TYPE_INT64 :
sqlite3_bind_int64 ( stmt , n , * ( ( uint64_t * ) ptr ) ) ;
break ;
case DB_TYPE_STRING :
sqlite3_bind_text ( stmt , n , * strptr , - 1 , SQLITE_STATIC ) ; // TODO should we use _TRANSIENT?
break ;
default :
2019-12-27 18:04:43 -05:00
DPRINTF ( E_LOG , L_DB , " BUG: Unknown type %d in column map \n " , map [ i ] . type ) ;
2018-09-23 16:24:24 -04:00
return - 1 ;
}
n + + ;
}
// This binds the final "WHERE id = ?" if it is an update
2019-12-27 18:04:43 -05:00
if ( id )
sqlite3_bind_int ( stmt , n , id ) ;
2018-09-23 16:24:24 -04:00
return 0 ;
}
2019-12-27 18:04:43 -05:00
static int
bind_mfi ( sqlite3_stmt * stmt , struct media_file_info * mfi )
{
return bind_generic ( stmt , mfi , mfi_cols_map , ARRAY_SIZE ( mfi_cols_map ) , mfi - > id ) ;
}
static int
bind_pli ( sqlite3_stmt * stmt , struct playlist_info * pli )
{
return bind_generic ( stmt , pli , pli_cols_map , ARRAY_SIZE ( pli_cols_map ) , pli - > id ) ;
}
2010-04-26 12:24:09 -04:00
2010-04-26 12:24:09 -04:00
/* Unlock notification support */
static void
unlock_notify_cb ( void * * args , int nargs )
{
struct db_unlock * u ;
int i ;
for ( i = 0 ; i < nargs ; i + + )
{
u = ( struct db_unlock * ) args [ i ] ;
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_mutex_lock ( & u - > lck ) ) ;
2010-04-26 12:24:09 -04:00
u - > proceed = 1 ;
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_cond_signal ( & u - > cond ) ) ;
2010-04-26 12:24:09 -04:00
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_mutex_unlock ( & u - > lck ) ) ;
2010-04-26 12:24:09 -04:00
}
}
static int
db_wait_unlock ( void )
{
struct db_unlock u ;
int ret ;
u . proceed = 0 ;
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , mutex_init ( & u . lck ) ) ;
CHECK_ERR ( L_DB , pthread_cond_init ( & u . cond , NULL ) ) ;
2010-04-26 12:24:09 -04:00
ret = sqlite3_unlock_notify ( hdl , unlock_notify_cb , & u ) ;
if ( ret = = SQLITE_OK )
{
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_mutex_lock ( & u . lck ) ) ;
2010-04-26 12:24:09 -04:00
if ( ! u . proceed )
2014-05-13 16:02:32 -04:00
{
DPRINTF ( E_INFO , L_DB , " Waiting for database unlock \n " ) ;
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_cond_wait ( & u . cond , & u . lck ) ) ;
2014-05-13 16:02:32 -04:00
}
2010-04-26 12:24:09 -04:00
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_mutex_unlock ( & u . lck ) ) ;
2010-04-26 12:24:09 -04:00
}
2017-01-21 10:11:20 -05:00
CHECK_ERR ( L_DB , pthread_cond_destroy ( & u . cond ) ) ;
CHECK_ERR ( L_DB , pthread_mutex_destroy ( & u . lck ) ) ;
2010-04-26 12:24:09 -04:00
return ret ;
}
static int
db_blocking_step ( sqlite3_stmt * stmt )
{
int ret ;
while ( ( ret = sqlite3_step ( stmt ) ) = = SQLITE_LOCKED )
{
ret = db_wait_unlock ( ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Database deadlocked! \n " ) ;
break ;
}
sqlite3_reset ( stmt ) ;
}
return ret ;
}
static int
db_blocking_prepare_v2 ( const char * query , int len , sqlite3_stmt * * stmt , const char * * end )
{
int ret ;
while ( ( ret = sqlite3_prepare_v2 ( hdl , query , len , stmt , end ) ) = = SQLITE_LOCKED )
{
ret = db_wait_unlock ( ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Database deadlocked! \n " ) ;
break ;
}
}
return ret ;
}
2018-09-23 16:24:24 -04:00
static int
db_statement_run ( sqlite3_stmt * stmt )
{
int ret ;
# ifdef HAVE_SQLITE3_EXPANDED_SQL
char * query ;
if ( logger_severity ( ) > = E_DBG )
{
query = sqlite3_expanded_sql ( stmt ) ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
sqlite3_free ( query ) ;
}
# else
DPRINTF ( E_DBG , L_DB , " Running query (prepared statement) \n " ) ;
# endif
while ( ( ret = db_blocking_step ( stmt ) ) = = SQLITE_ROW )
; /* EMPTY */
if ( ret ! = SQLITE_DONE )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
}
sqlite3_reset ( stmt ) ;
sqlite3_clear_bindings ( stmt ) ;
return ( ret = = SQLITE_DONE ) ? sqlite3_changes ( hdl ) : - 1 ;
}
2010-04-26 12:24:09 -04:00
2010-04-26 12:24:09 -04:00
/* Modelled after sqlite3_exec() */
static int
db_exec ( const char * query , char * * errmsg )
{
sqlite3_stmt * stmt ;
2011-05-28 04:48:31 -04:00
int try ;
2010-04-26 12:24:09 -04:00
int ret ;
* errmsg = NULL ;
2011-05-28 04:48:31 -04:00
for ( try = 0 ; try < 5 ; try + + )
2010-04-26 12:24:09 -04:00
{
2011-05-28 04:48:31 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
* errmsg = sqlite3_mprintf ( " prepare failed: %s " , sqlite3_errmsg ( hdl ) ) ;
return ret ;
}
while ( ( ret = db_blocking_step ( stmt ) ) = = SQLITE_ROW )
; /* EMPTY */
2010-04-26 12:24:09 -04:00
2011-05-28 04:48:31 -04:00
sqlite3_finalize ( stmt ) ;
if ( ret ! = SQLITE_SCHEMA )
break ;
}
2010-04-26 12:24:09 -04:00
if ( ret ! = SQLITE_DONE )
{
2011-05-28 04:46:41 -04:00
* errmsg = sqlite3_mprintf ( " step failed: %s " , sqlite3_errmsg ( hdl ) ) ;
2010-04-26 12:24:09 -04:00
return ret ;
}
return SQLITE_OK ;
}
2010-12-05 06:22:52 -05:00
/* Maintenance and DB hygiene */
static void
2018-02-23 13:44:40 -05:00
db_pragma_optimize ( void )
2010-12-05 06:22:52 -05:00
{
2018-06-16 15:43:58 -04:00
const char * query = " ANALYZE; " ;
2010-12-05 06:22:52 -05:00
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
2018-06-16 15:43:58 -04:00
DPRINTF ( E_LOG , L_DB , " ANALYZE failed: %s \n " , errmsg ) ;
2010-12-05 06:22:52 -05:00
sqlite3_free ( errmsg ) ;
}
}
2013-11-30 06:57:38 -05:00
/* Set names of default playlists according to config */
static void
db_set_cfg_names ( void )
{
2015-03-29 19:03:15 -04:00
# define Q_TMPL "UPDATE playlists SET title = '%q' WHERE type = %d AND special_id = %d;"
2013-12-08 17:03:03 -05:00
char * cfg_item [ 6 ] = { " name_library " , " name_music " , " name_movies " , " name_tvshows " , " name_podcasts " , " name_audiobooks " } ;
char special_id [ 6 ] = { 0 , 6 , 4 , 5 , 1 , 7 } ;
2013-11-30 06:57:38 -05:00
cfg_t * lib ;
char * query ;
char * title ;
char * errmsg ;
int ret ;
int i ;
lib = cfg_getsec ( cfg , " library " ) ;
for ( i = 0 ; i < ( sizeof ( cfg_item ) / sizeof ( cfg_item [ 0 ] ) ) ; i + + )
{
title = cfg_getstr ( lib , cfg_item [ i ] ) ;
if ( ! title )
{
DPRINTF ( E_LOG , L_DB , " Internal error, unknown config item '%s' \n " , cfg_item [ i ] ) ;
continue ;
}
2015-04-11 10:55:13 -04:00
query = sqlite3_mprintf ( Q_TMPL , title , PL_SPECIAL , special_id [ i ] ) ;
2013-11-30 06:57:38 -05:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Error setting playlist title, query %s, error: %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
else
DPRINTF ( E_DBG , L_DB , " Playlist title for config item '%s' set with query '%s' \n " , cfg_item [ i ] , query ) ;
sqlite3_free ( query ) ;
}
# undef Q_TMPL
}
2010-12-05 06:22:52 -05:00
void
db_hook_post_scan ( void )
{
DPRINTF ( E_DBG , L_DB , " Running post-scan DB maintenance tasks... \n " ) ;
2018-02-23 13:44:40 -05:00
db_pragma_optimize ( ) ;
2010-12-05 06:22:52 -05:00
DPRINTF ( E_DBG , L_DB , " Done with post-scan DB maintenance \n " ) ;
}
2009-06-07 14:49:13 -04:00
void
db_purge_cruft ( time_t ref )
{
2016-11-12 06:35:22 -05:00
# define Q_TMPL "DELETE FROM directories WHERE id >= %d AND db_timestamp < %" PRIi64 ";"
2009-06-07 14:49:13 -04:00
int i ;
int ret ;
2016-11-12 06:35:22 -05:00
char * query ;
2017-10-14 03:12:52 -04:00
char * queries_tmpl [ 4 ] =
2009-06-07 14:49:13 -04:00
{
2017-10-14 03:12:52 -04:00
" DELETE FROM playlistitems WHERE playlistid IN (SELECT p.id FROM playlists p WHERE p.type <> %d AND p.db_timestamp < % " PRIi64 " ); " ,
" DELETE FROM playlistitems WHERE filepath IN (SELECT f.path FROM files f WHERE -1 <> %d AND f.db_timestamp < % " PRIi64 " ); " ,
2015-03-29 19:03:15 -04:00
" DELETE FROM playlists WHERE type <> %d AND db_timestamp < % " PRIi64 " ; " ,
2015-12-27 01:16:50 -05:00
" DELETE FROM files WHERE -1 <> %d AND db_timestamp < % " PRIi64 " ; " ,
2009-06-07 14:49:13 -04:00
} ;
2016-12-27 15:31:01 -05:00
db_transaction_begin ( ) ;
2009-06-07 14:49:13 -04:00
for ( i = 0 ; i < ( sizeof ( queries_tmpl ) / sizeof ( queries_tmpl [ 0 ] ) ) ; i + + )
{
2016-11-12 06:35:22 -05:00
query = sqlite3_mprintf ( queries_tmpl [ i ] , PL_SPECIAL , ( int64_t ) ref ) ;
if ( ! query )
2009-06-07 14:49:13 -04:00
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
2016-12-27 15:31:01 -05:00
db_transaction_end ( ) ;
2016-11-12 06:35:22 -05:00
return ;
2009-06-07 14:49:13 -04:00
}
2016-11-12 06:35:22 -05:00
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , query ) ;
2009-06-07 14:49:13 -04:00
2016-11-12 06:35:22 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
2009-06-07 14:49:13 -04:00
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
}
2016-11-12 06:35:22 -05:00
query = sqlite3_mprintf ( Q_TMPL , DIR_MAX , ( int64_t ) ref ) ;
if ( ! query )
2009-06-07 14:49:13 -04:00
{
2016-11-12 06:35:22 -05:00
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
2016-12-27 15:31:01 -05:00
db_transaction_end ( ) ;
2016-11-12 06:35:22 -05:00
return ;
2009-06-07 14:49:13 -04:00
}
2016-11-12 06:35:22 -05:00
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , query ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2016-11-12 06:35:22 -05:00
if ( ret = = 0 )
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
2016-12-27 15:31:01 -05:00
db_transaction_end ( ) ;
2016-11-12 06:35:22 -05:00
# undef Q_TMPL
2009-06-07 14:49:13 -04:00
}
2013-11-29 16:48:53 -05:00
void
db_purge_all ( void )
{
2017-01-06 04:45:25 -05:00
# define Q_TMPL_PL "DELETE FROM playlists WHERE type <> %d;"
# define Q_TMPL_DIR "DELETE FROM directories WHERE id >= %d;"
2015-05-05 14:51:59 -04:00
char * queries [ 4 ] =
2013-11-29 16:48:53 -05:00
{
" DELETE FROM inotify; " ,
" DELETE FROM playlistitems; " ,
2013-12-16 16:21:04 -05:00
" DELETE FROM files; " ,
" DELETE FROM groups; " ,
2013-11-29 16:48:53 -05:00
} ;
char * errmsg ;
2015-03-29 19:03:15 -04:00
char * query ;
2013-11-29 16:48:53 -05:00
int i ;
int ret ;
for ( i = 0 ; i < ( sizeof ( queries ) / sizeof ( queries [ 0 ] ) ) ; i + + )
{
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , queries [ i ] ) ;
ret = db_exec ( queries [ i ] , & 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 ) ) ;
}
2015-03-29 19:03:15 -04:00
2017-01-06 04:45:25 -05:00
// Purge playlists
query = sqlite3_mprintf ( Q_TMPL_PL , PL_SPECIAL ) ;
2015-03-29 19:03:15 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Purge query '%s' error: %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
else
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
sqlite3_free ( query ) ;
2017-01-06 04:45:25 -05:00
// Purge directories
query = sqlite3_mprintf ( Q_TMPL_DIR , DIR_MAX ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
DPRINTF ( E_DBG , L_DB , " Running purge query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Purge query '%s' error: %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
else
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
sqlite3_free ( query ) ;
# undef Q_TMPL_PL
# undef Q_TMPL_DIR
2013-11-29 16:48:53 -05:00
}
2009-06-07 12:56:35 -04:00
static int
2017-01-04 16:29:47 -05:00
db_get_one_int ( const 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 ) ;
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_ROW )
{
2017-05-01 07:28:46 -04:00
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No matching row found for query: %s \n " , query ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s (%s) \n " , sqlite3_errmsg ( hdl ) , query ) ;
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
return - 1 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
return ret ;
}
2009-06-11 09:45:49 -04:00
2014-05-11 17:13:09 -04:00
/* Transactions */
void
db_transaction_begin ( void )
{
char * query = " BEGIN TRANSACTION; " ;
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " SQL error running '%s': %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
}
void
db_transaction_end ( void )
{
char * query = " END TRANSACTION; " ;
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " SQL error running '%s': %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
}
2016-11-04 10:34:00 -04:00
void
db_transaction_rollback ( void )
{
char * query = " ROLLBACK TRANSACTION; " ;
char * errmsg ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " SQL error running '%s': %s \n " , query , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
}
2017-01-04 16:29:47 -05:00
static void
db_free_query_clause ( struct query_clause * qc )
{
if ( ! qc )
return ;
2014-05-11 17:13:09 -04:00
2017-01-04 16:29:47 -05:00
sqlite3_free ( qc - > where ) ;
2018-01-20 18:22:21 -05:00
sqlite3_free ( qc - > group ) ;
2018-03-17 08:27:59 -04:00
sqlite3_free ( qc - > having ) ;
2018-01-20 18:22:21 -05:00
sqlite3_free ( qc - > order ) ;
2017-01-04 16:29:47 -05:00
sqlite3_free ( qc - > index ) ;
free ( qc ) ;
}
static struct query_clause *
db_build_query_clause ( struct query_params * qp )
2009-06-07 12:56:35 -04:00
{
2017-01-04 16:29:47 -05:00
struct query_clause * qc ;
qc = calloc ( 1 , sizeof ( struct query_clause ) ) ;
if ( ! qc )
goto error ;
2009-06-07 12:56:35 -04:00
2018-01-20 18:22:21 -05:00
if ( qp - > type & Q_F_BROWSE )
qc - > group = sqlite3_mprintf ( " GROUP BY %s " , browse_clause [ qp - > type & ~ Q_F_BROWSE ] . group ) ;
2018-09-22 03:32:05 -04:00
else if ( qp - > group )
qc - > group = sqlite3_mprintf ( " GROUP BY %s " , qp - > group ) ;
else
qc - > group = sqlite3_mprintf ( " " ) ;
2018-01-20 18:22:21 -05:00
2017-01-04 16:29:47 -05:00
if ( qp - > filter )
qc - > where = sqlite3_mprintf ( " WHERE f.disabled = 0 AND %s " , qp - > filter ) ;
else
qc - > where = sqlite3_mprintf ( " WHERE f.disabled = 0 " ) ;
2018-03-17 08:27:59 -04:00
if ( qp - > having & & ( qp - > type & ( Q_GROUP_ALBUMS | Q_GROUP_ARTISTS ) ) )
2018-09-22 03:32:05 -04:00
qc - > having = sqlite3_mprintf ( " HAVING %s " , qp - > having ) ;
else
qc - > having = sqlite3_mprintf ( " " ) ;
2018-03-17 08:27:59 -04:00
2018-04-15 02:03:42 -04:00
if ( qp - > order )
qc - > order = sqlite3_mprintf ( " ORDER BY %s " , qp - > order ) ;
2018-03-17 08:27:59 -04:00
else if ( qp - > sort )
2018-01-20 18:22:21 -05:00
qc - > order = sqlite3_mprintf ( " ORDER BY %s " , sort_clause [ qp - > sort ] ) ;
else if ( qp - > type & Q_F_BROWSE )
qc - > order = sqlite3_mprintf ( " ORDER BY %s " , browse_clause [ qp - > type & ~ Q_F_BROWSE ] . group ) ;
2017-01-04 16:29:47 -05:00
else
2018-01-20 18:22:21 -05:00
qc - > order = sqlite3_mprintf ( " " ) ;
2015-09-29 16:51:23 -04:00
2009-06-07 12:56:35 -04:00
switch ( qp - > idx_type )
{
case I_FIRST :
2017-01-04 16:29:47 -05:00
qc - > index = sqlite3_mprintf ( " LIMIT %d " , qp - > limit ) ;
2009-06-07 12:56:35 -04:00
break ;
case I_LAST :
2017-01-04 16:29:47 -05:00
qc - > index = sqlite3_mprintf ( " LIMIT -1 OFFSET %d " , qp - > results - qp - > limit ) ;
2009-06-07 12:56:35 -04:00
break ;
case I_SUB :
2018-01-20 18:22:21 -05:00
if ( qp - > limit )
qc - > index = sqlite3_mprintf ( " LIMIT %d OFFSET %d " , qp - > limit , qp - > offset ) ;
else
qc - > index = sqlite3_mprintf ( " LIMIT -1 OFFSET %d " , qp - > offset ) ;
2009-06-07 12:56:35 -04:00
break ;
case I_NONE :
2017-01-04 16:29:47 -05:00
qc - > index = sqlite3_mprintf ( " " ) ;
break ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 16:29:47 -05:00
if ( ! qc - > where | | ! qc - > index )
goto error ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
return qc ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
error :
DPRINTF ( E_LOG , L_DB , " Error building query clause \n " ) ;
db_free_query_clause ( qc ) ;
return NULL ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 16:29:47 -05:00
static char *
db_build_query_check ( struct query_params * qp , char * count , char * query )
2009-06-07 12:56:35 -04:00
{
2017-01-04 16:29:47 -05:00
if ( ! count | | ! query )
2009-06-07 12:56:35 -04:00
{
2017-01-04 16:29:47 -05:00
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
goto failed ;
2009-06-07 12:56:35 -04:00
}
2016-12-09 10:20:03 -05:00
qp - > results = db_get_one_int ( count ) ;
2009-06-07 12:56:35 -04:00
if ( qp - > results < 0 )
2018-09-23 16:24:24 -04:00
{
DPRINTF ( E_LOG , L_DB , " No results for count \n " ) ;
goto failed ;
}
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
sqlite3_free ( count ) ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
return query ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
failed :
sqlite3_free ( count ) ;
sqlite3_free ( query ) ;
return NULL ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_items ( struct query_params * qp , struct query_clause * qc )
2009-06-07 12:56:35 -04:00
{
2017-01-04 16:29:47 -05:00
char * count ;
2009-06-07 12:56:35 -04:00
char * query ;
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f %s; " , qc - > where ) ;
2018-09-22 03:32:05 -04:00
query = sqlite3_mprintf ( " SELECT f.* FROM files f %s %s %s %s; " , qc - > where , qc - > group , qc - > order , qc - > index ) ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_pls ( struct query_params * qp , struct query_clause * qc )
2009-06-07 12:56:35 -04:00
{
char * count ;
2017-01-04 16:29:47 -05:00
char * query ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM playlists f %s; " , qc - > where ) ;
2019-05-28 17:40:41 -04:00
query = sqlite3_mprintf ( Q_PL_SELECT " %s GROUP BY f.id %s %s; " , qc - > where , qc - > order , qc - > index ) ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 17:20:59 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_find_pls ( struct query_params * qp , struct query_clause * qc )
2017-01-04 17:20:59 -05:00
{
if ( ! qp - > filter )
{
DPRINTF ( E_LOG , L_DB , " Bug! Playlist find called without search criteria \n " ) ;
return NULL ;
}
// Use qp->filter because qc->where has a f.disabled which is not a column in playlistitems
2019-05-28 17:40:41 -04:00
sqlite3_free ( qc - > where ) ;
qc - > where = sqlite3_mprintf ( " WHERE f.id IN (SELECT playlistid FROM playlistitems WHERE %s) " , qp - > filter ) ;
2017-01-04 17:20:59 -05:00
2019-05-28 17:40:41 -04:00
return db_build_query_pls ( qp , qc ) ;
2017-01-04 17:20:59 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_plitems_plain ( struct query_params * qp , struct query_clause * qc )
2010-01-21 11:52:51 -05:00
{
char * count ;
2017-01-04 16:29:47 -05:00
char * query ;
2010-01-21 11:52:51 -05:00
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f JOIN playlistitems pi ON f.path = pi.filepath %s AND pi.playlistid = %d; " , qc - > where , qp - > id ) ;
query = sqlite3_mprintf ( " SELECT f.* FROM files f JOIN playlistitems pi ON f.path = pi.filepath %s AND pi.playlistid = %d ORDER BY pi.id ASC %s; " , qc - > where , qp - > id , qc - > index ) ;
2010-01-21 11:52:51 -05:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
}
2010-08-03 17:29:16 -04:00
2017-01-04 16:29:47 -05:00
static char *
2018-04-13 10:29:06 -04:00
db_build_query_plitems_smart ( struct query_params * qp , struct playlist_info * pli )
2017-01-04 16:29:47 -05:00
{
struct query_clause * qc ;
char * count ;
char * query ;
2018-04-13 10:29:06 -04:00
bool free_orderby = false ;
if ( pli - > query_limit > 0 )
{
if ( qp - > idx_type = = I_SUB )
{
if ( pli - > query_limit > qp - > offset + qp - > limit )
qp - > limit = pli - > query_limit ;
}
else if ( qp - > idx_type = = I_NONE )
{
qp - > idx_type = I_SUB ;
qp - > limit = pli - > query_limit ;
qp - > offset = 0 ;
}
else
{
DPRINTF ( E_WARN , L_DB , " Cannot append limit from smart playlist '%s' to query \n " , pli - > path ) ;
}
}
2018-04-15 02:03:42 -04:00
if ( pli - > query_order )
2018-04-13 10:29:06 -04:00
{
2018-04-15 02:03:42 -04:00
if ( ! qp - > order & & qp - > sort = = S_NONE )
2018-04-13 10:29:06 -04:00
{
2018-04-15 02:03:42 -04:00
qp - > order = strdup ( pli - > query_order ) ;
2018-04-13 10:29:06 -04:00
free_orderby = true ;
}
else
DPRINTF ( E_WARN , L_DB , " Cannot append order by from smart playlist '%s' to query \n " , pli - > path ) ;
}
2015-09-26 03:28:20 -04:00
2017-01-04 16:29:47 -05:00
qc = db_build_query_clause ( qp ) ;
2018-04-13 10:29:06 -04:00
if ( free_orderby )
{
2018-04-15 02:03:42 -04:00
free ( qp - > order ) ;
qp - > order = NULL ;
2018-04-13 10:29:06 -04:00
}
2017-01-04 16:29:47 -05:00
if ( ! qc )
return NULL ;
2015-09-26 03:28:20 -04:00
2018-04-13 10:29:06 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f %s AND %s LIMIT %d; " , qc - > where , pli - > query , pli - > query_limit ) ;
query = sqlite3_mprintf ( " SELECT f.* FROM files f %s AND %s %s %s; " , qc - > where , pli - > query , qc - > order , qc - > index ) ;
2010-01-21 11:52:51 -05:00
2017-01-04 16:29:47 -05:00
db_free_query_clause ( qc ) ;
2010-01-21 11:52:51 -05:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2010-01-21 11:52:51 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_plitems ( struct query_params * qp , struct query_clause * qc )
2010-01-21 11:52:51 -05:00
{
struct playlist_info * pli ;
2017-01-04 16:29:47 -05:00
char * query ;
2010-01-21 11:52:51 -05:00
2010-03-07 05:53:16 -05:00
if ( qp - > id < = 0 )
2019-05-28 17:40:41 -04:00
{
DPRINTF ( E_LOG , L_DB , " No playlist id specified in playlist items query \n " ) ;
return NULL ;
}
2010-01-21 11:52:51 -05:00
2010-03-07 05:53:16 -05:00
pli = db_pl_fetch_byid ( qp - > id ) ;
2010-01-21 11:52:51 -05:00
if ( ! pli )
2017-01-04 16:29:47 -05:00
return NULL ;
2010-01-21 11:52:51 -05:00
switch ( pli - > type )
{
2015-04-11 10:55:13 -04:00
case PL_SPECIAL :
2010-01-21 11:52:51 -05:00
case PL_SMART :
2018-04-13 10:29:06 -04:00
query = db_build_query_plitems_smart ( qp , pli ) ;
2010-01-21 11:52:51 -05:00
break ;
case PL_PLAIN :
2015-03-29 19:03:15 -04:00
case PL_FOLDER :
2019-05-28 17:40:41 -04:00
query = db_build_query_plitems_plain ( qp , qc ) ;
2010-01-21 11:52:51 -05:00
break ;
default :
DPRINTF ( E_LOG , L_DB , " Unknown playlist type %d in playlist items query \n " , pli - > type ) ;
2017-01-04 16:29:47 -05:00
query = NULL ;
2010-01-21 11:52:51 -05:00
break ;
}
free_pli ( pli , 0 ) ;
2017-01-04 16:29:47 -05:00
return query ;
2010-01-21 11:52:51 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_group_albums ( struct query_params * qp , struct query_clause * qc )
2010-01-10 06:26:48 -05:00
{
2017-01-04 16:29:47 -05:00
char * count ;
2010-01-10 06:26:48 -05:00
char * query ;
2018-03-17 15:20:19 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT f.songalbumid) FROM files f %s; " , qc - > where ) ;
2018-03-17 08:27:59 -04:00
query = sqlite3_mprintf ( " SELECT g.id, g.persistentid, f.album, f.album_sort, COUNT(f.id) as track_count, 1 as album_count, f.album_artist, f.songartistid, SUM(f.song_length) FROM files f JOIN groups g ON f.songalbumid = g.persistentid %s GROUP BY f.songalbumid %s %s %s; " , qc - > where , qc - > having , qc - > order , qc - > index ) ;
2010-01-10 06:26:48 -05:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2010-01-10 06:26:48 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_group_artists ( struct query_params * qp , struct query_clause * qc )
2013-08-29 16:00:37 -04:00
{
2017-01-04 16:29:47 -05:00
char * count ;
2013-08-29 16:00:37 -04:00
char * query ;
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT f.songartistid) FROM files f %s; " , qc - > where ) ;
2018-03-17 08:27:59 -04:00
query = sqlite3_mprintf ( " SELECT g.id, g.persistentid, f.album_artist, f.album_artist_sort, COUNT(f.id) as track_count, COUNT(DISTINCT f.songalbumid) as album_count, f.album_artist, f.songartistid, SUM(f.song_length) FROM files f JOIN groups g ON f.songartistid = g.persistentid %s GROUP BY f.songartistid %s %s %s; " , qc - > where , qc - > having , qc - > order , qc - > index ) ;
2013-08-29 16:00:37 -04:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2013-08-29 16:00:37 -04:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_group_items ( struct query_params * qp , struct query_clause * qc )
2010-03-07 06:53:23 -05:00
{
enum group_type gt ;
2017-01-04 16:29:47 -05:00
char * count ;
char * query ;
2014-11-10 16:53:08 -05:00
gt = db_group_type_bypersistentid ( qp - > persistentid ) ;
2010-03-07 06:53:23 -05:00
switch ( gt )
{
case G_ALBUMS :
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f %s AND f.songalbumid = % " PRIi64 " ; " , qc - > where , qp - > persistentid ) ;
query = sqlite3_mprintf ( " SELECT f.* FROM files f %s AND f.songalbumid = % " PRIi64 " %s %s; " , qc - > where , qp - > persistentid , qc - > order , qc - > index ) ;
2010-03-07 06:53:23 -05:00
break ;
2013-12-17 14:58:55 -05:00
case G_ARTISTS :
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f %s AND f.songartistid = % " PRIi64 " ; " , qc - > where , qp - > persistentid ) ;
query = sqlite3_mprintf ( " SELECT f.* FROM files f %s AND f.songartistid = % " PRIi64 " %s %s; " , qc - > where , qp - > persistentid , qc - > order , qc - > index ) ;
2013-12-17 14:58:55 -05:00
break ;
2010-03-07 06:53:23 -05:00
default :
2014-11-10 16:53:08 -05:00
DPRINTF ( E_LOG , L_DB , " Unsupported group type %d for group id % " PRIi64 " \n " , gt , qp - > persistentid ) ;
2017-01-04 16:29:47 -05:00
return NULL ;
2010-03-07 06:53:23 -05:00
}
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2010-03-07 06:53:23 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_group_dirs ( struct query_params * qp , struct query_clause * qc )
2010-03-07 06:53:23 -05:00
{
enum group_type gt ;
2017-01-04 16:29:47 -05:00
char * count ;
char * query ;
2014-11-10 16:53:08 -05:00
gt = db_group_type_bypersistentid ( qp - > persistentid ) ;
2010-03-07 06:53:23 -05:00
switch ( gt )
{
case G_ALBUMS :
2011-03-28 13:06:48 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT(SUBSTR(f.path, 1, LENGTH(f.path) - LENGTH(f.fname) - 1))) "
2017-01-04 16:29:47 -05:00
" FROM files f %s AND f.songalbumid = % " PRIi64 " ; " , qc - > where , qp - > persistentid ) ;
2011-03-28 13:06:48 -04:00
query = sqlite3_mprintf ( " SELECT DISTINCT(SUBSTR(f.path, 1, LENGTH(f.path) - LENGTH(f.fname) - 1)) "
2017-01-04 16:29:47 -05:00
" FROM files f %s AND f.songalbumid = % " PRIi64 " %s %s; " , qc - > where , qp - > persistentid , qc - > order , qc - > index ) ;
2010-03-07 06:53:23 -05:00
break ;
2013-12-17 14:58:55 -05:00
case G_ARTISTS :
2017-01-04 16:29:47 -05:00
count = sqlite3_mprintf ( " SELECT COUNT(DISTINCT(SUBSTR(f.path, 1, LENGTH(f.path) - LENGTH(f.fname) - 1))) "
" FROM files f %s AND f.songartistid = % " PRIi64 " ; " , qc - > where , qp - > persistentid ) ;
2013-12-17 14:58:55 -05:00
query = sqlite3_mprintf ( " SELECT DISTINCT(SUBSTR(f.path, 1, LENGTH(f.path) - LENGTH(f.fname) - 1)) "
2017-01-04 16:29:47 -05:00
" FROM files f %s AND f.songartistid = % " PRIi64 " %s %s; " , qc - > where , qp - > persistentid , qc - > order , qc - > index ) ;
2013-12-17 14:58:55 -05:00
break ;
default :
2017-01-04 16:29:47 -05:00
DPRINTF ( E_LOG , L_DB , " Unsupported group type %d for group id % " PRIi64 " \n " , gt , qp - > persistentid ) ;
return NULL ;
2010-03-07 06:53:23 -05:00
}
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2010-03-07 06:53:23 -05:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_browse ( struct query_params * qp , struct query_clause * qc )
2009-06-07 12:56:35 -04:00
{
2018-01-20 18:22:21 -05:00
const char * where ;
const char * select ;
2009-06-07 12:56:35 -04:00
char * count ;
2017-01-04 16:29:47 -05:00
char * query ;
2009-06-07 12:56:35 -04:00
2018-01-20 18:22:21 -05:00
select = browse_clause [ qp - > type & ~ Q_F_BROWSE ] . select ;
where = browse_clause [ qp - > type & ~ Q_F_BROWSE ] . where ;
2018-09-22 19:37:40 -04:00
count = sqlite3_mprintf ( " SELECT COUNT(*) FROM (SELECT %s FROM files f %s AND %s != '' %s); " , select , qc - > where , where , qc - > group ) ;
2018-01-20 18:22:21 -05:00
query = sqlite3_mprintf ( " SELECT %s FROM files f %s AND %s != '' %s %s %s; " , select , qc - > where , where , qc - > group , qc - > order , qc - > index ) ;
2009-06-07 12:56:35 -04:00
2017-01-04 16:29:47 -05:00
return db_build_query_check ( qp , count , query ) ;
2009-06-07 12:56:35 -04:00
}
2017-01-04 16:29:47 -05:00
static char *
2019-05-28 17:40:41 -04:00
db_build_query_count_items ( struct query_params * qp , struct query_clause * qc )
2015-04-06 03:19:48 -04:00
{
char * query ;
2017-01-04 16:29:47 -05:00
qp - > results = 1 ;
2015-04-06 03:19:48 -04:00
2018-04-27 00:42:57 -04:00
query = sqlite3_mprintf ( " SELECT COUNT(*), SUM(song_length), COUNT(DISTINCT songartistid), COUNT(DISTINCT songalbumid) FROM files f %s; " , qc - > where ) ;
2015-04-06 03:19:48 -04:00
if ( ! query )
2017-01-04 16:29:47 -05:00
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
2015-04-06 03:19:48 -04:00
2017-01-04 16:29:47 -05:00
return query ;
2015-04-06 03:19:48 -04:00
}
2009-06-07 12:56:35 -04:00
int
db_query_start ( struct query_params * qp )
{
2019-05-28 17:40:41 -04:00
struct query_clause * qc ;
2017-10-14 14:16:34 -04:00
sqlite3_stmt * stmt ;
2009-06-07 12:56:35 -04:00
char * query ;
int ret ;
qp - > stmt = NULL ;
2017-10-25 17:32:16 -04:00
qp - > results = - 1 ;
2009-06-07 12:56:35 -04:00
2019-05-28 17:40:41 -04:00
qc = db_build_query_clause ( qp ) ;
if ( ! qc )
return - 1 ;
2009-06-07 12:56:35 -04:00
switch ( qp - > type )
{
case Q_ITEMS :
2019-05-28 17:40:41 -04:00
query = db_build_query_items ( qp , qc ) ;
2009-06-07 12:56:35 -04:00
break ;
case Q_PL :
2019-05-28 17:40:41 -04:00
query = db_build_query_pls ( qp , qc ) ;
2009-06-07 12:56:35 -04:00
break ;
2017-01-04 17:20:59 -05:00
case Q_FIND_PL :
2019-05-28 17:40:41 -04:00
query = db_build_query_find_pls ( qp , qc ) ;
2017-01-04 17:20:59 -05:00
break ;
2009-06-07 12:56:35 -04:00
case Q_PLITEMS :
2019-05-28 17:40:41 -04:00
query = db_build_query_plitems ( qp , qc ) ;
2009-06-07 12:56:35 -04:00
break ;
2013-08-29 16:00:37 -04:00
case Q_GROUP_ALBUMS :
2019-05-28 17:40:41 -04:00
query = db_build_query_group_albums ( qp , qc ) ;
2013-08-29 16:00:37 -04:00
break ;
case Q_GROUP_ARTISTS :
2019-05-28 17:40:41 -04:00
query = db_build_query_group_artists ( qp , qc ) ;
2010-01-10 06:26:48 -05:00
break ;
2013-08-29 16:00:37 -04:00
case Q_GROUP_ITEMS :
2019-05-28 17:40:41 -04:00
query = db_build_query_group_items ( qp , qc ) ;
2010-03-07 06:53:23 -05:00
break ;
case Q_GROUP_DIRS :
2019-05-28 17:40:41 -04:00
query = db_build_query_group_dirs ( qp , qc ) ;
2010-03-07 06:53:23 -05:00
break ;
2015-04-06 03:19:48 -04:00
case Q_COUNT_ITEMS :
2019-05-28 17:40:41 -04:00
query = db_build_query_count_items ( qp , qc ) ;
2015-04-06 03:19:48 -04:00
break ;
2009-06-07 12:56:35 -04:00
default :
2018-01-20 18:22:21 -05:00
if ( qp - > type & Q_F_BROWSE )
2019-05-28 17:40:41 -04:00
query = db_build_query_browse ( qp , qc ) ;
2018-01-20 18:22:21 -05:00
else
query = NULL ;
2009-06-07 12:56:35 -04:00
}
2019-05-28 17:40:41 -04:00
db_free_query_clause ( qc ) ;
2017-01-04 16:29:47 -05:00
if ( ! query )
2018-01-20 18:22:21 -05:00
{
DPRINTF ( E_LOG , L_DB , " Could not create query, unknown type %d \n " , qp - > type ) ;
return - 1 ;
}
2009-06-07 12:56:35 -04:00
DPRINTF ( E_DBG , L_DB , " Starting query '%s' \n " , query ) ;
2017-10-14 14:16:34 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2009-06-07 12:56:35 -04:00
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 ) ;
2017-10-14 14:16:34 -04:00
qp - > stmt = stmt ;
2009-06-07 12:56:35 -04:00
return 0 ;
}
void
db_query_end ( struct query_params * qp )
{
if ( ! qp - > stmt )
return ;
sqlite3_finalize ( qp - > stmt ) ;
qp - > stmt = NULL ;
}
2017-01-07 18:21:47 -05:00
/*
* Utility function for running write queries ( INSERT , UPDATE , DELETE ) . If you
* set free to non - zero , the function will free the query . If you set
* library_update to non - zero it means that the update was not just of some
* internal value ( like a timestamp ) , but of something that requires clients
* to update their cache of the library ( and of course also of our own cache ) .
*/
2014-08-17 18:20:23 -04:00
static int
2017-12-09 11:12:13 -05:00
db_query_run ( char * query , int free , short update_events )
2014-08-17 18:20:23 -04:00
{
char * errmsg ;
2017-11-12 04:46:05 -05:00
int changes = 0 ;
2014-08-17 18:20:23 -04:00
int ret ;
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 ) ;
2015-06-08 17:07:56 -04:00
/* If the query will be long running we don't want the cache to start regenerating */
cache_daap_suspend ( ) ;
2014-08-17 18:20:23 -04:00
ret = db_exec ( query , & errmsg ) ;
if ( ret ! = SQLITE_OK )
DPRINTF ( E_LOG , L_DB , " Error '%s' while runnning '%s' \n " , errmsg , query ) ;
2017-11-12 04:46:05 -05:00
else
changes = sqlite3_changes ( hdl ) ;
2014-08-17 18:20:23 -04:00
sqlite3_free ( errmsg ) ;
if ( free )
sqlite3_free ( query ) ;
2017-01-07 18:21:47 -05:00
cache_daap_resume ( ) ;
2017-12-09 11:12:13 -05:00
if ( update_events & & changes > 0 )
library_update_trigger ( update_events ) ;
2014-08-22 16:51:13 -04:00
2014-08-17 18:20:23 -04:00
return ( ( ret ! = SQLITE_OK ) ? - 1 : 0 ) ;
}
2009-06-07 12:56:35 -04:00
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 ;
}
2013-08-29 16:00:37 -04:00
if ( ( qp - > type ! = Q_ITEMS ) & & ( qp - > type ! = Q_PLITEMS ) & & ( qp - > type ! = Q_GROUP_ITEMS ) )
2009-06-07 12:56:35 -04:00
{
2010-03-07 06:53:23 -05:00
DPRINTF ( E_LOG , L_DB , " Not an items, playlist or group items query! \n " ) ;
2009-06-07 12:56:35 -04:00
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( qp - > stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret = = SQLITE_DONE )
{
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
2009-06-07 12:56:35 -04:00
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 ) ;
2018-09-02 17:38:46 -04:00
// We allow more cols in db than in map because the db may be a future schema
if ( ncols < ARRAY_SIZE ( dbmfi_cols_map ) )
2009-06-07 12:56:35 -04:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than dbmfi column map (%u) \n " , ncols , ARRAY_SIZE ( dbmfi_cols_map ) ) ;
2009-06-07 12:56:35 -04:00
return - 1 ;
}
2018-09-02 17:38:46 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( dbmfi_cols_map ) ; i + + )
2009-06-07 12:56:35 -04:00
{
strcol = ( char * * ) ( ( char * ) dbmfi + dbmfi_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
return 0 ;
}
int
2019-05-28 17:40:41 -04:00
db_query_fetch_pl ( struct query_params * qp , struct db_playlist_info * dbpli )
2009-06-07 12:56:35 -04:00
{
int ncols ;
char * * strcol ;
2019-05-28 17:40:41 -04:00
uint32_t nitems ;
uint32_t nstreams ;
2010-01-21 11:52:51 -05:00
int type ;
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 ;
}
2017-01-06 13:58:38 -05:00
if ( ( qp - > type ! = Q_PL ) & & ( qp - > type ! = Q_FIND_PL ) )
2009-06-07 12:56:35 -04:00
{
DPRINTF ( E_LOG , L_DB , " Not a playlist query! \n " ) ;
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( qp - > stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret = = SQLITE_DONE )
{
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
2009-06-07 12:56:35 -04:00
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 ) ;
2018-09-02 17:38:46 -04:00
// We allow more cols in db than in map because the db may be a future schema
if ( ncols < ARRAY_SIZE ( dbpli_cols_map ) )
2009-06-07 12:56:35 -04:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than dbpli column map (%u) \n " , ncols , ARRAY_SIZE ( dbpli_cols_map ) ) ;
2009-06-07 12:56:35 -04:00
return - 1 ;
}
2018-04-14 04:40:48 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( dbpli_cols_map ) ; i + + )
2009-06-07 12:56:35 -04:00
{
strcol = ( char * * ) ( ( char * ) dbpli + dbpli_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
2019-05-28 17:40:41 -04:00
type = sqlite3_column_int ( qp - > stmt , 2 ) ;
if ( type = = PL_SPECIAL | | type = = PL_SMART )
2010-01-21 11:52:51 -05:00
{
2019-05-28 17:40:41 -04:00
db_files_get_count ( & nitems , & nstreams , dbpli - > query ) ;
snprintf ( qp - > buf1 , sizeof ( qp - > buf1 ) , " %d " , ( int ) nitems ) ;
snprintf ( qp - > buf2 , sizeof ( qp - > buf2 ) , " %d " , ( int ) nstreams ) ;
2016-02-21 12:05:11 -05:00
dbpli - > items = qp - > buf1 ;
dbpli - > streams = qp - > buf2 ;
2009-06-11 12:28:01 -04:00
}
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 ;
}
2013-08-29 16:00:37 -04:00
if ( ( qp - > type ! = Q_GROUP_ALBUMS ) & & ( qp - > type ! = Q_GROUP_ARTISTS ) )
2010-01-10 06:26:48 -05:00
{
DPRINTF ( E_LOG , L_DB , " Not a groups query! \n " ) ;
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( qp - > stmt ) ;
2010-01-10 06:26:48 -05:00
if ( ret = = SQLITE_DONE )
{
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
2010-01-10 06:26:48 -05:00
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 ) ;
2018-09-02 17:38:46 -04:00
// We allow more cols in db than in map because the db may be a future schema
if ( ncols < ARRAY_SIZE ( dbgri_cols_map ) )
2010-01-10 06:26:48 -05:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than dbgri column map (%u) \n " , ncols , ARRAY_SIZE ( dbgri_cols_map ) ) ;
2010-01-10 06:26:48 -05:00
return - 1 ;
}
2018-09-02 17:38:46 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( dbgri_cols_map ) ; i + + )
2010-01-10 06:26:48 -05:00
{
strcol = ( char * * ) ( ( char * ) dbgri + dbgri_cols_map [ i ] ) ;
* strcol = ( char * ) sqlite3_column_text ( qp - > stmt , i ) ;
}
return 0 ;
}
2015-04-06 03:19:48 -04:00
int
2015-05-26 15:24:10 -04:00
db_query_fetch_count ( struct query_params * qp , struct filecount_info * fci )
2015-04-06 03:19:48 -04:00
{
int ret ;
2015-05-26 15:24:10 -04:00
memset ( fci , 0 , sizeof ( struct filecount_info ) ) ;
2015-04-06 03:19:48 -04:00
if ( ! qp - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Query not started! \n " ) ;
return - 1 ;
}
if ( qp - > type ! = Q_COUNT_ITEMS )
{
DPRINTF ( E_LOG , L_DB , " Not a count query! \n " ) ;
return - 1 ;
}
ret = db_blocking_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of query results for count query \n " ) ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
2015-05-26 15:24:10 -04:00
fci - > count = sqlite3_column_int ( qp - > stmt , 0 ) ;
2017-03-04 11:23:52 -05:00
fci - > length = sqlite3_column_int64 ( qp - > stmt , 1 ) ;
2018-04-22 02:40:23 -04:00
fci - > artist_count = sqlite3_column_int ( qp - > stmt , 2 ) ;
fci - > album_count = sqlite3_column_int ( qp - > stmt , 3 ) ;
2015-04-06 03:19:48 -04:00
return 0 ;
}
2017-08-12 10:47:44 -04:00
int
db_filecount_get ( struct filecount_info * fci , struct query_params * qp )
{
int ret ;
ret = db_query_start ( qp ) ;
if ( ret < 0 )
{
db_query_end ( qp ) ;
return - 1 ;
}
ret = db_query_fetch_count ( qp , fci ) ;
if ( ret < 0 )
{
db_query_end ( qp ) ;
return - 1 ;
}
db_query_end ( qp ) ;
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 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( qp - > stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret = = SQLITE_DONE )
{
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
2009-06-07 12:56:35 -04:00
* 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 ;
}
2010-12-05 10:27:06 -05:00
int
db_query_fetch_string_sort ( struct query_params * qp , char * * string , char * * sortstring )
{
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 = db_blocking_step ( qp - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
2010-12-05 10:27:06 -05:00
* 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 ) ;
* sortstring = ( char * ) sqlite3_column_text ( qp - > stmt , 1 ) ;
return 0 ;
}
2009-06-07 12:56:35 -04:00
/* Files */
int
2019-05-28 17:40:41 -04:00
db_files_get_count ( uint32_t * nitems , uint32_t * nstreams , const char * filter )
2009-06-07 12:56:35 -04:00
{
2019-05-28 17:40:41 -04:00
sqlite3_stmt * stmt ;
char * query ;
int ret ;
if ( ! filter & & ! nstreams )
query = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f WHERE f.disabled = 0; " ) ;
else if ( ! filter )
query = sqlite3_mprintf ( " SELECT COUNT(*), SUM(data_kind = %d) FROM files f WHERE f.disabled = 0; " , DATA_KIND_HTTP ) ;
else if ( ! nstreams )
query = sqlite3_mprintf ( " SELECT COUNT(*) FROM files f WHERE f.disabled = 0 AND %s; " , filter ) ;
else
query = sqlite3_mprintf ( " SELECT COUNT(*), SUM(data_kind = %d) FROM files f WHERE f.disabled = 0 AND %s; " , DATA_KIND_HTTP , filter ) ;
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 = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
sqlite3_free ( query ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
ret = db_blocking_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " No matching row found for query: %s \n " , query ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s (%s) \n " , sqlite3_errmsg ( hdl ) , query ) ;
sqlite3_finalize ( stmt ) ;
return - 1 ;
}
if ( nitems )
* nitems = sqlite3_column_int ( stmt , 0 ) ;
if ( nstreams )
* nstreams = sqlite3_column_int ( stmt , 1 ) ;
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
sqlite3_finalize ( stmt ) ;
return 0 ;
2009-06-07 12:56:35 -04:00
}
void
db_file_inc_playcount ( int id )
{
2015-08-04 16:33:32 -04:00
# define Q_TMPL "UPDATE files SET play_count = play_count + 1, time_played = %" PRIi64 ", seek = 0 WHERE id = %d;"
2018-08-19 03:45:35 -04:00
/*
* Rating calculation is taken from from the beets plugin " mpdstats " ( see https : //beets.readthedocs.io/en/latest/plugins/mpdstats.html)
* and adapted to the forked - daapd rating rage ( 0 to 100 ) .
*
* Rating consist of the stable rating and a rolling rating .
* The stable rating is calculated based on the number was played and skipped :
* stable rating = ( play_count + 1.0 ) / ( play_count + skip_count + 2.0 ) * 100
* The rolling rating is calculated based on the current action ( played or skipped ) :
* rolling rating for played = rating + ( ( 100.0 - rating ) / 2.0 )
* rolling rating for skipped = rating - ( rating / 2.0 )
*
* The new rating is a mix of stable and rolling rating ( factor 0.75 ) :
* new rating = stable rating * 0.75 + rolling rating * 0.25
*/
# define Q_TMPL_WITH_RATING \
" UPDATE files " \
" SET play_count = play_count + 1, time_played = % " PRIi64 " , seek = 0, " \
" rating = CAST(((play_count + 1.0) / (play_count + skip_count + 2.0) * 100 * 0.75) + ((rating + ((100.0 - rating) / 2.0)) * 0.25) AS INT) " \
" WHERE id = %d; "
2009-06-07 12:56:35 -04:00
char * query ;
2019-01-30 02:36:34 -05:00
int ret ;
2009-06-07 12:56:35 -04:00
2018-08-19 03:45:35 -04:00
if ( db_rating_updates )
query = sqlite3_mprintf ( Q_TMPL_WITH_RATING , ( int64_t ) time ( NULL ) , id ) ;
else
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
2009-06-07 12:56:35 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2019-01-30 02:36:34 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
db_admin_setint64 ( DB_ADMIN_DB_MODIFIED , ( int64_t ) time ( NULL ) ) ;
2009-06-07 12:56:35 -04:00
# undef Q_TMPL
2018-08-19 03:45:35 -04:00
# undef Q_TMPL_WITH_RATING
2009-06-07 12:56:35 -04:00
}
2018-08-18 16:56:27 -04:00
void
db_file_inc_skipcount ( int id )
{
# define Q_TMPL "UPDATE files SET skip_count = skip_count + 1, time_skipped = %" PRIi64 " WHERE id = %d;"
2018-08-19 03:45:35 -04:00
// see db_file_inc_playcount for a description of how the rating is calculated
# define Q_TMPL_WITH_RATING \
" UPDATE files " \
" SET skip_count = skip_count + 1, time_skipped = % " PRIi64 " , seek = 0, " \
" rating = CAST(((play_count + 1.0) / (play_count + skip_count + 2.0) * 100 * 0.75) + ((rating - (rating / 2.0)) * 0.25) AS INT) " \
" WHERE id = %d; "
2018-08-18 16:56:27 -04:00
char * query ;
2019-01-30 02:36:34 -05:00
int ret ;
2018-08-18 16:56:27 -04:00
2018-08-19 03:45:35 -04:00
if ( db_rating_updates )
query = sqlite3_mprintf ( Q_TMPL_WITH_RATING , ( int64_t ) time ( NULL ) , id ) ;
else
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
2018-08-18 16:56:27 -04:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2019-01-30 02:36:34 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
db_admin_setint64 ( DB_ADMIN_DB_MODIFIED , ( int64_t ) time ( NULL ) ) ;
2018-08-18 16:56:27 -04:00
# undef Q_TMPL
2018-08-19 03:45:35 -04:00
# undef Q_TMPL_WITH_RATING
2018-08-18 16:56:27 -04:00
}
2019-01-23 03:25:34 -05:00
void
db_file_reset_playskip_count ( int id )
{
# define Q_TMPL "UPDATE files SET play_count = 0, skip_count = 0, time_played = 0, time_skipped = 0 WHERE id = %d;"
char * query ;
2019-01-30 02:36:34 -05:00
int ret ;
2019-01-23 03:25:34 -05:00
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2019-01-30 02:36:34 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
db_admin_setint64 ( DB_ADMIN_DB_MODIFIED , ( int64_t ) time ( NULL ) ) ;
2019-01-23 03:25:34 -05:00
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
void
2011-06-12 05:06:37 -04:00
db_file_ping ( int id )
2009-06-07 12:56:35 -04:00
{
2011-06-12 05:06:37 -04:00
# define Q_TMPL "UPDATE files SET db_timestamp = %" PRIi64 ", disabled = 0 WHERE id = %d;"
2009-06-07 12:56:35 -04:00
char * query ;
2011-06-12 05:06:37 -04:00
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
2009-06-07 12:56:35 -04:00
2016-12-18 15:18:15 -05:00
db_query_run ( query , 1 , 0 ) ;
2009-06-07 12:56:35 -04:00
# undef Q_TMPL
}
2017-09-30 18:43:24 -04:00
int
db_file_ping_bypath ( const char * path , time_t mtime_max )
{
2018-09-23 16:24:24 -04:00
sqlite3_bind_int64 ( db_statements . files_ping , 1 , ( int64_t ) time ( NULL ) ) ;
sqlite3_bind_text ( db_statements . files_ping , 2 , path , - 1 , SQLITE_STATIC ) ;
sqlite3_bind_int64 ( db_statements . files_ping , 3 , ( int64_t ) mtime_max ) ;
2017-09-30 18:43:24 -04:00
2018-09-23 16:24:24 -04:00
return db_statement_run ( db_statements . files_ping ) ;
2017-09-30 18:43:24 -04:00
}
2013-11-30 17:12:09 -05:00
void
2017-09-30 18:43:24 -04:00
db_file_ping_bymatch ( const char * path , int isdir )
2013-11-30 17:12:09 -05:00
{
2014-05-14 14:44:07 -04:00
# define Q_TMPL_DIR "UPDATE files SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q / %%';"
# define Q_TMPL_NODIR "UPDATE files SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q%%';"
2013-11-30 17:12:09 -05:00
char * query ;
2014-05-14 14:44:07 -04:00
if ( isdir )
query = sqlite3_mprintf ( Q_TMPL_DIR , ( int64_t ) time ( NULL ) , path ) ;
else
query = sqlite3_mprintf ( Q_TMPL_NODIR , ( int64_t ) time ( NULL ) , path ) ;
2016-12-18 15:18:15 -05:00
db_query_run ( query , 1 , 0 ) ;
2014-05-14 14:44:07 -04:00
# undef Q_TMPL_DIR
# undef Q_TMPL_NODIR
2013-11-30 17:12:09 -05:00
}
2010-03-07 05:06:15 -05:00
char *
db_file_path_byid ( int id )
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT f.path FROM files f WHERE f.id = %d;"
2010-03-07 05:06:15 -05:00
char * query ;
sqlite3_stmt * stmt ;
char * res ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return NULL ;
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , strlen ( query ) + 1 , & stmt , NULL ) ;
2010-03-07 05:06:15 -05:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return NULL ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2010-03-07 05:06:15 -05:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
2010-03-07 05:06:15 -05:00
else
2015-02-23 14:26:42 -05:00
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2010-03-07 05:06:15 -05:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return NULL ;
}
res = ( char * ) sqlite3_column_text ( stmt , 0 ) ;
if ( res )
res = strdup ( res ) ;
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2010-03-07 05:06:15 -05:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return res ;
# undef Q_TMPL
}
2009-11-22 09:39:38 -05:00
static int
2017-09-30 18:43:24 -04:00
db_file_id_byquery ( const 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 ) ;
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , strlen ( query ) + 1 , & stmt , NULL ) ;
2009-06-07 12:56:35 -04:00
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
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
2009-06-07 12:56:35 -04:00
else
2015-02-23 14:26:42 -05:00
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2009-06-07 12:56:35 -04:00
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
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
2009-11-22 09:39:38 -05:00
return ret ;
}
int
2017-09-30 18:43:24 -04:00
db_file_id_bypath ( const char * path )
2009-11-22 09:39:38 -05:00
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT f.id FROM files f WHERE f.path = '%q';"
2009-11-22 09:39:38 -05:00
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
2017-09-30 18:43:24 -04:00
db_file_id_byfile ( const char * filename )
2009-11-22 10:30:45 -05:00
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT f.id FROM files f WHERE f.fname = '%q';"
2009-11-22 10:30:45 -05:00
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
2017-09-30 18:43:24 -04:00
db_file_id_byurl ( const char * url )
2009-11-22 09:39:38 -05:00
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT f.id FROM files f WHERE f.url = '%q';"
2009-11-22 09:39:38 -05:00
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
}
2015-07-16 21:22:24 -04:00
int
2017-09-30 18:43:24 -04:00
db_file_id_by_virtualpath_match ( const char * path )
2015-07-16 21:22:24 -04:00
{
# define Q_TMPL "SELECT f.id FROM files f WHERE f.virtual_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 " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
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 ) ;
2017-09-30 18:43:24 -04:00
mfi = calloc ( 1 , sizeof ( struct media_file_info ) ) ;
2009-06-07 12:56:35 -04:00
if ( ! mfi )
{
DPRINTF ( E_LOG , L_DB , " Could not allocate struct media_file_info, out of memory \n " ) ;
return NULL ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
free ( mfi ) ;
return NULL ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
2009-06-07 12:56:35 -04:00
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 ) ;
2018-09-02 17:38:46 -04:00
// We allow more cols in db than in map because the db may be a future schema
if ( ncols < ARRAY_SIZE ( mfi_cols_map ) )
2009-06-07 12:56:35 -04:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than mfi column map (%u) \n " , ncols , ARRAY_SIZE ( mfi_cols_map ) ) ;
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
free ( mfi ) ;
return NULL ;
}
2018-09-02 17:38:46 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( mfi_cols_map ) ; i + + )
2009-06-07 12:56:35 -04:00
{
switch ( mfi_cols_map [ i ] . type )
{
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 ;
}
}
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
return mfi ;
}
struct media_file_info *
db_file_fetch_byid ( int id )
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT f.* FROM files f WHERE f.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
}
2014-12-21 14:41:44 -05:00
struct media_file_info *
2017-05-01 07:28:46 -04:00
db_file_fetch_byvirtualpath ( const char * virtual_path )
2014-12-21 14:41:44 -05:00
{
# define Q_TMPL "SELECT f.* FROM files f WHERE f.virtual_path = %Q;"
struct media_file_info * mfi ;
char * query ;
query = sqlite3_mprintf ( Q_TMPL , virtual_path ) ;
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
}
2009-06-07 12:56:35 -04:00
int
db_file_add ( struct media_file_info * mfi )
{
int ret ;
if ( mfi - > id ! = 0 )
{
2010-04-30 12:28:29 -04:00
DPRINTF ( E_WARN , L_DB , " Trying to add file with non-zero id; use db_file_update()? \n " ) ;
2009-06-07 12:56:35 -04:00
return - 1 ;
}
mfi - > db_timestamp = ( uint64_t ) time ( NULL ) ;
2015-05-29 13:47:53 -04:00
2019-12-03 12:24:39 -05:00
// We don't do this in fixup_tags_mfi() to avoid affecting db_file_update()
if ( mfi - > time_added = = 0 )
mfi - > time_added = mfi - > db_timestamp ;
2018-12-28 13:10:17 -05:00
fixup_tags_mfi ( mfi ) ;
2009-06-07 12:56:35 -04:00
2018-09-23 16:24:24 -04:00
ret = bind_mfi ( db_statements . files_insert , mfi ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2018-09-23 16:24:24 -04:00
ret = db_statement_run ( db_statements . files_insert ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2017-12-09 11:12:13 -05:00
library_update_trigger ( LISTENER_DATABASE ) ;
2014-08-21 04:14:24 -04:00
2009-06-07 12:56:35 -04:00
return 0 ;
}
int
db_file_update ( struct media_file_info * mfi )
{
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 ) ;
2018-12-28 13:10:17 -05:00
fixup_tags_mfi ( mfi ) ;
2009-06-07 12:56:35 -04:00
2018-09-23 16:24:24 -04:00
ret = bind_mfi ( db_statements . files_update , mfi ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2018-09-23 16:24:24 -04:00
ret = db_statement_run ( db_statements . files_update ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2017-12-09 11:12:13 -05:00
library_update_trigger ( LISTENER_DATABASE ) ;
2014-08-19 18:21:48 -04:00
2009-06-07 12:56:35 -04:00
return 0 ;
}
2015-08-04 16:33:32 -04:00
void
2017-01-15 17:22:06 -05:00
db_file_seek_update ( int id , uint32_t seek )
2015-08-04 16:33:32 -04:00
{
# define Q_TMPL "UPDATE files SET seek = %d WHERE id = %d;"
char * query ;
2019-01-30 02:36:34 -05:00
int ret ;
2015-08-04 16:33:32 -04:00
if ( id = = 0 )
return ;
query = sqlite3_mprintf ( Q_TMPL , seek , id ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2019-01-30 02:36:34 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
db_admin_setint64 ( DB_ADMIN_DB_MODIFIED , ( int64_t ) time ( NULL ) ) ;
2015-08-04 16:33:32 -04:00
# undef Q_TMPL
}
2019-01-30 02:36:34 -05:00
static int
db_file_rating_update ( char * query )
2017-12-09 11:12:13 -05:00
{
int ret ;
2017-12-15 11:12:19 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
2019-01-30 02:36:34 -05:00
{
db_admin_setint64 ( DB_ADMIN_DB_MODIFIED , ( int64_t ) time ( NULL ) ) ;
listener_notify ( LISTENER_RATING ) ;
}
2017-12-09 11:12:13 -05:00
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
2019-01-30 02:36:34 -05:00
}
int
db_file_rating_update_byid ( uint32_t id , uint32_t rating )
{
# define Q_TMPL "UPDATE files SET rating = %d WHERE id = %d;"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , rating , id ) ;
return db_file_rating_update ( query ) ;
2017-12-09 11:12:13 -05:00
# undef Q_TMPL
}
int
db_file_rating_update_byvirtualpath ( const char * virtual_path , uint32_t rating )
{
# define Q_TMPL "UPDATE files SET rating = %d WHERE virtual_path = %Q;"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , rating , virtual_path ) ;
2019-01-30 02:36:34 -05:00
return db_file_rating_update ( query ) ;
2017-12-09 11:12:13 -05:00
# undef Q_TMPL
}
2009-06-10 15:36:43 -04:00
void
2017-09-30 18:43:24 -04:00
db_file_delete_bypath ( const char * path )
2009-06-10 15:36:43 -04:00
{
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 ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
2017-12-09 11:12:13 -05:00
db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2009-06-10 15:36:43 -04:00
# undef Q_TMPL
}
2009-06-10 16:11:24 -04:00
void
2017-12-16 17:09:29 -05:00
db_file_disable_bypath ( const char * path , enum strip_type strip , uint32_t cookie )
2009-06-10 16:11:24 -04:00
{
2015-12-27 01:16:50 -05:00
# define Q_TMPL "UPDATE files SET path = substr(path, %d), virtual_path = substr(virtual_path, %d), disabled = %" PRIi64 " WHERE path = '%q';"
2009-06-10 16:11:24 -04:00
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
int vpath_striplen ;
2009-06-10 16:11:24 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
vpath_striplen = ( strip = = STRIP_PATH ) ? strlen ( " /file: " ) + path_striplen : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , vpath_striplen + 1 , disabled , path ) ;
2009-06-11 08:42:55 -04:00
2017-12-09 11:12:13 -05:00
db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2009-06-11 08:42:55 -04:00
# undef Q_TMPL
}
void
2017-12-16 17:09:29 -05:00
db_file_disable_bymatch ( const char * path , enum strip_type strip , uint32_t cookie )
2009-06-11 08:42:55 -04:00
{
2015-12-27 01:16:50 -05:00
# define Q_TMPL "UPDATE files SET path = substr(path, %d), virtual_path = substr(virtual_path, %d), disabled = %" PRIi64 " WHERE path LIKE '%q / %%';"
2009-06-11 08:42:55 -04:00
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
int vpath_striplen ;
2009-06-11 08:42:55 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
vpath_striplen = ( strip = = STRIP_PATH ) ? strlen ( " /file: " ) + path_striplen : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , vpath_striplen + 1 , disabled , path ) ;
2009-06-10 16:11:24 -04:00
2017-12-09 11:12:13 -05:00
db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2009-06-10 16:11:24 -04:00
# undef Q_TMPL
}
2017-12-16 17:09:29 -05:00
// "path" will be the directory part for directory updates (dir moved) and the
// full path for file updates (file moved). The db will have the filename in
// the path field for the former case (with a "/" prefix), and an empty path
// field for the latter.
2009-06-10 16:11:24 -04:00
int
2017-12-16 17:09:29 -05:00
db_file_enable_bycookie ( uint32_t cookie , const char * path , const char * filename )
2009-06-10 16:11:24 -04:00
{
2017-12-16 17:09:29 -05:00
# define Q_TMPL_UPDATE_FNAME "UPDATE files SET path = ('%q' || path), virtual_path = (' / file:%q' || virtual_path), fname = '%q', disabled = 0 WHERE disabled = %" PRIi64 ";"
# define Q_TMPL "UPDATE files SET path = ('%q' || path), virtual_path = (' / file:%q' || virtual_path), disabled = 0 WHERE disabled = %" PRIi64 ";"
2009-06-10 16:11:24 -04:00
char * query ;
int ret ;
2017-12-16 17:09:29 -05:00
if ( filename )
query = sqlite3_mprintf ( Q_TMPL_UPDATE_FNAME , path , path , filename , ( int64_t ) cookie ) ;
else
query = sqlite3_mprintf ( Q_TMPL , path , path , ( int64_t ) cookie ) ;
2009-06-10 16:11:24 -04:00
2017-12-16 17:09:29 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2014-08-21 17:06:52 -04:00
2014-08-17 18:20:23 -04:00
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
2017-12-16 17:09:29 -05:00
# undef Q_TMPL_UPDATE_FNAME
2009-06-10 16:11:24 -04:00
# undef Q_TMPL
}
2015-12-27 01:16:50 -05:00
int
2017-09-30 18:43:24 -04:00
db_file_update_directoryid ( const char * path , int dir_id )
2015-12-27 01:16:50 -05:00
{
# define Q_TMPL "UPDATE files SET directory_id = %d WHERE path = %Q;"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , dir_id , path ) ;
ret = db_query_run ( query , 1 , 0 ) ;
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
/* Playlists */
int
2019-05-28 17:40:41 -04:00
db_pl_get_count ( uint32_t * nitems )
2009-06-07 12:56:35 -04:00
{
2019-05-28 17:40:41 -04:00
int ret = db_get_one_int ( " SELECT COUNT(*) FROM playlists p WHERE p.disabled = 0; " ) ;
2010-01-21 11:52:51 -05:00
2019-05-28 17:40:41 -04:00
if ( ret < 0 )
return - 1 ;
2010-01-21 11:52:51 -05:00
2019-05-28 17:40:41 -04:00
* nitems = ( uint32_t ) ret ;
2010-01-21 11:52:51 -05:00
2019-05-28 17:40:41 -04:00
return 0 ;
2010-01-21 11:52:51 -05:00
}
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 ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , id ) ;
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2009-06-07 12:56:35 -04:00
# undef Q_TMPL
}
2013-11-30 17:12:09 -05:00
void
2017-09-30 18:43:24 -04:00
db_pl_ping_bymatch ( const char * path , int isdir )
2013-11-30 17:12:09 -05:00
{
2014-05-14 14:44:07 -04:00
# define Q_TMPL_DIR "UPDATE playlists SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q / %%';"
# define Q_TMPL_NODIR "UPDATE playlists SET db_timestamp = %" PRIi64 " WHERE path LIKE '%q%%';"
2013-11-30 17:12:09 -05:00
char * query ;
2014-05-14 14:44:07 -04:00
if ( isdir )
query = sqlite3_mprintf ( Q_TMPL_DIR , ( int64_t ) time ( NULL ) , path ) ;
else
query = sqlite3_mprintf ( Q_TMPL_NODIR , ( int64_t ) time ( NULL ) , path ) ;
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2014-05-14 14:44:07 -04:00
# undef Q_TMPL_DIR
# undef Q_TMPL_NODIR
2013-11-30 17:12:09 -05:00
}
2017-10-02 17:02:25 -04:00
void
db_pl_ping_items_bymatch ( const char * path , int id )
{
# define Q_TMPL "UPDATE files SET db_timestamp = %" PRIi64 ", disabled = 0 WHERE path IN (SELECT filepath FROM playlistitems WHERE filepath LIKE '%q%%' AND playlistid = %d);"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) time ( NULL ) , path , id ) ;
db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
2017-05-01 07:28:46 -04:00
int
db_pl_id_bypath ( const char * path )
2009-06-10 15:36:43 -04:00
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT p.id FROM playlists p WHERE p.path = '%q';"
2009-06-10 15:36:43 -04:00
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
2017-05-01 07:28:46 -04:00
ret = db_get_one_int ( query ) ;
2011-06-11 11:24:59 -04:00
2009-06-10 15:36:43 -04:00
sqlite3_free ( query ) ;
2017-05-01 07:28:46 -04:00
return ret ;
2009-06-10 15:36:43 -04:00
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
static struct playlist_info *
2017-09-30 18:43:24 -04:00
db_pl_fetch_byquery ( const char * query )
2009-06-07 12:56:35 -04:00
{
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 ) ;
2017-09-30 18:43:24 -04:00
pli = calloc ( 1 , sizeof ( struct playlist_info ) ) ;
2009-06-07 12:56:35 -04:00
if ( ! pli )
{
DPRINTF ( E_LOG , L_DB , " Could not allocate struct playlist_info, out of memory \n " ) ;
return NULL ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
free ( pli ) ;
return NULL ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
2009-06-07 12:56:35 -04:00
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 ) ;
2018-09-02 17:38:46 -04:00
if ( ncols < ARRAY_SIZE ( pli_cols_map ) )
2009-06-07 12:56:35 -04:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than pli column map (%u) \n " , ncols , ARRAY_SIZE ( pli_cols_map ) ) ;
2009-06-07 12:56:35 -04:00
sqlite3_finalize ( stmt ) ;
free ( pli ) ;
return NULL ;
}
2018-04-14 04:40:48 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( pli_cols_map ) ; i + + )
2009-06-07 12:56:35 -04:00
{
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 ;
}
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_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 ;
}
2019-05-28 17:40:41 -04:00
if ( pli - > type = = PL_SPECIAL | | pli - > type = = PL_SMART )
db_files_get_count ( & pli - > items , & pli - > streams , pli - > query ) ;
2009-06-07 12:56:35 -04:00
return pli ;
}
struct playlist_info *
2016-12-31 01:28:18 -05:00
db_pl_fetch_bypath ( const char * path )
2009-06-07 12:56:35 -04:00
{
struct playlist_info * pli ;
char * query ;
2019-05-28 17:40:41 -04:00
query = sqlite3_mprintf ( Q_PL_SELECT " WHERE f.path = '%q' GROUP BY f.id; " , 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 ;
}
2014-12-21 14:41:44 -05:00
struct playlist_info *
2017-05-01 07:28:46 -04:00
db_pl_fetch_byvirtualpath ( const char * virtual_path )
2014-12-21 14:41:44 -05:00
{
struct playlist_info * pli ;
char * query ;
2019-05-28 17:40:41 -04:00
query = sqlite3_mprintf ( Q_PL_SELECT " WHERE f.virtual_path = '%q' GROUP BY f.id; " , virtual_path ) ;
2014-12-21 14:41:44 -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 ;
}
2010-01-21 11:52:51 -05:00
struct playlist_info *
db_pl_fetch_byid ( int id )
{
struct playlist_info * pli ;
char * query ;
2019-05-28 17:40:41 -04:00
query = sqlite3_mprintf ( Q_PL_SELECT " WHERE f.id = %d GROUP BY f.id; " , id ) ;
2010-01-21 11:52:51 -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 ;
}
2009-11-22 06:34:37 -05:00
struct playlist_info *
2017-09-30 18:43:24 -04:00
db_pl_fetch_bytitlepath ( const char * title , const char * path )
2009-11-22 06:34:37 -05:00
{
struct playlist_info * pli ;
char * query ;
2019-05-28 17:40:41 -04:00
query = sqlite3_mprintf ( Q_PL_SELECT " WHERE f.title = '%q' AND f.path = '%q' GROUP BY f.id; " , 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 ;
}
int
2015-03-14 17:34:03 -04:00
db_pl_add ( struct playlist_info * pli , int * id )
2009-06-07 12:56:35 -04:00
{
int ret ;
2019-12-27 18:04:43 -05:00
// If the backend sets 1 it must be preserved, because the backend is still
// scanning and is going to update it later (see filescanner_playlist.c)
if ( pli - > db_timestamp ! = 1 )
pli - > db_timestamp = ( uint64_t ) time ( NULL ) ;
2018-12-28 13:10:17 -05:00
2019-12-27 18:04:43 -05:00
fixup_tags_pli ( pli ) ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
ret = bind_pli ( db_statements . playlists_insert , pli ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
ret = db_statement_run ( db_statements . playlists_insert ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
if ( id )
2009-06-07 12:56:35 -04:00
{
2019-12-27 18:04:43 -05:00
ret = db_pl_id_bypath ( pli - > path ) ;
if ( ret < 0 )
return - 1 ;
2015-03-14 17:34:03 -04:00
2019-12-27 18:04:43 -05:00
* id = ret ;
2009-06-07 12:56:35 -04:00
}
2019-12-27 18:04:43 -05:00
DPRINTF ( E_DBG , L_DB , " Added playlist %s (path %s) \n " , pli - > title , pli - > path ) ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
return 0 ;
}
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
int
db_pl_update ( struct playlist_info * pli )
{
int ret ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
// If the backend sets 1 it must be preserved, because the backend is still
// scanning and is going to update it later (see filescanner_playlist.c)
if ( pli - > db_timestamp ! = 1 )
pli - > db_timestamp = ( uint64_t ) time ( NULL ) ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
fixup_tags_pli ( pli ) ;
ret = bind_pli ( db_statements . playlists_update , pli ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
2019-12-27 18:04:43 -05:00
ret = db_statement_run ( db_statements . playlists_update ) ;
if ( ret < 0 )
return - 1 ;
2009-06-07 12:56:35 -04:00
return 0 ;
}
int
2016-12-31 01:28:18 -05:00
db_pl_add_item_bypath ( int plid , const 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 ;
2009-06-11 14:27:31 -04:00
query = sqlite3_mprintf ( Q_TMPL , plid , path ) ;
2009-11-22 06:17:28 -05:00
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2009-11-22 06:17:28 -05:00
# undef Q_TMPL
}
int
db_pl_add_item_byid ( int plid , int fileid )
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "INSERT INTO playlistitems (playlistid, filepath) VALUES (%d, (SELECT f.path FROM files f WHERE f.id = %d));"
2009-11-22 06:17:28 -05:00
char * query ;
query = sqlite3_mprintf ( Q_TMPL , plid , fileid ) ;
2009-06-07 12:56:35 -04:00
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 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 ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2014-08-17 18:20:23 -04:00
# undef Q_TMPL
}
2009-06-10 15:36:43 -04:00
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
int ret ;
if ( id = = 1 )
return ;
2009-06-10 15:36:43 -04:00
query = sqlite3_mprintf ( Q_TMPL , id ) ;
2014-08-22 16:51:13 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2009-06-10 15:36:43 -04:00
2014-08-17 18:20:23 -04:00
if ( ret = = 0 )
db_pl_clear_items ( id ) ;
2009-06-10 15:36:43 -04:00
# undef Q_TMPL
}
void
2017-09-30 18:43:24 -04:00
db_pl_delete_bypath ( const char * path )
2009-06-10 15:36:43 -04:00
{
2018-01-25 16:26:00 -05:00
int i ;
int ret ;
char * query ;
char * queries_tmpl [ ] =
{
" DELETE FROM playlistitems WHERE playlistid IN (SELECT id FROM playlists WHERE path = '%q'); " ,
" DELETE FROM playlists WHERE path = '%q'; " ,
} ;
2009-06-10 15:36:43 -04:00
2018-01-25 16:26:00 -05:00
for ( i = 0 ; i < ( sizeof ( queries_tmpl ) / sizeof ( queries_tmpl [ 0 ] ) ) ; i + + )
{
query = sqlite3_mprintf ( queries_tmpl [ i ] , path ) ;
2009-06-07 12:56:35 -04:00
2018-01-25 16:26:00 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret = = 0 )
DPRINTF ( E_DBG , L_DB , " Purged %d rows \n " , sqlite3_changes ( hdl ) ) ;
}
2009-06-07 12:56:35 -04:00
}
2009-06-10 16:11:24 -04:00
void
2017-12-16 17:09:29 -05:00
db_pl_disable_bypath ( const char * path , enum strip_type strip , uint32_t cookie )
2009-06-10 16:11:24 -04:00
{
2015-12-27 01:16:50 -05:00
# define Q_TMPL "UPDATE playlists SET path = substr(path, %d), virtual_path = substr(virtual_path, %d), disabled = %" PRIi64 " WHERE path = '%q';"
2009-06-10 16:11:24 -04:00
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
int vpath_striplen ;
2009-06-10 16:11:24 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
vpath_striplen = ( strip = = STRIP_PATH ) ? strlen ( " /file: " ) + path_striplen : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , vpath_striplen + 1 , disabled , path ) ;
2009-06-11 08:42:55 -04:00
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2009-06-11 08:42:55 -04:00
# undef Q_TMPL
}
void
2017-12-16 17:09:29 -05:00
db_pl_disable_bymatch ( const char * path , enum strip_type strip , uint32_t cookie )
2009-06-11 08:42:55 -04:00
{
2015-12-27 01:16:50 -05:00
# define Q_TMPL "UPDATE playlists SET path = substr(path, %d), virtual_path = substr(virtual_path, %d), disabled = %" PRIi64 " WHERE path LIKE '%q / %%';"
2009-06-11 08:42:55 -04:00
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
int vpath_striplen ;
2009-06-11 08:42:55 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
vpath_striplen = ( strip = = STRIP_PATH ) ? strlen ( " /file: " ) + path_striplen : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , vpath_striplen + 1 , disabled , path ) ;
2009-06-10 16:11:24 -04:00
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2009-06-10 16:11:24 -04:00
# undef Q_TMPL
}
int
2017-09-30 18:43:24 -04:00
db_pl_enable_bycookie ( uint32_t cookie , const char * path )
2009-06-10 16:11:24 -04:00
{
2017-12-16 17:09:29 -05:00
# define Q_TMPL "UPDATE playlists SET path = ('%q' || path), virtual_path = (' / file:%q' || virtual_path), disabled = 0 WHERE disabled = %" PRIi64 ";"
2009-06-10 16:11:24 -04:00
char * query ;
int ret ;
2015-12-27 01:16:50 -05:00
query = sqlite3_mprintf ( Q_TMPL , path , path , ( int64_t ) cookie ) ;
2009-06-10 16:11:24 -04:00
2014-08-22 16:51:13 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2009-06-10 16:11:24 -04:00
2014-08-17 18:20:23 -04:00
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
2009-06-10 16:11:24 -04:00
# undef Q_TMPL
}
2009-06-07 12:56:35 -04:00
2014-12-21 14:41:44 -05:00
2010-03-06 12:56:30 -05:00
/* Groups */
2016-12-18 15:18:15 -05:00
// Remove album and artist entries in the groups table that are not longer referenced from the files table
2016-12-14 13:28:27 -05:00
int
db_groups_cleanup ( )
{
# define Q_TMPL_ALBUM "DELETE FROM groups WHERE type = 1 AND NOT persistentid IN (SELECT songalbumid from files WHERE disabled = 0);"
# define Q_TMPL_ARTIST "DELETE FROM groups WHERE type = 2 AND NOT persistentid IN (SELECT songartistid from files WHERE disabled = 0);"
int ret ;
db_transaction_begin ( ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( Q_TMPL_ALBUM , 0 , LISTENER_DATABASE ) ;
2016-12-14 13:28:27 -05:00
if ( ret < 0 )
{
db_transaction_rollback ( ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Removed album group-entries: %d \n " , sqlite3_changes ( hdl ) ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( Q_TMPL_ARTIST , 0 , LISTENER_DATABASE ) ;
2016-12-14 13:28:27 -05:00
if ( ret < 0 )
{
db_transaction_rollback ( ) ;
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Removed artist group-entries: %d \n " , sqlite3_changes ( hdl ) ) ;
db_transaction_end ( ) ;
return 0 ;
# undef Q_TMPL_ALBUM
# undef Q_TMPL_ARTIST
}
2014-11-10 16:53:08 -05:00
static enum group_type
db_group_type_bypersistentid ( int64_t persistentid )
2010-03-07 06:53:23 -05:00
{
2015-01-08 16:40:47 -05:00
# define Q_TMPL "SELECT g.type FROM groups g WHERE g.persistentid = %" PRIi64 ";"
2010-03-07 06:53:23 -05:00
char * query ;
sqlite3_stmt * stmt ;
int ret ;
2014-11-10 16:53:08 -05:00
query = sqlite3_mprintf ( Q_TMPL , persistentid ) ;
2010-03-07 06:53:23 -05:00
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 ) ;
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , strlen ( query ) + 1 , & stmt , NULL ) ;
2010-03-07 06:53:23 -05:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return 0 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2010-03-07 06:53:23 -05:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
2014-06-01 17:58:44 -04:00
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
2010-03-07 06:53:23 -05:00
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2010-03-07 06:53:23 -05:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
2010-03-06 12:56:30 -05:00
2015-01-08 16:40:47 -05:00
int
db_group_persistentid_byid ( int id , int64_t * persistentid )
{
# define Q_TMPL "SELECT g.persistentid FROM groups g WHERE g.id = %d;"
char * query ;
sqlite3_stmt * stmt ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
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 = db_blocking_prepare_v2 ( 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 = db_blocking_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_DBG , 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 ;
}
* persistentid = sqlite3_column_int64 ( stmt , 0 ) ;
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2014-11-10 16:53:08 -05:00
2015-12-27 01:16:50 -05:00
/* Directories */
int
2018-11-24 06:14:41 -05:00
db_directory_id_byvirtualpath ( const char * virtual_path )
2015-12-27 01:16:50 -05:00
{
# define Q_TMPL "SELECT d.id FROM directories d WHERE d.virtual_path = '%q';"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , virtual_path ) ;
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
}
2018-11-24 06:14:41 -05:00
int
db_directory_id_bypath ( const char * path )
{
# define Q_TMPL "SELECT d.id FROM directories d WHERE d.path = '%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 " ) ;
return 0 ;
}
ret = db_file_id_byquery ( query ) ;
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
2015-12-27 01:16:50 -05:00
int
db_directory_enum_start ( struct directory_enum * de )
{
2019-08-21 16:54:34 -04:00
# define Q_TMPL "SELECT * FROM directories WHERE disabled = 0 AND parent_id = %d ORDER BY virtual_path COLLATE NOCASE;"
2017-10-14 14:16:34 -04:00
sqlite3_stmt * stmt ;
2015-12-27 01:16:50 -05:00
char * query ;
int ret ;
de - > stmt = NULL ;
query = sqlite3_mprintf ( Q_TMPL , de - > parent_id ) ;
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 ) ;
2017-10-14 14:16:34 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2015-12-27 01:16:50 -05:00
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 ) ;
2017-10-14 14:16:34 -04:00
de - > stmt = stmt ;
2015-12-27 01:16:50 -05:00
return 0 ;
# undef Q_TMPL
}
int
db_directory_enum_fetch ( struct directory_enum * de , struct directory_info * di )
{
uint64_t disabled ;
int ret ;
memset ( di , 0 , sizeof ( struct directory_info ) ) ;
if ( ! de - > stmt )
{
DPRINTF ( E_LOG , L_DB , " Directory enum not started! \n " ) ;
return - 1 ;
}
ret = db_blocking_step ( de - > stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of directory 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 ;
}
di - > id = sqlite3_column_int ( de - > stmt , 0 ) ;
di - > virtual_path = ( char * ) sqlite3_column_text ( de - > stmt , 1 ) ;
di - > db_timestamp = sqlite3_column_int ( de - > stmt , 2 ) ;
disabled = sqlite3_column_int64 ( de - > stmt , 3 ) ;
di - > disabled = ( disabled ! = 0 ) ;
di - > parent_id = sqlite3_column_int ( de - > stmt , 4 ) ;
2018-11-24 03:23:32 -05:00
di - > path = ( char * ) sqlite3_column_text ( de - > stmt , 5 ) ;
2015-12-27 01:16:50 -05:00
return 0 ;
}
void
db_directory_enum_end ( struct directory_enum * de )
{
if ( ! de - > stmt )
return ;
sqlite3_finalize ( de - > stmt ) ;
de - > stmt = NULL ;
}
static int
db_directory_add ( struct directory_info * di , int * id )
{
2018-11-24 03:23:32 -05:00
# define QADD_TMPL "INSERT INTO directories (virtual_path, db_timestamp, disabled, parent_id, path)" \
" VALUES (TRIM(%Q), %d, %d, %d, TRIM(%Q)); "
2015-12-27 01:16:50 -05:00
char * query ;
char * errmsg ;
2017-11-12 22:40:12 -05:00
int vp_len = strlen ( di - > virtual_path ) ;
2015-12-27 01:16:50 -05:00
int ret ;
2017-11-12 22:40:12 -05:00
if ( vp_len & & di - > virtual_path [ vp_len - 1 ] = = ' ' )
{
/* Since sqlite removes the trailing space, so these
* directories will be found as new in perpetuity .
*/
2017-12-16 17:09:29 -05:00
DPRINTF ( E_LOG , L_DB , " Directory name ends with space: '%s' \n " , di - > virtual_path ) ;
2017-11-12 22:40:12 -05:00
}
2018-11-24 03:23:32 -05:00
query = sqlite3_mprintf ( QADD_TMPL , di - > virtual_path , di - > db_timestamp , di - > disabled , di - > parent_id , di - > path ) ;
2015-12-27 01:16:50 -05: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 ) ;
ret = db_exec ( query , & 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 ;
}
2017-12-16 17:09:29 -05:00
DPRINTF ( E_DBG , L_DB , " Added directory '%s' with id %d \n " , di - > virtual_path , * id ) ;
2015-12-27 01:16:50 -05:00
return 0 ;
# undef QADD_TMPL
}
static int
db_directory_update ( struct directory_info * di )
{
2018-11-24 03:23:32 -05:00
# define QADD_TMPL "UPDATE directories SET virtual_path = TRIM(%Q), db_timestamp = %d, disabled = %d, parent_id = %d, path = TRIM(%Q)" \
2015-12-27 01:16:50 -05:00
" WHERE id = %d; "
char * query ;
char * errmsg ;
int ret ;
/* Add */
2018-11-24 03:23:32 -05:00
query = sqlite3_mprintf ( QADD_TMPL , di - > virtual_path , di - > db_timestamp , di - > disabled , di - > parent_id , di - > path , di - > id ) ;
2015-12-27 01:16:50 -05: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 ) ;
ret = db_exec ( query , & 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 ) ;
2017-12-16 17:09:29 -05:00
DPRINTF ( E_DBG , L_DB , " Updated directory '%s' with id %d \n " , di - > virtual_path , di - > id ) ;
2015-12-27 01:16:50 -05:00
return 0 ;
# undef QADD_TMPL
}
int
2018-11-24 03:44:18 -05:00
db_directory_addorupdate ( char * virtual_path , char * path , int disabled , int parent_id )
2015-12-27 01:16:50 -05:00
{
struct directory_info di ;
int id ;
int ret ;
id = db_directory_id_byvirtualpath ( virtual_path ) ;
di . id = id ;
di . parent_id = parent_id ;
di . virtual_path = virtual_path ;
2018-11-24 03:44:18 -05:00
di . path = path ;
2015-12-27 01:16:50 -05:00
di . disabled = disabled ;
di . db_timestamp = ( uint64_t ) time ( NULL ) ;
if ( di . id = = 0 )
ret = db_directory_add ( & di , & id ) ;
else
ret = db_directory_update ( & di ) ;
if ( ret < 0 | | id < = 0 )
{
DPRINTF ( E_LOG , L_DB , " Insert or update of directory failed '%s' \n " , virtual_path ) ;
return - 1 ;
}
return id ;
}
void
2017-01-01 10:51:21 -05:00
db_directory_ping_bymatch ( char * virtual_path )
2015-12-27 01:16:50 -05:00
{
2017-01-01 10:51:21 -05:00
# define Q_TMPL_DIR "UPDATE directories SET db_timestamp = %" PRIi64 " WHERE virtual_path = '%q' OR virtual_path LIKE '%q / %%';"
2015-12-27 01:16:50 -05:00
char * query ;
2017-01-01 10:51:21 -05:00
query = sqlite3_mprintf ( Q_TMPL_DIR , ( int64_t ) time ( NULL ) , virtual_path , virtual_path ) ;
2015-12-27 01:16:50 -05:00
2016-12-18 15:18:15 -05:00
db_query_run ( query , 1 , 0 ) ;
2015-12-27 01:16:50 -05:00
# undef Q_TMPL_DIR
}
void
2017-12-16 17:09:29 -05:00
db_directory_disable_bymatch ( char * path , enum strip_type strip , uint32_t cookie )
2015-12-27 01:16:50 -05:00
{
2019-05-28 17:40:41 -04:00
# define Q_TMPL "UPDATE directories SET virtual_path = substr(virtual_path, %d)," \
" disabled = % " PRIi64 " WHERE virtual_path = '/file:%q' OR virtual_path LIKE '/file:%q/%%'; "
2015-12-27 01:16:50 -05:00
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int vpath_striplen ;
2015-12-27 01:16:50 -05:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
vpath_striplen = ( strip = = STRIP_PATH ) ? strlen ( " /file: " ) + strlen ( path ) : 0 ;
query = sqlite3_mprintf ( Q_TMPL , vpath_striplen + 1 , disabled , path , path , path ) ;
2015-12-27 01:16:50 -05:00
2017-12-09 11:12:13 -05:00
db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2015-12-27 01:16:50 -05:00
# undef Q_TMPL
}
int
db_directory_enable_bycookie ( uint32_t cookie , char * path )
{
2019-05-28 17:40:41 -04:00
# define Q_TMPL "UPDATE directories SET virtual_path = (' / file:%q' || virtual_path)," \
" disabled = 0 WHERE disabled = % " PRIi64 " ; "
2015-12-27 01:16:50 -05:00
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path , ( int64_t ) cookie ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2015-12-27 01:16:50 -05:00
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
# undef Q_TMPL
}
2016-01-30 01:27:20 -05:00
int
db_directory_enable_bypath ( char * path )
{
2017-12-17 03:13:09 -05:00
# define Q_TMPL "UPDATE directories SET disabled = 0 WHERE virtual_path = %Q AND disabled <> 0;"
2016-01-30 01:27:20 -05:00
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2016-01-30 01:27:20 -05:00
return ( ( ret < 0 ) ? - 1 : sqlite3_changes ( hdl ) ) ;
# undef Q_TMPL
}
2015-12-27 01:16:50 -05:00
2010-01-30 11:01:27 -05:00
/* Remotes */
static int
db_pairing_delete_byremote ( char * remote_id )
{
# define Q_TMPL "DELETE FROM pairings WHERE remote = '%q';"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , remote_id ) ;
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2010-01-30 11:01:27 -05:00
# undef Q_TMPL
}
int
db_pairing_add ( struct pairing_info * pi )
{
# define Q_TMPL "INSERT INTO pairings (remote, name, guid) VALUES ('%q', '%q', '%q');"
char * query ;
int ret ;
ret = db_pairing_delete_byremote ( pi - > remote_id ) ;
if ( ret < 0 )
return ret ;
query = sqlite3_mprintf ( Q_TMPL , pi - > remote_id , pi - > name , pi - > guid ) ;
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2010-01-30 11:01:27 -05:00
# undef Q_TMPL
}
int
db_pairing_fetch_byguid ( struct pairing_info * pi )
{
2011-03-28 13:06:48 -04:00
# define Q_TMPL "SELECT p.* FROM pairings p WHERE p.guid = '%q';"
2010-01-30 11:01:27 -05:00
char * query ;
sqlite3_stmt * stmt ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , pi - > guid ) ;
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 ) ;
2010-04-26 12:24:09 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2010-01-30 11:01:27 -05:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( stmt ) ;
2010-01-30 11:01:27 -05:00
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_INFO , L_DB , " Pairing GUID %s not found \n " , pi - > guid ) ;
else
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
pi - > remote_id = strdup ( ( char * ) sqlite3_column_text ( stmt , 0 ) ) ;
pi - > name = strdup ( ( char * ) sqlite3_column_text ( stmt , 1 ) ) ;
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2010-01-30 11:01:27 -05:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2014-03-11 18:20:29 -04:00
# ifdef HAVE_SPOTIFY_H
/* Spotify */
void
db_spotify_purge ( void )
{
2017-11-12 04:46:05 -05:00
# define Q_TMPL "UPDATE directories SET disabled = %" PRIi64 " WHERE virtual_path = ' / spotify:' AND disabled <> %" PRIi64 ";"
2016-11-12 06:35:22 -05:00
char * queries [ 4 ] =
2014-03-11 18:20:29 -04:00
{
" DELETE FROM files WHERE path LIKE 'spotify:%%'; " ,
" DELETE FROM playlistitems WHERE filepath LIKE 'spotify:%%'; " ,
" DELETE FROM playlists WHERE path LIKE 'spotify:%%'; " ,
2016-01-30 01:27:20 -05:00
" DELETE FROM directories WHERE virtual_path LIKE '/spotify:/%%'; " ,
2014-03-11 18:20:29 -04:00
} ;
2016-11-12 06:35:22 -05:00
char * query ;
2014-03-11 18:20:29 -04:00
int i ;
int ret ;
for ( i = 0 ; i < ( sizeof ( queries ) / sizeof ( queries [ 0 ] ) ) ; i + + )
{
2017-12-09 11:12:13 -05:00
ret = db_query_run ( queries [ i ] , 0 , LISTENER_DATABASE ) ;
2014-03-11 18:20:29 -04:00
2014-08-17 18:20:23 -04:00
if ( ret = = 0 )
2016-11-13 14:28:29 -05:00
DPRINTF ( E_DBG , L_DB , " Processed %d rows \n " , sqlite3_changes ( hdl ) ) ;
2014-03-11 18:20:29 -04:00
}
2016-11-12 06:35:22 -05:00
// Disable the spotify directory by setting 'disabled' to INOTIFY_FAKE_COOKIE value
2017-11-12 04:46:05 -05:00
query = sqlite3_mprintf ( Q_TMPL , INOTIFY_FAKE_COOKIE , INOTIFY_FAKE_COOKIE ) ;
2016-11-12 06:35:22 -05:00
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return ;
}
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2016-11-12 06:35:22 -05:00
if ( ret = = 0 )
DPRINTF ( E_DBG , L_DB , " Disabled spotify directory \n " ) ;
# undef Q_TMPL
2014-03-11 18:20:29 -04:00
}
/* Spotify */
void
db_spotify_pl_delete ( int id )
{
2016-01-16 11:02:03 -05:00
char * queries_tmpl [ 2 ] =
2014-03-11 18:20:29 -04:00
{
" DELETE FROM playlists WHERE id = %d; " ,
" DELETE FROM playlistitems WHERE playlistid = %d; " ,
} ;
2014-08-17 18:20:23 -04:00
char * query ;
2014-03-11 18:20:29 -04:00
int i ;
int ret ;
for ( i = 0 ; i < ( sizeof ( queries_tmpl ) / sizeof ( queries_tmpl [ 0 ] ) ) ; i + + )
{
2014-08-17 18:20:23 -04:00
query = sqlite3_mprintf ( queries_tmpl [ i ] , id ) ;
2014-03-11 18:20:29 -04:00
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2014-03-11 18:20:29 -04:00
2014-08-17 18:20:23 -04:00
if ( ret = = 0 )
2014-03-11 18:20:29 -04:00
DPRINTF ( E_DBG , L_DB , " Deleted %d rows \n " , sqlite3_changes ( hdl ) ) ;
}
}
2016-01-16 11:02:03 -05:00
/* Spotify */
void
2016-11-13 14:28:29 -05:00
db_spotify_files_delete ( void )
2016-01-16 11:02:03 -05:00
{
# define Q_TMPL "DELETE FROM files WHERE path LIKE 'spotify:%%' AND NOT path IN (SELECT filepath FROM playlistitems);"
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL ) ;
2017-12-09 11:12:13 -05:00
ret = db_query_run ( query , 1 , LISTENER_DATABASE ) ;
2016-01-16 11:02:03 -05:00
if ( ret = = 0 )
DPRINTF ( E_DBG , L_DB , " Deleted %d rows \n " , sqlite3_changes ( hdl ) ) ;
# undef Q_TMPL
}
2014-03-11 18:20:29 -04:00
# endif
2014-08-15 16:54:47 -04:00
/* Admin */
int
2016-12-09 10:31:42 -05:00
db_admin_set ( const char * key , const char * value )
2014-08-15 16:54:47 -04:00
{
2014-08-27 16:12:42 -04:00
# define Q_TMPL "INSERT OR REPLACE INTO admin (key, value) VALUES ('%q', '%q');"
2014-08-15 16:54:47 -04:00
char * query ;
query = sqlite3_mprintf ( Q_TMPL , key , value ) ;
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2014-08-15 16:54:47 -04:00
# undef Q_TMPL
}
2017-11-19 01:46:28 -05:00
int
db_admin_setint ( const char * key , int value )
{
# define Q_TMPL "INSERT OR REPLACE INTO admin (key, value) VALUES ('%q', '%d');"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , key , value ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
2017-11-12 03:27:26 -05:00
int
db_admin_setint64 ( const char * key , int64_t value )
{
# define Q_TMPL "INSERT OR REPLACE INTO admin (key, value) VALUES ('%q', '%" PRIi64 "');"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , key , value ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
static int
admin_get ( const char * key , short type , void * value )
2014-08-15 16:54:47 -04:00
{
# define Q_TMPL "SELECT value FROM admin a WHERE a.key = '%q';"
char * query ;
sqlite3_stmt * stmt ;
2017-11-12 03:27:26 -05:00
char * cval ;
int32_t * ival ;
int64_t * i64val ;
char * * strval ;
2014-08-15 16:54:47 -04:00
int ret ;
query = sqlite3_mprintf ( Q_TMPL , key ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
2017-11-12 03:27:26 -05:00
return - 1 ;
2014-08-15 16:54:47 -04:00
}
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( query , strlen ( query ) + 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
2018-03-03 14:13:09 -05:00
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2014-08-15 16:54:47 -04:00
sqlite3_free ( query ) ;
2017-11-12 03:27:26 -05:00
return - 1 ;
2014-08-15 16:54:47 -04:00
}
ret = db_blocking_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret = = SQLITE_DONE )
DPRINTF ( E_DBG , L_DB , " No results \n " ) ;
else
2018-03-03 14:13:09 -05:00
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2014-08-15 16:54:47 -04:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
2017-11-12 03:27:26 -05:00
return - 1 ;
2014-08-15 16:54:47 -04:00
}
2017-11-12 03:27:26 -05:00
switch ( type )
{
case DB_TYPE_INT :
ival = ( int32_t * ) value ;
* ival = sqlite3_column_int ( stmt , 0 ) ;
break ;
case DB_TYPE_INT64 :
i64val = ( int64_t * ) value ;
* i64val = sqlite3_column_int64 ( stmt , 0 ) ;
break ;
case DB_TYPE_STRING :
strval = ( char * * ) value ;
cval = ( char * ) sqlite3_column_text ( stmt , 0 ) ;
if ( cval )
* strval = strdup ( cval ) ;
break ;
default :
DPRINTF ( E_LOG , L_DB , " BUG: Unknown type %d in admin_set \n " , type ) ;
ret = - 2 ;
}
2014-08-15 16:54:47 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
2017-11-12 03:27:26 -05:00
return ret ;
2014-08-15 16:54:47 -04:00
# undef Q_TMPL
}
2017-11-12 03:27:26 -05:00
char *
db_admin_get ( const char * key )
{
char * value = NULL ;
int ret ;
ret = admin_get ( key , DB_TYPE_STRING , & value ) ;
if ( ret < 0 )
{
2018-03-03 14:13:09 -05:00
DPRINTF ( E_DBG , L_DB , " Could not find key '%s' in admin table \n " , key ) ;
2017-11-12 03:27:26 -05:00
return NULL ;
}
return value ;
}
2017-11-19 01:46:28 -05:00
int
db_admin_getint ( const char * key )
{
int value = 0 ;
int ret ;
ret = admin_get ( key , DB_TYPE_INT , & value ) ;
if ( ret < 0 )
{
2018-03-03 14:13:09 -05:00
DPRINTF ( E_DBG , L_DB , " Could not find key '%s' in admin table \n " , key ) ;
2017-11-19 01:46:28 -05:00
return 0 ;
}
return value ;
}
2017-11-12 03:27:26 -05:00
int64_t
db_admin_getint64 ( const char * key )
{
int64_t value = 0 ;
int ret ;
ret = admin_get ( key , DB_TYPE_INT64 , & value ) ;
if ( ret < 0 )
{
2018-03-03 14:13:09 -05:00
DPRINTF ( E_DBG , L_DB , " Could not find key '%s' in admin table \n " , key ) ;
2017-11-12 03:27:26 -05:00
return 0 ;
}
return value ;
}
2014-08-15 16:54:47 -04:00
int
db_admin_delete ( const char * key )
{
# define Q_TMPL "DELETE FROM admin where key='%q';"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , key ) ;
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2014-08-15 16:54:47 -04:00
# undef Q_TMPL
}
2010-01-30 11:01:27 -05:00
2010-11-02 13:40:14 -04:00
/* Speakers */
int
2017-06-19 15:52:01 -04:00
db_speaker_save ( struct output_device * device )
2010-11-02 13:40:14 -04:00
{
2017-06-19 15:52:01 -04:00
# define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name, auth_key) VALUES (%" PRIi64 ", %d, %d, %Q, %Q);"
2010-11-02 13:40:14 -04:00
char * query ;
2017-06-19 15:52:01 -04:00
query = sqlite3_mprintf ( Q_TMPL , device - > id , device - > selected , device - > volume , device - > name , device - > auth_key ) ;
2010-11-02 13:40:14 -04:00
2014-08-22 16:51:13 -04:00
return db_query_run ( query , 1 , 0 ) ;
2010-11-02 13:40:14 -04:00
# undef Q_TMPL
}
int
2017-06-19 15:52:01 -04:00
db_speaker_get ( struct output_device * device , uint64_t id )
2010-11-02 13:40:14 -04:00
{
2017-06-19 15:52:01 -04:00
# define Q_TMPL "SELECT s.selected, s.volume, s.name, s.auth_key FROM speakers s WHERE s.id = %" PRIi64 ";"
2010-11-02 13:40:14 -04:00
sqlite3_stmt * stmt ;
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , id ) ;
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 = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
ret = - 1 ;
goto out ;
}
ret = db_blocking_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
if ( ret ! = SQLITE_DONE )
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_finalize ( stmt ) ;
ret = - 1 ;
goto out ;
}
2017-06-19 15:52:01 -04:00
device - > id = id ;
device - > selected = sqlite3_column_int ( stmt , 0 ) ;
device - > volume = sqlite3_column_int ( stmt , 1 ) ;
free ( device - > name ) ;
device - > name = safe_strdup ( ( char * ) sqlite3_column_text ( stmt , 2 ) ) ;
free ( device - > auth_key ) ;
device - > auth_key = safe_strdup ( ( char * ) sqlite3_column_text ( stmt , 3 ) ) ;
2010-11-02 13:40:14 -04:00
2011-06-11 11:24:59 -04:00
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
2010-11-02 13:40:14 -04:00
sqlite3_finalize ( stmt ) ;
ret = 0 ;
out :
sqlite3_free ( query ) ;
return ret ;
# undef Q_TMPL
}
void
db_speaker_clear_all ( void )
{
2014-08-22 16:51:13 -04:00
db_query_run ( " UPDATE speakers SET selected = 0; " , 0 , 0 ) ;
2010-11-02 13:40:14 -04:00
}
2016-11-04 10:34:00 -04:00
/* Queue */
2010-11-02 13:40:14 -04:00
2016-11-04 10:34:00 -04:00
/*
2017-11-25 11:57:35 -05:00
* Start a new transaction for modifying the queue . Returns the new queue version for the following changes .
* After finishing all queue modifications ' queue_transaction_end ' needs to be called .
2016-11-04 10:34:00 -04:00
*/
2017-11-25 11:57:35 -05:00
static int
queue_transaction_begin ( )
2009-06-11 10:12:17 -04:00
{
2016-12-03 01:00:28 -05:00
int queue_version ;
2009-06-11 10:12:17 -04:00
2016-11-04 10:34:00 -04:00
db_transaction_begin ( ) ;
2009-06-11 10:12:17 -04:00
2017-11-19 15:14:41 -05:00
queue_version = db_admin_getint ( DB_ADMIN_QUEUE_VERSION ) ;
2016-12-03 01:00:28 -05:00
queue_version + + ;
2009-06-11 10:12:17 -04:00
2017-11-25 11:57:35 -05:00
return queue_version ;
}
/*
* If retval = = 0 , updates the version of the queue in the admin table , commits the transaction
* and notifies listener of LISTENER_QUEUE about the changes .
* If retval < 0 , rollsback the transaction .
*
* This function must be called after modifying the queue .
*
* @ param retval ' retval ' = = 0 , if modifying the queue was successful or ' retval ' < 0 if an error occurred
* @ param queue_version The new queue version , for the pending modifications
*/
static void
queue_transaction_end ( int retval , int queue_version )
{
int ret ;
if ( retval ! = 0 )
goto error ;
2017-11-19 15:14:41 -05:00
ret = db_admin_setint ( DB_ADMIN_QUEUE_VERSION , queue_version ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto error ;
2009-06-11 10:12:17 -04:00
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
2017-01-07 18:24:40 -05:00
listener_notify ( LISTENER_QUEUE ) ;
2017-11-25 11:57:35 -05:00
return ;
error :
db_transaction_rollback ( ) ;
2009-06-11 10:12:17 -04:00
}
2016-11-04 10:34:00 -04:00
static int
2017-11-25 11:57:35 -05:00
queue_reshuffle ( uint32_t item_id , int queue_version ) ;
static int
queue_add_file ( struct db_media_file_info * dbmfi , int pos , int shuffle_pos , int queue_version )
2016-11-04 10:34:00 -04:00
{
# define Q_TMPL "INSERT INTO queue " \
" (id, file_id, song_length, data_kind, media_kind, " \
" pos, shuffle_pos, path, virtual_path, title, " \
2019-02-01 10:39:10 -05:00
" artist, composer, album_artist, album, genre, songalbumid, songartistid, " \
2016-11-04 10:34:00 -04:00
" time_modified, artist_sort, album_sort, album_artist_sort, year, " \
2019-06-07 04:53:37 -04:00
" type, bitrate, samplerate, channels, " \
2017-11-25 11:57:35 -05:00
" track, disc, queue_version) " \
2016-11-04 10:34:00 -04:00
" VALUES " \
" (NULL, %s, %s, %s, %s, " \
" %d, %d, %Q, %Q, %Q, " \
2019-02-01 10:39:10 -05:00
" %Q, %Q, %Q, %Q, %Q, %s, %s, " \
2016-11-04 10:34:00 -04:00
" %s, %Q, %Q, %Q, %s, " \
2019-06-07 04:53:37 -04:00
" %Q, %s, %s, %s, " \
2017-11-25 11:57:35 -05:00
" %s, %s, %d); "
2016-11-04 10:34:00 -04:00
2009-06-11 10:12:17 -04:00
char * query ;
2016-11-04 10:34:00 -04:00
int ret ;
2009-06-11 10:12:17 -04:00
2016-11-04 10:34:00 -04:00
query = sqlite3_mprintf ( Q_TMPL ,
dbmfi - > id , dbmfi - > song_length , dbmfi - > data_kind , dbmfi - > media_kind ,
2018-09-06 15:16:27 -04:00
pos , shuffle_pos , dbmfi - > path , dbmfi - > virtual_path , dbmfi - > title ,
2019-02-01 10:39:10 -05:00
dbmfi - > artist , dbmfi - > composer , dbmfi - > album_artist , dbmfi - > album , dbmfi - > genre , dbmfi - > songalbumid , dbmfi - > songartistid ,
2016-11-04 10:34:00 -04:00
dbmfi - > time_modified , dbmfi - > artist_sort , dbmfi - > album_sort , dbmfi - > album_artist_sort , dbmfi - > year ,
2019-06-07 04:53:37 -04:00
dbmfi - > type , dbmfi - > bitrate , dbmfi - > samplerate , dbmfi - > channels ,
2017-11-25 11:57:35 -05:00
dbmfi - > track , dbmfi - > disc , queue_version ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2009-06-11 10:12:17 -04:00
2016-11-04 10:34:00 -04:00
return ret ;
2009-06-10 12:11:11 -04:00
# undef Q_TMPL
}
2018-03-09 13:03:43 -05:00
static int
queue_add_item ( struct db_queue_item * item , int pos , int shuffle_pos , int queue_version )
{
# define Q_TMPL "INSERT INTO queue " \
" (id, file_id, song_length, data_kind, media_kind, " \
" pos, shuffle_pos, path, virtual_path, title, " \
2019-02-01 10:39:10 -05:00
" artist, composer, album_artist, album, genre, songalbumid, songartistid, " \
2018-03-09 13:03:43 -05:00
" time_modified, artist_sort, album_sort, album_artist_sort, year, " \
2019-06-07 04:53:37 -04:00
" type, bitrate, samplerate, channels, " \
2018-11-28 14:31:50 -05:00
" track, disc, artwork_url, queue_version) " \
2018-03-09 13:03:43 -05:00
" VALUES " \
" (NULL, %d, %d, %d, %d, " \
" %d, %d, %Q, %Q, %Q, " \
2019-02-01 10:39:10 -05:00
" %Q, %Q, %Q, %Q, %Q, % " PRIi64 " , % " PRIi64 " , " \
2018-03-09 13:03:43 -05:00
" %d, %Q, %Q, %Q, %d, " \
2019-06-07 04:53:37 -04:00
" %Q, % " PRIu32 " , % " PRIu32 " , % " PRIu32 " , " \
2018-11-28 14:31:50 -05:00
" %d, %d, %Q, %d); "
2018-03-09 13:03:43 -05:00
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL ,
item - > file_id , item - > song_length , item - > data_kind , item - > media_kind ,
2018-10-13 02:30:41 -04:00
pos , shuffle_pos , item - > path , item - > virtual_path , item - > title ,
2019-02-01 10:39:10 -05:00
item - > artist , item - > composer , item - > album_artist , item - > album , item - > genre , item - > songalbumid , item - > songartistid ,
2018-03-09 13:03:43 -05:00
item - > time_modified , item - > artist_sort , item - > album_sort , item - > album_artist_sort , item - > year ,
2019-06-07 04:53:37 -04:00
item - > type , item - > bitrate , item - > samplerate , item - > channels ,
2018-11-28 14:31:50 -05:00
item - > track , item - > disc , item - > artwork_url , queue_version ) ;
2018-03-09 13:03:43 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
return ret ;
# undef Q_TMPL
}
2017-01-22 17:10:15 -05:00
int
db_queue_update_item ( struct db_queue_item * qi )
{
# define Q_TMPL "UPDATE queue SET " \
" file_id = %d, song_length = %d, data_kind = %d, media_kind = %d, " \
" pos = %d, shuffle_pos = %d, path = '%q', virtual_path = %Q, " \
" title = %Q, artist = %Q, album_artist = %Q, album = %Q, " \
2018-11-21 07:52:13 -05:00
" composer = %Q, " \
2019-02-01 10:39:10 -05:00
" genre = %Q, time_modified = %d, " \
" songalbumid = % " PRIi64 " , songartistid = % " PRIi64 " , " \
2017-01-22 17:10:15 -05:00
" artist_sort = %Q, album_sort = %Q, album_artist_sort = %Q, " \
2017-11-25 11:57:35 -05:00
" year = %d, track = %d, disc = %d, artwork_url = %Q, " \
" queue_version = %d " \
2017-01-22 17:10:15 -05:00
" WHERE id = %d; "
2017-11-25 11:57:35 -05:00
int queue_version ;
2017-01-22 17:10:15 -05:00
char * query ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2017-01-22 17:10:15 -05:00
query = sqlite3_mprintf ( Q_TMPL ,
qi - > file_id , qi - > song_length , qi - > data_kind , qi - > media_kind ,
qi - > pos , qi - > shuffle_pos , qi - > path , qi - > virtual_path ,
qi - > title , qi - > artist , qi - > album_artist , qi - > album ,
2018-11-21 07:52:13 -05:00
qi - > composer ,
2019-02-01 10:39:10 -05:00
qi - > genre , qi - > time_modified ,
qi - > songalbumid , qi - > songartistid ,
2017-01-22 17:10:15 -05:00
qi - > artist_sort , qi - > album_sort , qi - > album_artist_sort ,
2017-11-25 11:57:35 -05:00
qi - > year , qi - > track , qi - > disc , qi - > artwork_url , queue_version ,
2017-01-22 17:10:15 -05:00
qi - > id ) ;
ret = db_query_run ( query , 1 , 0 ) ;
2017-12-02 10:44:29 -05:00
/* MPD changes playlist version when metadata changes */
2017-11-25 11:57:35 -05:00
queue_transaction_end ( ret , queue_version ) ;
2017-12-02 10:44:29 -05:00
2017-01-22 17:10:15 -05:00
return ret ;
# undef Q_TMPL
}
2016-11-04 10:34:00 -04:00
/*
* Adds the files matching the given query to the queue after the item with the given item id
*
* The files table is queried with the given parameters and all found files are added after the
* item with the given item id to the " normal " queue . They are appended to end of the shuffled queue
* ( assuming that the shuffled queue will get reshuffled after adding new items ) .
*
* The function returns - 1 on failure ( e . g . error reading from database ) and if the given item id
* does not exist . It wraps all database access in a transaction and performs a rollback if an error
* occurs , leaving the queue in a consistent state .
*
* @ param qp Query parameters for the files table
* @ param item_id Files are added after item with this id
* @ return 0 on success , - 1 on failure
*/
int
db_queue_add_by_queryafteritemid ( struct query_params * qp , uint32_t item_id )
2009-06-10 12:11:11 -04:00
{
2016-11-04 10:34:00 -04:00
int pos ;
2009-06-10 12:11:11 -04:00
int ret ;
2016-11-04 10:34:00 -04:00
// Position of the first new item
pos = db_queue_get_pos ( item_id , 0 ) ;
if ( pos < 0 )
2009-06-10 12:11:11 -04:00
{
2018-09-06 15:16:27 -04:00
return - 1 ;
2009-06-10 12:11:11 -04:00
}
2016-11-04 10:34:00 -04:00
pos + + ;
2009-06-10 12:11:11 -04:00
2018-09-06 15:16:27 -04:00
ret = db_queue_add_by_query ( qp , 0 , 0 , pos , NULL , NULL ) ;
2017-11-25 11:57:35 -05:00
return ret ;
2014-06-09 17:42:02 -04:00
}
2018-03-09 13:03:43 -05:00
int
2018-10-13 02:30:41 -04:00
db_queue_add_start ( struct db_queue_add_info * queue_add_info , int pos )
2018-03-09 13:03:43 -05:00
{
2019-05-28 17:40:41 -04:00
uint32_t queue_count ;
int ret ;
2018-03-09 13:03:43 -05:00
memset ( queue_add_info , 0 , sizeof ( struct db_queue_add_info ) ) ;
queue_add_info - > queue_version = queue_transaction_begin ( ) ;
2019-05-28 17:40:41 -04:00
ret = db_queue_get_count ( & queue_count ) ;
if ( ret < 0 )
2018-03-09 13:03:43 -05:00
{
ret = - 1 ;
queue_transaction_end ( ret , queue_add_info - > queue_version ) ;
return ret ;
}
2018-10-13 02:30:41 -04:00
queue_add_info - > pos = queue_count ;
queue_add_info - > shuffle_pos = queue_count ;
if ( pos > = 0 & & pos < queue_count )
queue_add_info - > pos = pos ;
queue_add_info - > start_pos = queue_add_info - > pos ;
return 0 ;
2018-03-09 13:03:43 -05:00
}
2018-10-13 02:30:41 -04:00
int
2018-11-10 01:41:36 -05:00
db_queue_add_end ( struct db_queue_add_info * queue_add_info , char reshuffle , uint32_t item_id , int ret )
2018-03-09 13:03:43 -05:00
{
2018-10-13 02:30:41 -04:00
char * query ;
// Update pos for all items from the given position
if ( ret = = 0 )
{
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos + %d, queue_version = %d WHERE pos >= %d AND queue_version < %d; " ,
queue_add_info - > count , queue_add_info - > queue_version , queue_add_info - > start_pos , queue_add_info - > queue_version ) ;
ret = db_query_run ( query , 1 , 0 ) ;
}
2018-11-10 01:41:36 -05:00
// Reshuffle after adding new items
if ( ret = = 0 & & reshuffle )
{
ret = queue_reshuffle ( item_id , queue_add_info - > queue_version ) ;
}
2018-03-09 13:03:43 -05:00
queue_transaction_end ( ret , queue_add_info - > queue_version ) ;
2018-10-13 02:30:41 -04:00
return ret ;
2018-03-09 13:03:43 -05:00
}
int
db_queue_add_item ( struct db_queue_add_info * queue_add_info , struct db_queue_item * item )
{
int ret ;
fixup_tags_queue_item ( item ) ;
2018-10-13 02:30:41 -04:00
ret = queue_add_item ( item , queue_add_info - > pos , queue_add_info - > shuffle_pos , queue_add_info - > queue_version ) ;
2018-03-09 13:03:43 -05:00
if ( ret = = 0 )
2018-10-13 02:30:41 -04:00
{
queue_add_info - > pos + + ;
queue_add_info - > shuffle_pos + + ;
queue_add_info - > count + + ;
if ( queue_add_info - > new_item_id = = 0 )
queue_add_info - > new_item_id = ( int ) sqlite3_last_insert_rowid ( hdl ) ;
}
2018-03-09 13:03:43 -05:00
return ret ;
# undef Q_TMPL
}
2016-11-04 10:34:00 -04:00
/*
* Adds the files matching the given query to the queue
*
* The files table is queried with the given parameters and all found files are added to the end of the
* " normal " queue and the shuffled queue .
*
* The function returns - 1 on failure ( e . g . error reading from database ) . It wraps all database access
* in a transaction and performs a rollback if an error occurs , leaving the queue in a consistent state .
*
* @ param qp Query parameters for the files table
* @ param reshuffle If 1 queue will be reshuffled after adding new items
* @ param item_id The base item id , all items after this will be reshuffled
2018-09-06 15:16:27 -04:00
* @ param position The position in the queue for the new queue item , - 1 to add at end of queue
* @ param count If not NULL returns the number of items added to the queue
* @ param new_item_id If not NULL return the queue item id of the first new queue item
* @ return 0 on success , - 1 on failure
2016-11-04 10:34:00 -04:00
*/
2014-06-09 17:42:02 -04:00
int
2018-09-06 15:16:27 -04:00
db_queue_add_by_query ( struct query_params * qp , char reshuffle , uint32_t item_id , int position , int * count , int * new_item_id )
2014-06-09 17:42:02 -04:00
{
2016-11-04 10:34:00 -04:00
struct db_media_file_info dbmfi ;
2018-09-06 15:16:27 -04:00
char * query ;
2017-11-25 11:57:35 -05:00
int queue_version ;
2019-05-28 17:40:41 -04:00
uint32_t queue_count ;
2016-11-04 10:34:00 -04:00
int pos ;
int ret ;
2014-06-09 17:42:02 -04:00
2018-09-06 15:16:27 -04:00
if ( new_item_id )
* new_item_id = 0 ;
if ( count )
* count = 0 ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
2019-05-28 17:40:41 -04:00
ret = db_queue_get_count ( & queue_count ) ;
if ( ret < 0 )
2014-06-09 17:42:02 -04:00
{
2017-11-25 11:57:35 -05:00
ret = - 1 ;
goto end_transaction ;
2014-06-09 17:42:02 -04:00
}
2016-11-04 10:34:00 -04:00
ret = db_query_start ( qp ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2014-06-09 17:42:02 -04:00
2016-11-04 10:34:00 -04:00
DPRINTF ( E_DBG , L_DB , " Player queue query returned %d items \n " , qp - > results ) ;
2014-06-09 17:42:02 -04:00
2017-03-05 02:46:00 -05:00
if ( qp - > results = = 0 )
{
db_query_end ( qp ) ;
2017-12-13 16:01:19 -05:00
db_transaction_end ( ) ;
return 0 ;
2017-03-05 02:46:00 -05:00
}
2018-09-06 15:16:27 -04:00
if ( position < 0 | | position > queue_count )
{
pos = queue_count ;
}
else
{
pos = position ;
// Update pos for all items from the given position (make room for the new items in the queue)
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos + %d, queue_version = %d WHERE pos >= %d; " , qp - > results , queue_version , pos ) ;
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
goto end_transaction ;
}
2016-11-04 10:34:00 -04:00
while ( ( ( ret = db_query_fetch_file ( qp , & dbmfi ) ) = = 0 ) & & ( dbmfi . id ) )
2014-06-09 17:42:02 -04:00
{
2018-09-06 15:16:27 -04:00
ret = queue_add_file ( & dbmfi , pos , queue_count , queue_version ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
DPRINTF ( E_DBG , L_DB , " Failed to add song id %s (%s) \n " , dbmfi . id , dbmfi . title ) ;
break ;
}
DPRINTF ( E_DBG , L_DB , " Added song id %s (%s) to queue \n " , dbmfi . id , dbmfi . title ) ;
2018-09-06 15:16:27 -04:00
if ( new_item_id & & * new_item_id = = 0 )
* new_item_id = ( int ) sqlite3_last_insert_rowid ( hdl ) ;
if ( count )
( * count ) + + ;
2016-11-04 10:34:00 -04:00
pos + + ;
2018-09-06 15:16:27 -04:00
queue_count + + ;
2016-11-04 10:34:00 -04:00
}
db_query_end ( qp ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2009-06-10 12:11:11 -04:00
2016-11-04 10:34:00 -04:00
// Reshuffle after adding new items
if ( reshuffle )
{
2017-11-25 11:57:35 -05:00
ret = queue_reshuffle ( item_id , queue_version ) ;
2016-11-04 10:34:00 -04:00
}
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2018-09-06 15:16:27 -04:00
return ret ;
2009-06-10 12:11:11 -04:00
}
2016-11-04 10:34:00 -04:00
/*
* Adds the items of the stored playlist with the given id to the end of the queue
*
* @ param plid Id of the stored playlist
* @ param reshuffle If 1 queue will be reshuffled after adding new items
* @ param item_id The base item id , all items after this will be reshuffled
2018-09-06 15:16:27 -04:00
* @ param position The position in the queue for the new queue item , - 1 to add at end of queue
* @ param count If not NULL returns the number of items added to the queue
* @ param new_item_id If not NULL return the queue item id of the first new queue item
* @ return 0 on success , - 1 on failure
2016-11-04 10:34:00 -04:00
*/
int
2018-09-06 15:16:27 -04:00
db_queue_add_by_playlistid ( int plid , char reshuffle , uint32_t item_id , int position , int * count , int * new_item_id )
2009-06-11 09:24:10 -04:00
{
2016-11-04 10:34:00 -04:00
struct query_params qp ;
int ret ;
2009-06-11 09:24:10 -04:00
2016-11-04 10:34:00 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
2009-06-11 09:24:10 -04:00
2016-11-04 10:34:00 -04:00
qp . id = plid ;
qp . type = Q_PLITEMS ;
2009-06-11 09:24:10 -04:00
2018-09-06 15:16:27 -04:00
ret = db_queue_add_by_query ( & qp , reshuffle , item_id , position , count , new_item_id ) ;
2016-11-04 10:34:00 -04:00
return ret ;
2009-06-11 09:24:10 -04:00
}
2016-11-04 10:34:00 -04:00
/*
* Adds the file with the given id to the queue
*
* @ param id Id of the file
* @ param reshuffle If 1 queue will be reshuffled after adding new items
* @ param item_id The base item id , all items after this will be reshuffled
2018-09-06 15:16:27 -04:00
* @ param position The position in the queue for the new queue item , - 1 to add at end of queue
* @ param count If not NULL returns the number of items added to the queue
* @ param new_item_id If not NULL return the queue item id of the first new queue item
* @ return 0 on success , - 1 on failure
2016-11-04 10:34:00 -04:00
*/
int
2018-09-06 15:16:27 -04:00
db_queue_add_by_fileid ( int id , char reshuffle , uint32_t item_id , int position , int * count , int * new_item_id )
2009-06-11 09:24:10 -04:00
{
2016-11-04 10:34:00 -04:00
struct query_params qp ;
char buf [ 124 ] ;
int ret ;
2009-06-11 09:24:10 -04:00
2016-11-04 10:34:00 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
2009-06-11 09:24:10 -04:00
2016-11-04 10:34:00 -04:00
qp . type = Q_ITEMS ;
snprintf ( buf , sizeof ( buf ) , " f.id = % " PRIu32 , id ) ;
qp . filter = buf ;
2009-06-11 09:24:10 -04:00
2018-09-06 15:16:27 -04:00
ret = db_queue_add_by_query ( & qp , reshuffle , item_id , position , count , new_item_id ) ;
2016-11-04 10:34:00 -04:00
return ret ;
2009-06-11 09:24:10 -04:00
}
2009-06-10 12:11:11 -04:00
2016-11-04 10:34:00 -04:00
static int
2017-04-16 17:54:07 -04:00
queue_enum_start ( struct query_params * qp )
2016-11-04 10:34:00 -04:00
{
2018-01-20 18:22:21 -05:00
# define Q_TMPL "SELECT * FROM queue f WHERE %s ORDER BY %s;"
2017-10-14 14:16:34 -04:00
sqlite3_stmt * stmt ;
2016-11-04 10:34:00 -04:00
char * query ;
2016-12-04 02:18:33 -05:00
const char * orderby ;
2016-11-04 10:34:00 -04:00
int ret ;
2017-04-16 17:54:07 -04:00
qp - > stmt = NULL ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
if ( qp - > sort )
orderby = sort_clause [ qp - > sort ] ;
2016-11-04 10:34:00 -04:00
else
2016-12-04 02:18:33 -05:00
orderby = sort_clause [ S_POS ] ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
if ( qp - > filter )
2017-10-14 14:16:34 -04:00
query = sqlite3_mprintf ( Q_TMPL , qp - > filter , orderby ) ;
2016-11-04 10:34:00 -04:00
else
query = sqlite3_mprintf ( Q_TMPL , " 1=1 " , orderby ) ;
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 ) ;
2017-10-14 14:16:34 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2016-11-04 10:34:00 -04:00
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 ) ;
2017-10-14 14:16:34 -04:00
qp - > stmt = stmt ;
2016-11-04 10:34:00 -04:00
return 0 ;
# undef Q_TMPL
}
static inline char *
strdup_if ( char * str , int cond )
{
if ( str = = NULL )
return NULL ;
if ( cond )
return strdup ( str ) ;
return str ;
}
static int
2017-04-16 17:54:07 -04:00
queue_enum_fetch ( struct query_params * qp , struct db_queue_item * queue_item , int keep_item )
2016-11-04 10:34:00 -04:00
{
int ret ;
memset ( queue_item , 0 , sizeof ( struct db_queue_item ) ) ;
2017-04-16 17:54:07 -04:00
if ( ! qp - > stmt )
2016-11-04 10:34:00 -04:00
{
DPRINTF ( E_LOG , L_DB , " Queue enum not started! \n " ) ;
return - 1 ;
}
2017-04-16 17:54:07 -04:00
ret = db_blocking_step ( qp - > stmt ) ;
2016-11-04 10:34:00 -04:00
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of queue 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 ;
}
2017-04-16 17:54:07 -04:00
queue_item - > id = ( uint32_t ) sqlite3_column_int ( qp - > stmt , 0 ) ;
queue_item - > file_id = ( uint32_t ) sqlite3_column_int ( qp - > stmt , 1 ) ;
queue_item - > pos = ( uint32_t ) sqlite3_column_int ( qp - > stmt , 2 ) ;
queue_item - > shuffle_pos = ( uint32_t ) sqlite3_column_int ( qp - > stmt , 3 ) ;
queue_item - > data_kind = sqlite3_column_int ( qp - > stmt , 4 ) ;
queue_item - > media_kind = sqlite3_column_int ( qp - > stmt , 5 ) ;
queue_item - > song_length = ( uint32_t ) sqlite3_column_int ( qp - > stmt , 6 ) ;
queue_item - > path = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 7 ) , keep_item ) ;
queue_item - > virtual_path = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 8 ) , keep_item ) ;
queue_item - > title = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 9 ) , keep_item ) ;
queue_item - > artist = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 10 ) , keep_item ) ;
queue_item - > album_artist = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 11 ) , keep_item ) ;
queue_item - > album = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 12 ) , keep_item ) ;
queue_item - > genre = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 13 ) , keep_item ) ;
queue_item - > songalbumid = sqlite3_column_int64 ( qp - > stmt , 14 ) ;
queue_item - > time_modified = sqlite3_column_int ( qp - > stmt , 15 ) ;
queue_item - > artist_sort = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 16 ) , keep_item ) ;
queue_item - > album_sort = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 17 ) , keep_item ) ;
queue_item - > album_artist_sort = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 18 ) , keep_item ) ;
queue_item - > year = sqlite3_column_int ( qp - > stmt , 19 ) ;
queue_item - > track = sqlite3_column_int ( qp - > stmt , 20 ) ;
queue_item - > disc = sqlite3_column_int ( qp - > stmt , 21 ) ;
queue_item - > artwork_url = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 22 ) , keep_item ) ;
2019-10-20 02:38:40 -04:00
queue_item - > queue_version = sqlite3_column_int ( qp - > stmt , 23 ) ;
2018-11-21 07:52:13 -05:00
queue_item - > composer = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 24 ) , keep_item ) ;
2019-02-01 10:39:10 -05:00
queue_item - > songartistid = sqlite3_column_int64 ( qp - > stmt , 25 ) ;
2019-06-04 16:51:44 -04:00
queue_item - > type = strdup_if ( ( char * ) sqlite3_column_text ( qp - > stmt , 26 ) , keep_item ) ;
queue_item - > bitrate = sqlite3_column_int ( qp - > stmt , 27 ) ;
queue_item - > samplerate = sqlite3_column_int ( qp - > stmt , 28 ) ;
2019-06-07 04:53:37 -04:00
queue_item - > channels = sqlite3_column_int ( qp - > stmt , 29 ) ;
2016-11-04 10:34:00 -04:00
return 0 ;
}
int
2017-04-16 17:54:07 -04:00
db_queue_enum_start ( struct query_params * qp )
2016-11-04 10:34:00 -04:00
{
int ret ;
db_transaction_begin ( ) ;
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
db_transaction_rollback ( ) ;
return ret ;
}
void
2017-04-16 17:54:07 -04:00
db_queue_enum_end ( struct query_params * qp )
2016-11-04 10:34:00 -04:00
{
2017-04-16 17:54:07 -04:00
db_query_end ( qp ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
}
int
2017-04-16 17:54:07 -04:00
db_queue_enum_fetch ( struct query_params * qp , struct db_queue_item * queue_item )
2016-11-04 10:34:00 -04:00
{
2017-04-16 17:54:07 -04:00
return queue_enum_fetch ( qp , queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
}
int
db_queue_get_pos ( uint32_t item_id , char shuffle )
{
# define Q_TMPL "SELECT pos FROM queue WHERE id = %d;"
# define Q_TMPL_SHUFFLE "SELECT shuffle_pos FROM queue WHERE id = %d;"
char * query ;
int pos ;
if ( shuffle )
query = sqlite3_mprintf ( Q_TMPL_SHUFFLE , item_id ) ;
else
query = sqlite3_mprintf ( Q_TMPL , item_id ) ;
2016-12-09 10:20:03 -05:00
pos = db_get_one_int ( query ) ;
2016-11-04 10:34:00 -04:00
sqlite3_free ( query ) ;
return pos ;
# undef Q_TMPL
# undef Q_TMPL_SHUFFLE
}
static int
2016-12-04 03:30:21 -05:00
queue_fetch_byitemid ( uint32_t item_id , struct db_queue_item * queue_item , int with_metadata )
2016-11-04 10:34:00 -04:00
{
2017-04-16 17:54:07 -04:00
struct query_params qp ;
2016-11-04 10:34:00 -04:00
int ret ;
2017-04-16 17:54:07 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
qp . filter = sqlite3_mprintf ( " id = %d " , item_id ) ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( & qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
2017-04-16 17:54:07 -04:00
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
return - 1 ;
}
2017-04-16 17:54:07 -04:00
ret = queue_enum_fetch ( & qp , queue_item , with_metadata ) ;
db_query_end ( & qp ) ;
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
struct db_queue_item *
db_queue_fetch_byitemid ( uint32_t item_id )
{
struct db_queue_item * queue_item ;
int ret ;
queue_item = calloc ( 1 , sizeof ( struct db_queue_item ) ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for queue_item \n " ) ;
return NULL ;
}
db_transaction_begin ( ) ;
2016-12-04 03:30:21 -05:00
ret = queue_fetch_byitemid ( item_id , queue_item , 1 ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
if ( ret < 0 )
{
free_queue_item ( queue_item , 0 ) ;
DPRINTF ( E_LOG , L_DB , " Error fetching queue item by item id \n " ) ;
return NULL ;
}
2016-12-03 02:03:19 -05:00
else if ( queue_item - > id = = 0 )
2016-11-12 12:45:54 -05:00
{
// No item found
free_queue_item ( queue_item , 0 ) ;
return NULL ;
}
2016-11-04 10:34:00 -04:00
return queue_item ;
}
struct db_queue_item *
db_queue_fetch_byfileid ( uint32_t file_id )
{
struct db_queue_item * queue_item ;
2017-04-16 17:54:07 -04:00
struct query_params qp ;
2016-11-04 10:34:00 -04:00
int ret ;
2017-04-16 17:54:07 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
2016-11-04 10:34:00 -04:00
queue_item = calloc ( 1 , sizeof ( struct db_queue_item ) ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for queue_item \n " ) ;
return NULL ;
}
db_transaction_begin ( ) ;
2017-04-16 17:54:07 -04:00
qp . filter = sqlite3_mprintf ( " file_id = %d " , file_id ) ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( & qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
2017-04-16 17:54:07 -04:00
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
free_queue_item ( queue_item , 0 ) ;
DPRINTF ( E_LOG , L_DB , " Error fetching queue item by file id \n " ) ;
return NULL ;
}
2017-04-16 17:54:07 -04:00
ret = queue_enum_fetch ( & qp , queue_item , 1 ) ;
db_query_end ( & qp ) ;
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
if ( ret < 0 )
{
free_queue_item ( queue_item , 0 ) ;
DPRINTF ( E_LOG , L_DB , " Error fetching queue item by file id \n " ) ;
return NULL ;
}
2016-12-03 02:03:19 -05:00
else if ( queue_item - > id = = 0 )
2016-11-12 12:45:54 -05:00
{
// No item found
free_queue_item ( queue_item , 0 ) ;
return NULL ;
}
2016-11-04 10:34:00 -04:00
return queue_item ;
}
static int
2016-12-04 03:30:21 -05:00
queue_fetch_bypos ( uint32_t pos , char shuffle , struct db_queue_item * queue_item , int with_metadata )
2016-11-04 10:34:00 -04:00
{
2017-04-16 17:54:07 -04:00
struct query_params qp ;
2016-11-04 10:34:00 -04:00
int ret ;
2017-04-16 17:54:07 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
2016-11-04 10:34:00 -04:00
if ( shuffle )
2017-04-16 17:54:07 -04:00
qp . filter = sqlite3_mprintf ( " shuffle_pos = %d " , pos ) ;
2016-11-04 10:34:00 -04:00
else
2017-04-16 17:54:07 -04:00
qp . filter = sqlite3_mprintf ( " pos = %d " , pos ) ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( & qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
2017-04-16 17:54:07 -04:00
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
return - 1 ;
}
2017-04-16 17:54:07 -04:00
ret = queue_enum_fetch ( & qp , queue_item , with_metadata ) ;
db_query_end ( & qp ) ;
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
struct db_queue_item *
db_queue_fetch_bypos ( uint32_t pos , char shuffle )
{
struct db_queue_item * queue_item ;
int ret ;
queue_item = calloc ( 1 , sizeof ( struct db_queue_item ) ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_MAIN , " Out of memory for queue_item \n " ) ;
return NULL ;
}
db_transaction_begin ( ) ;
2016-12-04 03:30:21 -05:00
ret = queue_fetch_bypos ( pos , shuffle , queue_item , 1 ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
if ( ret < 0 )
{
free_queue_item ( queue_item , 0 ) ;
DPRINTF ( E_LOG , L_DB , " Error fetching queue item by pos id \n " ) ;
return NULL ;
}
2016-12-03 02:03:19 -05:00
else if ( queue_item - > id = = 0 )
2016-11-12 12:45:54 -05:00
{
// No item found
free_queue_item ( queue_item , 0 ) ;
return NULL ;
}
2016-11-04 10:34:00 -04:00
return queue_item ;
}
static int
2016-12-04 03:30:21 -05:00
queue_fetch_byposrelativetoitem ( int pos , uint32_t item_id , char shuffle , struct db_queue_item * queue_item , int with_metadata )
2016-11-04 10:34:00 -04:00
{
int pos_absolute ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Fetch by pos: pos (%d) relative to item with id (%d) \n " , pos , item_id ) ;
pos_absolute = db_queue_get_pos ( item_id , shuffle ) ;
if ( pos_absolute < 0 )
{
return - 1 ;
}
DPRINTF ( E_DBG , L_DB , " Fetch by pos: item (%d) has absolute pos %d \n " , item_id , pos_absolute ) ;
pos_absolute + = pos ;
2016-12-04 03:30:21 -05:00
ret = queue_fetch_bypos ( pos_absolute , shuffle , queue_item , with_metadata ) ;
2016-11-04 10:34:00 -04:00
2017-01-20 11:03:45 -05:00
if ( ret < 0 )
DPRINTF ( E_LOG , L_DB , " Error fetching item by pos: pos (%d) relative to item with id (%d) \n " , pos , item_id ) ;
else
DPRINTF ( E_DBG , L_DB , " Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d) \n " , queue_item - > id , queue_item - > pos , queue_item - > file_id ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
struct db_queue_item *
db_queue_fetch_byposrelativetoitem ( int pos , uint32_t item_id , char shuffle )
{
struct db_queue_item * queue_item ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Fetch by pos: pos (%d) relative to item with id (%d) \n " , pos , item_id ) ;
queue_item = calloc ( 1 , sizeof ( struct db_queue_item ) ) ;
if ( ! queue_item )
{
DPRINTF ( E_LOG , L_MAIN , " Out of memory for queue_item \n " ) ;
return NULL ;
}
db_transaction_begin ( ) ;
2016-12-04 03:30:21 -05:00
ret = queue_fetch_byposrelativetoitem ( pos , item_id , shuffle , queue_item , 1 ) ;
2016-11-04 10:34:00 -04:00
db_transaction_end ( ) ;
if ( ret < 0 )
{
free_queue_item ( queue_item , 0 ) ;
DPRINTF ( E_LOG , L_DB , " Error fetching queue item by pos relative to item id \n " ) ;
return NULL ;
}
2016-12-03 02:03:19 -05:00
else if ( queue_item - > id = = 0 )
2016-11-12 12:45:54 -05:00
{
// No item found
free_queue_item ( queue_item , 0 ) ;
return NULL ;
}
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
DPRINTF ( E_DBG , L_DB , " Fetch by pos: fetched item (id=%d, pos=%d, file-id=%d) \n " , queue_item - > id , queue_item - > pos , queue_item - > file_id ) ;
2016-11-04 10:34:00 -04:00
return queue_item ;
}
struct db_queue_item *
db_queue_fetch_next ( uint32_t item_id , char shuffle )
{
return db_queue_fetch_byposrelativetoitem ( 1 , item_id , shuffle ) ;
}
struct db_queue_item *
db_queue_fetch_prev ( uint32_t item_id , char shuffle )
{
return db_queue_fetch_byposrelativetoitem ( - 1 , item_id , shuffle ) ;
}
2017-02-04 01:12:53 -05:00
static int
2017-11-25 11:57:35 -05:00
queue_fix_pos ( enum sort_type sort , int queue_version )
2016-11-04 10:34:00 -04:00
{
2017-11-25 11:57:35 -05:00
# define Q_TMPL "UPDATE queue SET %q = %d, queue_version = %d WHERE id = %d and %q <> %d;"
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
struct query_params qp ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
char * query ;
int pos ;
int ret ;
2017-04-16 17:54:07 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
qp . sort = sort ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( & qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
return - 1 ;
}
pos = 0 ;
2017-04-16 17:54:07 -04:00
while ( ( ret = queue_enum_fetch ( & qp , & queue_item , 0 ) ) = = 0 & & ( queue_item . id > 0 ) )
2016-11-04 10:34:00 -04:00
{
if ( queue_item . pos ! = pos )
2017-02-04 01:12:53 -05:00
{
if ( sort = = S_SHUFFLE_POS )
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( Q_TMPL , " shuffle_pos " , pos , queue_version , queue_item . id , " shuffle_pos " , pos ) ;
2017-02-04 01:12:53 -05:00
else
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( Q_TMPL , " pos " , pos , queue_version , queue_item . id , " pos " , pos ) ;
2017-02-04 01:12:53 -05:00
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-02-04 01:12:53 -05:00
{
DPRINTF ( E_LOG , L_DB , " Failed to update item with item-id: %d \n " , queue_item . id ) ;
break ;
}
2016-11-04 10:34:00 -04:00
}
pos + + ;
}
2017-04-16 17:54:07 -04:00
db_query_end ( & qp ) ;
2017-02-04 01:12:53 -05:00
return ret ;
# undef Q_TMPL
}
/*
2017-11-25 11:57:35 -05:00
* Remove files that are disabled or non existent in the library and repair ordering of
2017-02-04 01:12:53 -05:00
* the queue ( shuffle and normal )
*/
int
db_queue_cleanup ( )
{
# define Q_TMPL "DELETE FROM queue WHERE NOT file_id IN (SELECT id from files WHERE disabled = 0);"
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
int queue_version ;
2017-02-04 01:12:53 -05:00
int deleted ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2017-02-04 01:12:53 -05:00
ret = db_query_run ( Q_TMPL , 0 , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2017-02-04 01:12:53 -05:00
deleted = sqlite3_changes ( hdl ) ;
if ( deleted < = 0 )
{
// Nothing to do
db_transaction_end ( ) ;
return 0 ;
}
2016-11-04 10:34:00 -04:00
2017-02-04 01:12:53 -05:00
// Update position of normal queue
2017-11-25 11:57:35 -05:00
ret = queue_fix_pos ( S_POS , queue_version ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2017-02-04 01:12:53 -05:00
// Update position of shuffle queue
2017-11-25 11:57:35 -05:00
ret = queue_fix_pos ( S_SHUFFLE_POS , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
return ret ;
2016-11-04 10:34:00 -04:00
# undef Q_TMPL
}
2017-02-11 03:28:35 -05:00
/*
* Removes all items from the queue except the item give by ' keep_item_id ' ( if ' keep_item_id ' > 0 ) .
*
* @ param keep_item_id item - id ( e . g . the now playing item ) to be left in the queue
*/
2016-11-04 10:34:00 -04:00
int
2017-02-11 03:28:35 -05:00
db_queue_clear ( uint32_t keep_item_id )
2016-11-04 10:34:00 -04:00
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2017-02-11 03:28:35 -05:00
char * query ;
2016-11-04 10:34:00 -04:00
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2017-02-11 03:28:35 -05:00
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " DELETE FROM queue where id <> %d; " , keep_item_id ) ;
2017-02-11 03:28:35 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
if ( ret = = 0 & & keep_item_id )
2016-11-04 10:34:00 -04:00
{
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = 0, shuffle_pos = 0, queue_version = %d where id = %d; " , queue_version , keep_item_id ) ;
2017-02-11 03:28:35 -05:00
ret = db_query_run ( query , 1 , 0 ) ;
}
2017-11-25 11:57:35 -05:00
queue_transaction_end ( ret , queue_version ) ;
2017-02-11 03:28:35 -05:00
2016-11-04 10:34:00 -04:00
return ret ;
}
static int
2017-11-25 11:57:35 -05:00
queue_delete_item ( struct db_queue_item * queue_item , int queue_version )
2016-11-04 10:34:00 -04:00
{
char * query ;
int ret ;
// Remove item with the given item_id
2016-12-03 02:03:19 -05:00
query = sqlite3_mprintf ( " DELETE FROM queue where id = %d; " , queue_item - > id ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
{
return - 1 ;
}
// Update pos for all items after the item with given item_id
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d; " , queue_version , queue_item - > pos ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
{
return - 1 ;
}
// Update shuffle_pos for all items after the item with given item_id
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = shuffle_pos - 1, queue_version = %d WHERE shuffle_pos > %d; " , queue_version , queue_item - > shuffle_pos ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
{
return - 1 ;
}
return 0 ;
}
int
db_queue_delete_byitemid ( uint32_t item_id )
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
2016-12-04 03:30:21 -05:00
ret = queue_fetch_byitemid ( item_id , & queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
if ( queue_item . id = = 0 )
2016-11-04 10:34:00 -04:00
{
db_transaction_end ( ) ;
return 0 ;
}
2017-11-25 11:57:35 -05:00
ret = queue_delete_item ( & queue_item , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
int
db_queue_delete_bypos ( uint32_t pos , int count )
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2017-02-04 01:26:48 -05:00
char * query ;
2016-11-04 10:34:00 -04:00
int to_pos ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
2017-02-04 01:26:48 -05:00
// Remove item with the given item_id
to_pos = pos + count ;
query = sqlite3_mprintf ( " DELETE FROM queue where pos >= %d AND pos < %d; " , pos , to_pos ) ;
ret = db_query_run ( query , 1 , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
ret = queue_fix_pos ( S_POS , queue_version ) ;
2017-02-04 01:26:48 -05:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
ret = queue_fix_pos ( S_SHUFFLE_POS , queue_version ) ;
2017-02-04 01:26:48 -05:00
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
int
db_queue_delete_byposrelativetoitem ( uint32_t pos , uint32_t item_id , char shuffle )
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
2016-12-04 03:30:21 -05:00
ret = queue_fetch_byposrelativetoitem ( pos , item_id , shuffle , & queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
if ( queue_item . id = = 0 )
2016-11-12 12:45:54 -05:00
{
// No item found
db_transaction_end ( ) ;
return 0 ;
}
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
ret = queue_delete_item ( & queue_item , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
return ret ;
}
/*
* Moves the queue item with the given id to the given position ( zero - based ) .
*
* @ param item_id Queue item id
* @ param pos_to target position in the queue ( zero - based )
2017-04-17 04:46:24 -04:00
* @ þ aram shuffle If 1 move item in the shuffle queue
2016-11-04 10:34:00 -04:00
* @ return 0 on success , - 1 on failure
*/
int
2017-04-17 04:46:24 -04:00
db_queue_move_byitemid ( uint32_t item_id , int pos_to , char shuffle )
2016-11-04 10:34:00 -04:00
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2016-11-04 10:34:00 -04:00
char * query ;
int pos_from ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
// Find item with the given item_id
2017-04-17 04:46:24 -04:00
pos_from = db_queue_get_pos ( item_id , shuffle ) ;
2016-11-04 10:34:00 -04:00
if ( pos_from < 0 )
{
2017-11-25 11:57:35 -05:00
ret = - 1 ;
goto end_transaction ;
2016-11-04 10:34:00 -04:00
}
// Update pos for all items after the item with given item_id
2017-04-17 04:46:24 -04:00
if ( shuffle )
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = shuffle_pos - 1, queue_version = %d WHERE shuffle_pos > %d; " , queue_version , pos_from ) ;
2017-04-17 04:46:24 -04:00
else
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d; " , queue_version , pos_from ) ;
2017-04-17 04:46:24 -04:00
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update pos for all items from the given pos_to
2017-04-17 04:46:24 -04:00
if ( shuffle )
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = shuffle_pos + 1, queue_version = %d WHERE shuffle_pos >= %d; " , queue_version , pos_to ) ;
2017-04-17 04:46:24 -04:00
else
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos + 1, queue_version = %d WHERE pos >= %d; " , queue_version , pos_to ) ;
2017-04-17 04:46:24 -04:00
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update item with the given item_id
2017-04-17 04:46:24 -04:00
if ( shuffle )
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = %d, queue_version = %d where id = %d; " , pos_to , queue_version , item_id ) ;
2017-04-17 04:46:24 -04:00
else
2017-12-30 01:59:36 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = %d, queue_version = %d where id = %d; " , pos_to , queue_version , item_id ) ;
2017-04-17 04:46:24 -04:00
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
return ret ;
2016-11-04 10:34:00 -04:00
}
/*
* Moves the queue item at the given position to the given position ( zero - based ) .
*
* @ param pos_from Position of the queue item to move
* @ param pos_to target position in the queue ( zero - based )
* @ return 0 on success , - 1 on failure
*/
int
db_queue_move_bypos ( int pos_from , int pos_to )
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
char * query ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
// Find item to move
2016-12-04 03:30:21 -05:00
ret = queue_fetch_bypos ( pos_from , 0 , & queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
if ( queue_item . id = = 0 )
2016-11-04 10:34:00 -04:00
{
db_transaction_end ( ) ;
return 0 ;
}
// Update pos for all items after the item with given position
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d; " , queue_version , queue_item . pos ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update pos for all items from the given pos_to
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos + 1, queue_version = %d WHERE pos >= %d; " , queue_version , pos_to ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update item with the given item_id
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = %d, queue_version = %d where id = %d; " , pos_to , queue_version , queue_item . id ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
return ret ;
2016-11-04 10:34:00 -04:00
}
/*
* Moves the queue item at the given position to the given target position . The positions
* are relavtive to the given base item ( item id ) .
*
* @ param from_pos Relative position of the queue item to the base item
* @ param to_offset Target position relative to the base item
* @ param item_id The base item id ( normaly the now playing item )
* @ return 0 on success , - 1 on failure
*/
int
db_queue_move_byposrelativetoitem ( uint32_t from_pos , uint32_t to_offset , uint32_t item_id , char shuffle )
{
2017-11-25 11:57:35 -05:00
int queue_version ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
char * query ;
int pos_move_from ;
int pos_move_to ;
int ret ;
2017-11-25 11:57:35 -05:00
queue_version = queue_transaction_begin ( ) ;
2016-11-04 10:34:00 -04:00
DPRINTF ( E_DBG , L_DB , " Move by pos: from %d offset %d relative to item (%d) \n " , from_pos , to_offset , item_id ) ;
// Find item with the given item_id
2016-12-04 03:30:21 -05:00
ret = queue_fetch_byitemid ( item_id , & queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
DPRINTF ( E_DBG , L_DB , " Move by pos: base item (id=%d, pos=%d, file-id=%d) \n " , queue_item . id , queue_item . pos , queue_item . file_id ) ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
if ( queue_item . id = = 0 )
2016-11-04 10:34:00 -04:00
{
db_transaction_end ( ) ;
return 0 ;
}
// Calculate the position of the item to move
if ( shuffle )
pos_move_from = queue_item . shuffle_pos + from_pos ;
else
pos_move_from = queue_item . pos + from_pos ;
// Calculate the position where to move the item to
if ( shuffle )
pos_move_to = queue_item . shuffle_pos + to_offset ;
else
pos_move_to = queue_item . pos + to_offset ;
if ( pos_move_to < pos_move_from )
{
/*
* Moving an item to a previous position seems to send an offset incremented by one
*/
pos_move_to + + ;
}
DPRINTF ( E_DBG , L_DB , " Move by pos: absolute pos: move from %d to %d \n " , pos_move_from , pos_move_to ) ;
// Find item to move
2016-12-04 03:30:21 -05:00
ret = queue_fetch_bypos ( pos_move_from , shuffle , & queue_item , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
DPRINTF ( E_DBG , L_DB , " Move by pos: move item (id=%d, pos=%d, file-id=%d) \n " , queue_item . id , queue_item . pos , queue_item . file_id ) ;
2016-11-04 10:34:00 -04:00
2016-12-03 02:03:19 -05:00
if ( queue_item . id = = 0 )
2016-11-04 10:34:00 -04:00
{
db_transaction_end ( ) ;
return 0 ;
}
// Update pos for all items after the item with given position
if ( shuffle )
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = shuffle_pos - 1, queue_version = %d WHERE shuffle_pos > %d; " , queue_version , queue_item . shuffle_pos ) ;
2016-11-04 10:34:00 -04:00
else
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos - 1, queue_version = %d WHERE pos > %d; " , queue_version , queue_item . pos ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update pos for all items from the given pos_to
if ( shuffle )
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = shuffle_pos + 1, queue_version = %d WHERE shuffle_pos >= %d; " , queue_version , pos_move_to ) ;
2016-11-04 10:34:00 -04:00
else
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = pos + 1, queue_version = %d WHERE pos >= %d; " , queue_version , pos_move_to ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
2017-11-25 11:57:35 -05:00
goto end_transaction ;
2016-11-04 10:34:00 -04:00
// Update item with the given item_id
if ( shuffle )
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = %d, queue_version = %d where id = %d; " , pos_move_to , queue_version , queue_item . id ) ;
2016-11-04 10:34:00 -04:00
else
2017-11-25 11:57:35 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET pos = %d, queue_version = %d where id = %d; " , pos_move_to , queue_version , queue_item . id ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
2017-11-25 11:57:35 -05:00
end_transaction :
queue_transaction_end ( ret , queue_version ) ;
2016-11-04 10:34:00 -04:00
2017-11-25 11:57:35 -05:00
return ret ;
2016-11-04 10:34:00 -04:00
}
/*
* Reshuffles the shuffle queue
*
* If the given item_id is 0 , the whole shuffle queue is reshuffled , otherwise the
* queue is reshuffled after the item with the given id ( excluding this item ) .
*
* @ param item_id The base item , after this item the queue is reshuffled
* @ return 0 on success , - 1 on failure
*/
2017-11-25 11:57:35 -05:00
static int
queue_reshuffle ( uint32_t item_id , int queue_version )
2016-11-04 10:34:00 -04:00
{
char * query ;
int pos ;
2019-05-28 17:40:41 -04:00
uint32_t count ;
2016-11-04 10:34:00 -04:00
struct db_queue_item queue_item ;
int * shuffle_pos ;
int len ;
int i ;
2017-04-16 17:54:07 -04:00
struct query_params qp ;
2016-11-04 10:34:00 -04:00
int ret ;
DPRINTF ( E_DBG , L_DB , " Reshuffle queue after item with item-id: %d \n " , item_id ) ;
2017-11-25 11:57:35 -05:00
// Reset the shuffled order and mark all items as changed
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = pos, queue_version = %d; " , queue_version ) ;
ret = db_query_run ( query , 1 , 0 ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2019-05-28 17:40:41 -04:00
return - 1 ;
2016-11-04 10:34:00 -04:00
pos = 0 ;
if ( item_id > 0 )
{
pos = db_queue_get_pos ( item_id , 0 ) ;
if ( pos < 0 )
2019-05-28 17:40:41 -04:00
return - 1 ;
2016-11-04 10:34:00 -04:00
pos + + ; // Do not reshuffle the base item
}
2019-05-28 17:40:41 -04:00
ret = db_queue_get_count ( & count ) ;
if ( ret < 0 )
return - 1 ;
2016-11-04 10:34:00 -04:00
len = count - pos ;
2019-05-28 17:40:41 -04:00
DPRINTF ( E_DBG , L_DB , " Reshuffle %d items off % " PRIu32 " total items, starting from pos %d \n " , len , count , pos ) ;
2016-11-04 10:34:00 -04:00
shuffle_pos = malloc ( len * sizeof ( int ) ) ;
for ( i = 0 ; i < len ; i + + )
{
shuffle_pos [ i ] = i + pos ;
}
shuffle_int ( & shuffle_rng , shuffle_pos , len ) ;
2017-04-16 17:54:07 -04:00
memset ( & qp , 0 , sizeof ( struct query_params ) ) ;
qp . filter = sqlite3_mprintf ( " pos >= %d " , pos ) ;
2016-11-04 10:34:00 -04:00
2017-04-16 17:54:07 -04:00
ret = queue_enum_start ( & qp ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
{
2017-04-16 17:54:07 -04:00
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
return - 1 ;
}
i = 0 ;
2017-04-16 17:54:07 -04:00
while ( ( ret = queue_enum_fetch ( & qp , & queue_item , 0 ) ) = = 0 & & ( queue_item . id > 0 ) & & ( i < len ) )
2016-11-04 10:34:00 -04:00
{
2016-12-03 02:03:19 -05:00
query = sqlite3_mprintf ( " UPDATE queue SET shuffle_pos = %d where id = %d; " , shuffle_pos [ i ] , queue_item . id ) ;
2016-11-04 10:34:00 -04:00
ret = db_query_run ( query , 1 , 0 ) ;
if ( ret < 0 )
{
2016-12-03 02:03:19 -05:00
DPRINTF ( E_LOG , L_DB , " Failed to delete item with item-id: %d \n " , queue_item . id ) ;
2016-11-04 10:34:00 -04:00
break ;
}
i + + ;
}
2017-04-16 17:54:07 -04:00
db_query_end ( & qp ) ;
sqlite3_free ( qp . filter ) ;
2016-11-04 10:34:00 -04:00
if ( ret < 0 )
2019-05-28 17:40:41 -04:00
return - 1 ;
2016-11-04 10:34:00 -04:00
return 0 ;
}
2017-11-25 11:57:35 -05:00
/*
* Reshuffles the shuffle queue
*
* If the given item_id is 0 , the whole shuffle queue is reshuffled , otherwise the
* queue is reshuffled after the item with the given id ( excluding this item ) .
*
* @ param item_id The base item , after this item the queue is reshuffled
* @ return 0 on success , - 1 on failure
*/
int
db_queue_reshuffle ( uint32_t item_id )
{
int queue_version ;
int ret ;
queue_version = queue_transaction_begin ( ) ;
ret = queue_reshuffle ( item_id , queue_version ) ;
queue_transaction_end ( ret , queue_version ) ;
return ret ;
}
2018-11-05 15:35:17 -05:00
/*
* Increment queue version ( triggers queue change event )
*/
int
db_queue_inc_version ( )
{
int queue_version ;
queue_version = queue_transaction_begin ( ) ;
queue_transaction_end ( 0 , queue_version ) ;
return 0 ;
}
2016-11-04 10:34:00 -04:00
int
2019-05-28 17:40:41 -04:00
db_queue_get_count ( uint32_t * nitems )
2016-11-04 10:34:00 -04:00
{
2019-05-28 17:40:41 -04:00
int ret = db_get_one_int ( " SELECT COUNT(*) FROM queue; " ) ;
if ( ret < 0 )
return - 1 ;
* nitems = ( uint32_t ) ret ;
return 0 ;
2016-11-04 10:34:00 -04:00
}
/* Inotify */
int
db_watch_clear ( void )
{
return db_query_run ( " DELETE FROM inotify; " , 0 , 0 ) ;
}
int
db_watch_add ( struct watch_info * wi )
{
# define Q_TMPL "INSERT INTO inotify (wd, cookie, path) VALUES (%d, 0, '%q');"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , wi - > wd , wi - > path ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
int
db_watch_delete_bywd ( uint32_t wd )
{
# define Q_TMPL "DELETE FROM inotify WHERE wd = %d;"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , wd ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
int
db_watch_delete_bypath ( char * path )
{
# define Q_TMPL "DELETE FROM inotify WHERE path = '%q';"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
int
db_watch_delete_bymatch ( char * path )
{
# define Q_TMPL "DELETE FROM inotify WHERE path LIKE '%q / %%';"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , path ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
int
db_watch_delete_bycookie ( uint32_t cookie )
{
# define Q_TMPL "DELETE FROM inotify WHERE cookie = %" PRIi64 ";"
char * query ;
if ( cookie = = 0 )
return - 1 ;
query = sqlite3_mprintf ( Q_TMPL , ( int64_t ) cookie ) ;
return db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
static int
db_watch_get_byquery ( struct watch_info * wi , char * query )
{
sqlite3_stmt * stmt ;
char * * strval ;
char * cval ;
uint32_t * ival ;
int64_t cookie ;
int ncols ;
int i ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 = db_blocking_step ( stmt ) ;
if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_WARN , L_DB , " Watch not found: '%s' \n " , query ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ncols = sqlite3_column_count ( stmt ) ;
2018-09-02 17:38:46 -04:00
if ( ncols < ARRAY_SIZE ( wi_cols_map ) )
2016-11-04 10:34:00 -04:00
{
2018-09-04 14:07:05 -04:00
DPRINTF ( E_LOG , L_DB , " BUG: database has fewer columns (%d) than wi column map (%u) \n " , ncols , ARRAY_SIZE ( wi_cols_map ) ) ;
2016-11-04 10:34:00 -04:00
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
2018-09-02 17:38:46 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( wi_cols_map ) ; i + + )
2016-11-04 10:34:00 -04:00
{
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 ;
}
}
# ifdef DB_PROFILE
while ( db_blocking_step ( stmt ) = = SQLITE_ROW )
; /* EMPTY */
# endif
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
}
int
db_watch_get_bywd ( struct watch_info * wi )
{
# define Q_TMPL "SELECT * FROM inotify WHERE wd = %d;"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , wi - > wd ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
return db_watch_get_byquery ( wi , query ) ;
# undef Q_TMPL
}
int
db_watch_get_bypath ( struct watch_info * wi )
{
# define Q_TMPL "SELECT * FROM inotify WHERE path = '%q';"
char * query ;
query = sqlite3_mprintf ( Q_TMPL , wi - > path ) ;
if ( ! query )
{
DPRINTF ( E_LOG , L_DB , " Out of memory for query string \n " ) ;
return - 1 ;
}
return db_watch_get_byquery ( wi , query ) ;
# undef Q_TMPL
}
void
2017-12-16 17:09:29 -05:00
db_watch_mark_bypath ( char * path , enum strip_type strip , uint32_t cookie )
2016-11-04 10:34:00 -04:00
{
# define Q_TMPL "UPDATE inotify SET path = substr(path, %d), cookie = %" PRIi64 " WHERE path = '%q';"
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
2016-11-04 10:34:00 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , disabled , path ) ;
2016-11-04 10:34:00 -04:00
db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
void
2017-12-16 17:09:29 -05:00
db_watch_mark_bymatch ( char * path , enum strip_type strip , uint32_t cookie )
2016-11-04 10:34:00 -04:00
{
# define Q_TMPL "UPDATE inotify SET path = substr(path, %d), cookie = %" PRIi64 " WHERE path LIKE '%q / %%';"
char * query ;
int64_t disabled ;
2017-12-16 17:09:29 -05:00
int path_striplen ;
2016-11-04 10:34:00 -04:00
disabled = ( cookie ! = 0 ) ? cookie : INOTIFY_FAKE_COOKIE ;
2017-12-16 17:09:29 -05:00
path_striplen = ( strip = = STRIP_PATH ) ? strlen ( path ) : 0 ;
query = sqlite3_mprintf ( Q_TMPL , path_striplen + 1 , disabled , path ) ;
2016-11-04 10:34:00 -04:00
db_query_run ( query , 1 , 0 ) ;
# undef Q_TMPL
}
void
2009-06-11 09:42:51 -04:00
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 ;
if ( cookie = = 0 )
return ;
query = sqlite3_mprintf ( Q_TMPL , path , ( int64_t ) cookie ) ;
2014-08-22 16:51:13 -04:00
db_query_run ( query , 1 , 0 ) ;
2009-06-11 09:42:51 -04:00
# 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 ;
}
2016-12-09 10:20:03 -05:00
ret = db_get_one_int ( 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 ";"
2017-10-14 14:16:34 -04:00
sqlite3_stmt * stmt ;
2009-06-11 10:44:49 -04:00
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 ) ;
2017-10-14 14:16:34 -04:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2009-06-11 10:44:49 -04:00
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 ) ;
2017-10-14 14:16:34 -04:00
we - > stmt = stmt ;
2009-06-11 10:44:49 -04:00
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 ;
}
2010-04-26 12:24:09 -04:00
ret = db_blocking_step ( we - > stmt ) ;
2009-06-11 10:44:49 -04:00
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-09-29 12:49:10 -04:00
# ifdef DB_PROFILE
2018-01-13 04:48:44 -05:00
static int
db_xprofile ( unsigned int trace_type , void * notused , void * ptr , void * ptr_data )
2010-09-29 12:49:10 -04:00
{
2018-01-13 04:48:44 -05:00
sqlite3_stmt * pstmt ;
int64_t ms = 0 ;
2011-06-11 10:59:25 -04:00
sqlite3_stmt * stmt ;
2018-01-13 04:48:44 -05:00
const char * pquery ;
2011-06-11 10:59:25 -04:00
char * query ;
2018-01-13 04:48:44 -05:00
int log_level ;
2011-06-11 10:59:25 -04:00
int ret ;
2018-01-13 04:48:44 -05:00
if ( trace_type ! = SQLITE_TRACE_PROFILE )
return 0 ;
pstmt = ptr ;
pquery = sqlite3_sql ( pstmt ) ;
ms = * ( ( int64_t * ) ptr_data ) / 1000000 ;
if ( ms > 1000 )
log_level = E_LOG ;
else if ( ms > 500 )
log_level = E_WARN ;
else if ( ms > 10 )
log_level = E_DBG ;
else
log_level = E_SPAM ;
if ( log_level > logger_severity ( ) )
return 0 ;
DPRINTF ( log_level , L_DBPERF , " SQL PROFILE query: %s \n " , pquery ) ;
DPRINTF ( log_level , L_DBPERF , " SQL PROFILE time: % " PRIi64 " ms \n " , ms ) ;
2011-06-11 10:59:25 -04:00
if ( ( strncmp ( pquery , " SELECT " , 6 ) ! = 0 )
& & ( strncmp ( pquery , " UPDATE " , 6 ) ! = 0 )
& & ( strncmp ( pquery , " DELETE " , 6 ) ! = 0 ) )
2018-01-13 04:48:44 -05:00
return 0 ;
2011-06-11 10:59:25 -04:00
/* Disable profiling callback */
2018-01-13 04:48:44 -05:00
sqlite3_trace_v2 ( hdl , 0 , NULL , NULL ) ;
2011-06-11 10:59:25 -04:00
query = sqlite3_mprintf ( " EXPLAIN QUERY PLAN %s " , pquery ) ;
if ( ! query )
{
2018-01-13 04:48:44 -05:00
DPRINTF ( log_level , L_DBPERF , " Query plan: Out of memory \n " ) ;
2011-06-11 10:59:25 -04:00
goto out ;
}
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
sqlite3_free ( query ) ;
if ( ret ! = SQLITE_OK )
{
2018-01-13 04:48:44 -05:00
DPRINTF ( log_level , L_DBPERF , " Query plan: Could not prepare statement: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2011-06-11 10:59:25 -04:00
goto out ;
}
2018-01-13 04:48:44 -05:00
DPRINTF ( log_level , L_DBPERF , " Query plan: \n " ) ;
2011-06-11 10:59:25 -04:00
while ( ( ret = db_blocking_step ( stmt ) ) = = SQLITE_ROW )
{
2018-01-13 04:48:44 -05:00
DPRINTF ( log_level , L_DBPERF , " (%d,%d,%d) %s \n " ,
2011-06-11 10:59:25 -04:00
sqlite3_column_int ( stmt , 0 ) , sqlite3_column_int ( stmt , 1 ) , sqlite3_column_int ( stmt , 2 ) ,
sqlite3_column_text ( stmt , 3 ) ) ;
}
if ( ret ! = SQLITE_DONE )
2018-01-13 04:48:44 -05:00
DPRINTF ( log_level , L_DBPERF , " Query plan: Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2011-06-11 10:59:25 -04:00
sqlite3_finalize ( stmt ) ;
out :
/* Reenable profiling callback */
2018-01-13 04:48:44 -05:00
sqlite3_trace_v2 ( hdl , SQLITE_TRACE_PROFILE , db_xprofile , NULL ) ;
return 0 ;
2010-09-29 12:49:10 -04:00
}
# endif
2014-06-08 02:25:58 -04:00
static int
db_pragma_get_cache_size ( )
{
sqlite3_stmt * stmt ;
char * query = " PRAGMA cache_size; " ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 = db_blocking_step ( stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
sqlite3_free ( query ) ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
return ret ;
}
static int
db_pragma_set_cache_size ( int pages )
{
# define Q_TMPL "PRAGMA cache_size=%d;"
sqlite3_stmt * stmt ;
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , pages ) ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 ;
}
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
2014-07-27 05:16:00 -04:00
# undef Q_TMPL
2014-06-08 02:25:58 -04:00
}
static char *
db_pragma_set_journal_mode ( char * mode )
{
# define Q_TMPL "PRAGMA journal_mode=%s;"
sqlite3_stmt * stmt ;
char * query ;
int ret ;
char * new_mode ;
query = sqlite3_mprintf ( Q_TMPL , mode ) ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 NULL ;
}
ret = db_blocking_step ( stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
sqlite3_free ( query ) ;
return NULL ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return NULL ;
}
new_mode = ( char * ) sqlite3_column_text ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return new_mode ;
# undef Q_TMPL
}
static int
db_pragma_get_synchronous ( )
{
sqlite3_stmt * stmt ;
char * query = " PRAGMA synchronous; " ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 = db_blocking_step ( stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
sqlite3_free ( query ) ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
return ret ;
}
static int
db_pragma_set_synchronous ( int synchronous )
{
# define Q_TMPL "PRAGMA synchronous=%d;"
sqlite3_stmt * stmt ;
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , synchronous ) ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 ;
}
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
2014-07-27 05:16:00 -04:00
# undef Q_TMPL
2014-06-08 02:25:58 -04:00
}
2010-09-29 12:49:10 -04:00
2015-09-13 03:10:42 -04:00
static int
db_pragma_get_mmap_size ( )
{
sqlite3_stmt * stmt ;
char * query = " PRAGMA mmap_size; " ;
int ret ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 = db_blocking_step ( stmt ) ;
if ( ret = = SQLITE_DONE )
{
DPRINTF ( E_DBG , L_DB , " End of query results \n " ) ;
sqlite3_free ( query ) ;
return 0 ;
}
else if ( ret ! = SQLITE_ROW )
{
DPRINTF ( E_LOG , L_DB , " Could not step: %s \n " , sqlite3_errmsg ( hdl ) ) ;
sqlite3_free ( query ) ;
return - 1 ;
}
ret = sqlite3_column_int ( stmt , 0 ) ;
sqlite3_finalize ( stmt ) ;
return ret ;
}
static int
db_pragma_set_mmap_size ( int mmap_size )
{
# define Q_TMPL "PRAGMA mmap_size=%d;"
sqlite3_stmt * stmt ;
char * query ;
int ret ;
query = sqlite3_mprintf ( Q_TMPL , mmap_size ) ;
DPRINTF ( E_DBG , L_DB , " Running query '%s' \n " , query ) ;
ret = db_blocking_prepare_v2 ( 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 ;
}
sqlite3_finalize ( stmt ) ;
sqlite3_free ( query ) ;
return 0 ;
# undef Q_TMPL
}
2018-09-23 16:24:24 -04:00
static int
db_open ( void )
2009-06-07 12:56:35 -04:00
{
2010-09-28 12:38:10 -04:00
char * errmsg ;
2009-06-07 12:56:35 -04:00
int ret ;
2014-06-08 02:25:58 -04:00
int cache_size ;
char * journal_mode ;
int synchronous ;
2015-09-13 03:10:42 -04:00
int mmap_size ;
2009-06-07 12:56:35 -04:00
2010-03-21 06:33:05 -04:00
ret = sqlite3_open ( db_path , & hdl ) ;
2009-06-07 12:56:35 -04:00
if ( ret ! = SQLITE_OK )
{
2017-10-29 17:07:58 -04:00
DPRINTF ( E_LOG , L_DB , " Could not open '%s': %s \n " , db_path , sqlite3_errmsg ( hdl ) ) ;
2009-06-07 12:56:35 -04:00
sqlite3_close ( hdl ) ;
return - 1 ;
}
2010-09-28 12:38:10 -04:00
ret = sqlite3_enable_load_extension ( hdl , 1 ) ;
2010-01-04 12:00:40 -05:00
if ( ret ! = SQLITE_OK )
{
2010-09-28 12:38:10 -04:00
DPRINTF ( E_LOG , L_DB , " Could not enable extension loading \n " ) ;
sqlite3_close ( hdl ) ;
return - 1 ;
}
errmsg = NULL ;
ret = sqlite3_load_extension ( hdl , PKGLIBDIR " /forked-daapd-sqlext.so " , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
if ( errmsg )
{
DPRINTF ( E_LOG , L_DB , " Could not load SQLite extension: %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
else
DPRINTF ( E_LOG , L_DB , " Could not load SQLite extension: %s \n " , sqlite3_errmsg ( hdl ) ) ;
2010-01-04 12:00:40 -05:00
sqlite3_close ( hdl ) ;
return - 1 ;
}
2010-09-28 12:38:10 -04:00
ret = sqlite3_enable_load_extension ( hdl , 0 ) ;
2010-08-29 04:21:35 -04:00
if ( ret ! = SQLITE_OK )
{
2010-09-28 12:38:10 -04:00
DPRINTF ( E_LOG , L_DB , " Could not disable extension loading \n " ) ;
2010-08-29 04:21:35 -04:00
sqlite3_close ( hdl ) ;
return - 1 ;
}
2010-09-29 12:49:10 -04:00
# ifdef DB_PROFILE
2018-01-13 04:48:44 -05:00
sqlite3_trace_v2 ( hdl , SQLITE_TRACE_PROFILE , db_xprofile , NULL ) ;
2010-09-29 12:49:10 -04:00
# endif
2014-11-10 16:53:08 -05:00
cache_size = cfg_getint ( cfg_getsec ( cfg , " sqlite " ) , " pragma_cache_size_library " ) ;
2014-06-08 02:25:58 -04:00
if ( cache_size > - 1 )
{
db_pragma_set_cache_size ( cache_size ) ;
cache_size = db_pragma_get_cache_size ( ) ;
DPRINTF ( E_DBG , L_DB , " Database cache size in pages: %d \n " , cache_size ) ;
}
2014-11-10 16:53:08 -05:00
journal_mode = cfg_getstr ( cfg_getsec ( cfg , " sqlite " ) , " pragma_journal_mode " ) ;
2014-06-08 02:25:58 -04:00
if ( journal_mode )
{
journal_mode = db_pragma_set_journal_mode ( journal_mode ) ;
DPRINTF ( E_DBG , L_DB , " Database journal mode: %s \n " , journal_mode ) ;
}
2014-11-10 16:53:08 -05:00
synchronous = cfg_getint ( cfg_getsec ( cfg , " sqlite " ) , " pragma_synchronous " ) ;
2014-06-08 02:25:58 -04:00
if ( synchronous > - 1 )
{
db_pragma_set_synchronous ( synchronous ) ;
synchronous = db_pragma_get_synchronous ( ) ;
DPRINTF ( E_DBG , L_DB , " Database synchronous: %d \n " , synchronous ) ;
}
2015-09-13 03:10:42 -04:00
mmap_size = cfg_getint ( cfg_getsec ( cfg , " sqlite " ) , " pragma_mmap_size_library " ) ;
if ( mmap_size > - 1 )
{
db_pragma_set_mmap_size ( mmap_size ) ;
mmap_size = db_pragma_get_mmap_size ( ) ;
DPRINTF ( E_DBG , L_DB , " Database mmap_size: %d \n " , mmap_size ) ;
}
2018-09-23 16:24:24 -04:00
return 0 ;
}
2019-12-27 18:04:43 -05:00
static sqlite3_stmt *
db_statements_prepare_insert ( const struct col_type_map * map , size_t map_size , const char * table )
2018-09-23 16:24:24 -04:00
{
char * query ;
char keystr [ 2048 ] ;
char valstr [ 1024 ] ;
2019-12-27 18:04:43 -05:00
sqlite3_stmt * stmt ;
2018-09-23 16:24:24 -04:00
int ret ;
int i ;
2018-10-06 17:27:54 -04:00
memset ( keystr , 0 , sizeof ( keystr ) ) ;
memset ( valstr , 0 , sizeof ( valstr ) ) ;
2019-12-27 18:04:43 -05:00
for ( i = 0 ; i < map_size ; i + + )
2018-09-23 16:24:24 -04:00
{
2019-12-27 18:04:43 -05:00
if ( map [ i ] . flag & DB_FLAG_NO_BIND )
2018-09-23 16:24:24 -04:00
continue ;
2019-12-27 18:04:43 -05:00
CHECK_ERR ( L_DB , safe_snprintf_cat ( keystr , sizeof ( keystr ) , " %s, " , map [ i ] . name ) ) ;
2018-10-06 17:27:54 -04:00
CHECK_ERR ( L_DB , safe_snprintf_cat ( valstr , sizeof ( valstr ) , " ?, " ) ) ;
2018-09-23 16:24:24 -04:00
}
2018-10-06 17:27:54 -04:00
// Terminate at the ending ", "
* ( strrchr ( keystr , ' , ' ) ) = ' \0 ' ;
* ( strrchr ( valstr , ' , ' ) ) = ' \0 ' ;
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
CHECK_NULL ( L_DB , query = db_mprintf ( " INSERT INTO %s (%s) VALUES (%s); " , table , keystr , valstr ) ) ;
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2018-09-23 16:24:24 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not prepare statement '%s': %s \n " , query , sqlite3_errmsg ( hdl ) ) ;
free ( query ) ;
2019-12-27 18:04:43 -05:00
return NULL ;
2018-09-23 16:24:24 -04:00
}
free ( query ) ;
2019-12-27 18:04:43 -05:00
return stmt ;
}
static sqlite3_stmt *
db_statements_prepare_update ( const struct col_type_map * map , size_t map_size , const char * table )
{
char * query ;
char keystr [ 2048 ] ;
sqlite3_stmt * stmt ;
int ret ;
int i ;
2018-10-06 17:27:54 -04:00
memset ( keystr , 0 , sizeof ( keystr ) ) ;
2019-12-27 18:04:43 -05:00
for ( i = 0 ; i < map_size ; i + + )
2018-09-23 16:24:24 -04:00
{
2019-12-27 18:04:43 -05:00
if ( map [ i ] . flag & DB_FLAG_NO_BIND )
2018-09-23 16:24:24 -04:00
continue ;
2019-12-27 18:04:43 -05:00
if ( map [ i ] . flag & DB_FLAG_NO_ZERO )
CHECK_ERR ( L_DB , safe_snprintf_cat ( keystr , sizeof ( keystr ) , " %s = daap_no_zero(?, %s), " , map [ i ] . name , map [ i ] . name ) ) ;
2018-12-30 15:55:55 -05:00
else
2019-12-27 18:04:43 -05:00
CHECK_ERR ( L_DB , safe_snprintf_cat ( keystr , sizeof ( keystr ) , " %s = ?, " , map [ i ] . name ) ) ;
2018-09-23 16:24:24 -04:00
}
2018-10-06 17:27:54 -04:00
// Terminate at the ending ", "
* ( strrchr ( keystr , ' , ' ) ) = ' \0 ' ;
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
CHECK_NULL ( L_DB , query = db_mprintf ( " UPDATE %s SET %s WHERE %s = ?; " , table , keystr , map [ 0 ] . name ) ) ;
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2018-09-23 16:24:24 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not prepare statement '%s': %s \n " , query , sqlite3_errmsg ( hdl ) ) ;
free ( query ) ;
2019-12-27 18:04:43 -05:00
return NULL ;
2018-09-23 16:24:24 -04:00
}
2019-12-27 18:04:43 -05:00
2018-09-23 16:24:24 -04:00
free ( query ) ;
2019-12-27 18:04:43 -05:00
return stmt ;
}
2018-09-23 16:24:24 -04:00
2019-12-27 18:04:43 -05:00
static sqlite3_stmt *
db_statements_prepare_ping ( const char * table )
{
char * query ;
sqlite3_stmt * stmt ;
int ret ;
CHECK_NULL ( L_DB , query = db_mprintf ( " UPDATE %s SET db_timestamp = ?, disabled = 0 WHERE path = ? AND db_timestamp >= ?; " , table ) ) ;
ret = db_blocking_prepare_v2 ( query , - 1 , & stmt , NULL ) ;
2018-09-23 16:24:24 -04:00
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not prepare statement '%s': %s \n " , query , sqlite3_errmsg ( hdl ) ) ;
free ( query ) ;
2019-12-27 18:04:43 -05:00
return NULL ;
2018-09-23 16:24:24 -04:00
}
free ( query ) ;
2019-12-27 18:04:43 -05:00
return stmt ;
}
static int
db_statements_prepare ( void )
{
db_statements . files_insert = db_statements_prepare_insert ( mfi_cols_map , ARRAY_SIZE ( mfi_cols_map ) , " files " ) ;
db_statements . files_update = db_statements_prepare_update ( mfi_cols_map , ARRAY_SIZE ( mfi_cols_map ) , " files " ) ;
db_statements . files_ping = db_statements_prepare_ping ( " files " ) ;
db_statements . playlists_insert = db_statements_prepare_insert ( pli_cols_map , ARRAY_SIZE ( pli_cols_map ) , " playlists " ) ;
db_statements . playlists_update = db_statements_prepare_update ( pli_cols_map , ARRAY_SIZE ( pli_cols_map ) , " playlists " ) ;
if ( ! db_statements . files_insert | | ! db_statements . files_update | | ! db_statements . files_ping
| | ! db_statements . playlists_insert | | ! db_statements . playlists_update
)
return - 1 ;
2018-09-23 16:24:24 -04:00
return 0 ;
}
int
db_perthread_init ( void )
{
int ret ;
ret = db_open ( ) ;
if ( ret < 0 )
return - 1 ;
ret = db_statements_prepare ( ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_DB , " Could not prepare statements \n " ) ;
sqlite3_close ( hdl ) ;
return - 1 ;
}
2009-06-07 12:56:35 -04:00
return 0 ;
}
void
db_perthread_deinit ( void )
{
sqlite3_stmt * stmt ;
2010-07-17 06:48:11 -04:00
if ( ! hdl )
return ;
2009-06-07 12:56:35 -04:00
/* Tear down anything that's in flight */
while ( ( stmt = sqlite3_next_stmt ( hdl , 0 ) ) )
sqlite3_finalize ( stmt ) ;
sqlite3_close ( hdl ) ;
}
2009-06-08 12:41:59 -04:00
static int
db_check_version ( void )
{
2009-12-26 03:18:56 -05:00
# define Q_VACUUM "VACUUM;"
char * errmsg ;
2015-01-14 14:32:36 -05:00
int db_ver_major ;
int db_ver_minor ;
int db_ver ;
int vacuum ;
2009-06-08 12:41:59 -04:00
int ret ;
2015-01-14 14:32:36 -05:00
vacuum = cfg_getbool ( cfg_getsec ( cfg , " sqlite " ) , " vacuum " ) ;
2009-06-08 12:41:59 -04:00
2017-11-19 15:14:41 -05:00
db_ver_major = db_admin_getint ( DB_ADMIN_SCHEMA_VERSION_MAJOR ) ;
2017-11-19 01:46:28 -05:00
if ( ! db_ver_major )
2017-11-19 15:14:41 -05:00
db_ver_major = db_admin_getint ( DB_ADMIN_SCHEMA_VERSION ) ; // Pre schema v15.1
2015-01-14 14:32:36 -05:00
2017-11-19 01:46:28 -05:00
if ( ! db_ver_major )
2015-01-14 14:32:36 -05:00
return 1 ; // Will create new database
2017-11-19 15:14:41 -05:00
db_ver_minor = db_admin_getint ( DB_ADMIN_SCHEMA_VERSION_MINOR ) ;
2009-06-08 12:41:59 -04:00
2015-01-14 14:32:36 -05:00
db_ver = db_ver_major * 100 + db_ver_minor ;
2018-12-29 18:39:42 -05:00
if ( db_ver_major < 17 )
2009-06-08 12:41:59 -04:00
{
2015-01-14 14:32:36 -05:00
DPRINTF ( E_FATAL , L_DB , " Database schema v%d too old, cannot upgrade \n " , db_ver_major ) ;
2009-06-08 12:41:59 -04:00
return - 1 ;
}
2015-01-14 14:32:36 -05:00
else if ( db_ver_major > SCHEMA_VERSION_MAJOR )
2011-03-15 16:24:39 -04:00
{
2015-01-14 14:32:36 -05:00
DPRINTF ( E_FATAL , L_DB , " Database schema v%d is newer than the supported version \n " , db_ver_major ) ;
2011-03-15 16:24:39 -04:00
return - 1 ;
}
2015-01-14 14:32:36 -05:00
else if ( db_ver < ( SCHEMA_VERSION_MAJOR * 100 + SCHEMA_VERSION_MINOR ) )
2009-06-08 12:41:59 -04:00
{
2015-01-14 14:46:03 -05:00
DPRINTF ( E_LOG , L_DB , " Database schema outdated, upgrading schema v%d.%d -> v%d.%d... \n " ,
2015-01-14 14:32:36 -05:00
db_ver_major , db_ver_minor , SCHEMA_VERSION_MAJOR , SCHEMA_VERSION_MINOR ) ;
2015-02-26 08:09:14 -05:00
ret = sqlite3_exec ( hdl , " BEGIN TRANSACTION; " , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
2009-12-26 03:18:56 -05:00
{
2015-02-26 08:09:14 -05:00
DPRINTF ( E_LOG , L_DB , " DB error while running 'BEGIN TRANSACTION': %s \n " , errmsg ) ;
2014-01-11 17:05:29 -05:00
2015-02-26 08:09:14 -05:00
sqlite3_free ( errmsg ) ;
return - 1 ;
}
2014-12-21 14:41:44 -05:00
2018-12-29 18:27:47 -05:00
// Will drop indices and triggers
2015-12-19 01:51:10 -05:00
ret = db_upgrade ( hdl , db_ver ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_DB , " Database upgrade errored out, rolling back changes ... \n " ) ;
ret = sqlite3_exec ( hdl , " ROLLBACK TRANSACTION; " , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " DB error while running 'ROLLBACK TRANSACTION': %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
return - 1 ;
}
2016-11-04 02:10:36 -04:00
ret = db_init_indices ( hdl ) ;
2015-02-26 08:09:14 -05:00
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_DB , " Database upgrade errored out, rolling back changes ... \n " ) ;
ret = sqlite3_exec ( hdl , " ROLLBACK TRANSACTION; " , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " DB error while running 'ROLLBACK TRANSACTION': %s \n " , errmsg ) ;
2014-12-21 14:41:44 -05:00
2015-02-26 08:09:14 -05:00
sqlite3_free ( errmsg ) ;
}
2014-12-21 14:41:44 -05:00
2015-02-26 08:09:14 -05:00
return - 1 ;
2018-12-29 18:27:47 -05:00
}
ret = db_init_triggers ( hdl ) ;
if ( ret < 0 )
{
DPRINTF ( E_LOG , L_DB , " Database upgrade errored out, rolling back changes ... \n " ) ;
ret = sqlite3_exec ( hdl , " ROLLBACK TRANSACTION; " , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " DB error while running 'ROLLBACK TRANSACTION': %s \n " , errmsg ) ;
sqlite3_free ( errmsg ) ;
}
return - 1 ;
2015-02-26 08:09:14 -05:00
}
2015-01-14 14:37:29 -05:00
2015-02-26 08:09:14 -05:00
ret = sqlite3_exec ( hdl , " COMMIT TRANSACTION; " , NULL , NULL , & errmsg ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_LOG , L_DB , " DB error while running 'COMMIT TRANSACTION': %s \n " , errmsg ) ;
2009-12-26 03:18:56 -05:00
2015-02-26 08:09:14 -05:00
sqlite3_free ( errmsg ) ;
return - 1 ;
2009-12-26 03:18:56 -05:00
}
2015-02-26 08:09:14 -05:00
DPRINTF ( E_LOG , L_DB , " Upgrading schema to v%d.%d completed \n " , SCHEMA_VERSION_MAJOR , SCHEMA_VERSION_MINOR ) ;
2015-01-08 18:28:06 -05:00
2015-02-26 08:09:14 -05:00
vacuum = 1 ;
2015-01-14 14:32:36 -05:00
}
2018-09-02 17:38:46 -04:00
else if ( db_ver_minor > SCHEMA_VERSION_MINOR )
{
DPRINTF ( E_LOG , L_DB , " Future (but compatible) database version detected (v%d.%d) \n " , db_ver_major , db_ver_minor ) ;
}
2009-12-26 03:18:56 -05:00
2015-01-14 14:32:36 -05:00
if ( vacuum )
2009-06-08 12:41:59 -04:00
{
2015-01-14 14:46:03 -05:00
DPRINTF ( E_LOG , L_DB , " Now vacuuming database, this may take some time... \n " ) ;
2015-01-10 16:44:26 -05:00
2015-01-14 14:32:36 -05:00
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 ) ;
2009-06-08 12:41:59 -04:00
2015-01-14 14:32:36 -05:00
sqlite3_free ( errmsg ) ;
return - 1 ;
}
}
2015-01-10 16:44:26 -05:00
2009-06-08 12:41:59 -04:00
return 0 ;
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 )
{
2019-05-28 17:40:41 -04:00
uint32_t files ;
uint32_t pls ;
2009-06-07 12:56:35 -04:00
int ret ;
2018-09-02 17:38:46 -04:00
if ( ARRAY_SIZE ( dbmfi_cols_map ) ! = ARRAY_SIZE ( mfi_cols_map ) )
{
DPRINTF ( E_FATAL , L_DB , " BUG: mfi column maps are not in sync \n " ) ;
return - 1 ;
}
if ( ARRAY_SIZE ( dbpli_cols_map ) ! = ARRAY_SIZE ( pli_cols_map ) )
{
DPRINTF ( E_FATAL , L_DB , " BUG: pli column maps are not in sync \n " ) ;
return - 1 ;
}
2010-03-21 06:33:05 -04:00
db_path = cfg_getstr ( cfg_getsec ( cfg , " general " ) , " db_path " ) ;
2018-08-19 03:45:35 -04:00
db_rating_updates = cfg_getbool ( cfg_getsec ( cfg , " library " ) , " rating_updates " ) ;
2010-03-21 06:33:05 -04:00
2018-09-02 17:38:46 -04:00
DPRINTF ( E_LOG , L_DB , " Configured to use database file '%s' \n " , db_path ) ;
2010-04-26 12:24:09 -04:00
ret = sqlite3_config ( SQLITE_CONFIG_MULTITHREAD ) ;
2010-04-26 12:24:09 -04:00
if ( ret ! = SQLITE_OK )
{
2010-04-26 12:24:09 -04:00
DPRINTF ( E_FATAL , L_DB , " Could not switch SQLite3 to multithread mode \n " ) ;
DPRINTF ( E_FATAL , L_DB , " Check that SQLite3 has been configured for thread-safe operations \n " ) ;
2010-04-26 12:24:09 -04:00
return - 1 ;
}
2010-04-27 13:46:39 -04:00
ret = sqlite3_enable_shared_cache ( 1 ) ;
if ( ret ! = SQLITE_OK )
{
DPRINTF ( E_FATAL , L_DB , " Could not enable SQLite3 shared-cache mode \n " ) ;
return - 1 ;
}
2010-04-26 12:24:09 -04:00
ret = sqlite3_initialize ( ) ;
if ( ret ! = SQLITE_OK )
2009-06-07 12:56:35 -04:00
{
2010-04-26 12:24:09 -04:00
DPRINTF ( E_FATAL , L_DB , " SQLite3 failed to initialize \n " ) ;
2009-06-07 12:56:35 -04:00
return - 1 ;
}
2018-09-23 16:24:24 -04:00
ret = db_open ( ) ;
2009-06-07 12:56:35 -04:00
if ( ret < 0 )
2018-09-23 16:24:24 -04:00
{
DPRINTF ( E_FATAL , L_DB , " Could not open database \n " ) ;
return - 1 ;
}
2009-06-07 12:56:35 -04:00
2009-06-08 12:41:59 -04:00
ret = db_check_version ( ) ;
if ( ret < 0 )
2010-04-27 14:48:46 -04:00
{
DPRINTF ( E_FATAL , L_DB , " Database version check errored out, incompatible database \n " ) ;
db_perthread_deinit ( ) ;
return - 1 ;
}
else if ( ret > 0 )
2009-06-08 12:41:59 -04:00
{
2015-01-14 14:32:36 -05:00
DPRINTF ( E_LOG , L_DB , " Could not check database version, trying DB init \n " ) ;
2010-01-21 11:51:00 -05:00
2016-11-04 02:10:36 -04:00
ret = db_init_tables ( hdl ) ;
2010-01-21 11:51:00 -05:00
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
}
2013-11-30 06:57:38 -05:00
db_set_cfg_names ( ) ;
2019-05-28 17:40:41 -04:00
CHECK_ERR ( L_DB , db_files_get_count ( & files , NULL , NULL ) ) ;
CHECK_ERR ( L_DB , db_pl_get_count ( & pls ) ) ;
2017-11-19 15:14:41 -05:00
db_admin_setint64 ( DB_ADMIN_START_TIME , ( int64_t ) time ( NULL ) ) ;
2009-06-07 12:56:35 -04:00
db_perthread_deinit ( ) ;
2019-05-28 17:40:41 -04:00
DPRINTF ( E_LOG , L_DB , " Database OK with % " PRIu32 " active files and % " PRIu32 " active playlists \n " , files , pls ) ;
2009-06-07 12:56:35 -04:00
2016-11-04 10:34:00 -04:00
rng_init ( & shuffle_rng ) ;
2009-06-07 12:56:35 -04:00
return 0 ;
}
2010-04-26 12:24:09 -04:00
void
db_deinit ( void )
{
sqlite3_shutdown ( ) ;
}