Big performance wins

This commit is contained in:
Ron Pedde 2005-03-19 10:02:19 +00:00
parent 95eb9caa99
commit adc26c90b4
8 changed files with 155 additions and 1433 deletions

View File

@ -1,6 +1,12 @@
/*
* $Id$
*
* SQLite database schema
*/
CREATE TABLE songs ( CREATE TABLE songs (
id INTEGER PRIMARY KEY NOT NULL, id INTEGER PRIMARY KEY NOT NULL,
path VARCHAR(4096) NOT NULL, path VARCHAR(4096) UNIQUE NOT NULL,
fname VARCHAR(255) NOT NULL, fname VARCHAR(255) NOT NULL,
title VARCHAR(1024) DEFAULT NULL, title VARCHAR(1024) DEFAULT NULL,
artist VARCHAR(1024) DEFAULT NULL, artist VARCHAR(1024) DEFAULT NULL,
@ -34,12 +40,13 @@ CREATE TABLE songs (
time_played INTEGER DEFAULT 0, time_played INTEGER DEFAULT 0,
db_timestamp INTEGER DEFAULT 0, db_timestamp INTEGER DEFAULT 0,
disabled INTEGER DEFAULT 0, disabled INTEGER DEFAULT 0,
updated INTEGER DEFAULT 0, sample_count INTEGER DEFAULT 0,
force_update INTEGER DEFAULT 0 force_update INTEGER DEFAULT 0
); );
CREATE TABLE config ( CREATE TABLE config (
term VARCHAR(255) NOT NULL, term VARCHAR(255) NOT NULL,
subterm VARCHAR(255) DEFAULT NULL,
value VARCHAR(1024) NOT NULL value VARCHAR(1024) NOT NULL
); );
@ -62,10 +69,10 @@ CREATE TABLE users (
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL password VARCHAR(255) NOT NULL
); );
;CREATE INDEX idx_path on songs(path);
INSERT INTO config VALUES ('version','1'); CREATE INDEX idx_path on songs(path);
INSERT INTO config VALUES ('version','','1');
INSERT INTO users VALUES (1,1,'admin','mt-daapd'); INSERT INTO users VALUES (1,1,'admin','mt-daapd');
INSERT INTO playlists VALUES (1,'Library',1,0,'1'); INSERT INTO playlists VALUES (1,'Library',1,0,'1');

View File

@ -52,11 +52,11 @@ typedef struct tag_db_functions {
int(*dbs_enum_fetch)(DBQUERYINFO *, unsigned char **); int(*dbs_enum_fetch)(DBQUERYINFO *, unsigned char **);
int(*dbs_enum_reset)(DBQUERYINFO *); int(*dbs_enum_reset)(DBQUERYINFO *);
int(*dbs_enum_end)(void); int(*dbs_enum_end)(void);
int(*dbs_get_id)(char *);
int(*dbs_start_scan)(void); int(*dbs_start_scan)(void);
int(*dbs_end_scan)(void); int(*dbs_end_scan)(void);
int(*dbs_get_count)(CountType_t); int(*dbs_get_count)(CountType_t);
MP3FILE*(*dbs_fetch_item)(int); MP3FILE*(*dbs_fetch_item)(int);
MP3FILE*(*dbs_fetch_path)(char *);
void(*dbs_dispose_item)(MP3FILE*); void(*dbs_dispose_item)(MP3FILE*);
}DB_FUNCTIONS; }DB_FUNCTIONS;
@ -75,11 +75,11 @@ DB_FUNCTIONS db_functions[] = {
db_sqlite_enum_fetch, db_sqlite_enum_fetch,
db_sqlite_enum_reset, db_sqlite_enum_reset,
db_sqlite_enum_end, db_sqlite_enum_end,
db_sqlite_get_id,
db_sqlite_start_scan, db_sqlite_start_scan,
db_sqlite_end_scan, db_sqlite_end_scan,
db_sqlite_get_count, db_sqlite_get_count,
db_sqlite_fetch_item, db_sqlite_fetch_item,
db_sqlite_fetch_path,
db_sqlite_dispose_item db_sqlite_dispose_item
}, },
#endif #endif
@ -423,19 +423,12 @@ int db_scanning(void) {
*/ */
int db_add(MP3FILE *pmp3) { int db_add(MP3FILE *pmp3) {
int retval; int retval;
int id;
id=db_get_id(pmp3->path);
db_writelock(); db_writelock();
if(id) { retval=db_current->dbs_add(pmp3);
retval=db_current->dbs_update(pmp3); db_revision_no++;
} else {
retval=db_current->dbs_add(pmp3);
db_revision_no++;
}
db_unlock(); db_unlock();
return retval; return retval;
} }
@ -517,6 +510,16 @@ MP3FILE *db_fetch_item(int id) {
return retval; return retval;
} }
MP3FILE *db_fetch_path(char *path) {
MP3FILE *retval;
db_readlock();
retval=db_current->dbs_fetch_path(path);
db_unlock();
return retval;
}
int db_start_scan(void) { int db_start_scan(void) {
int retval; int retval;
@ -538,33 +541,7 @@ int db_end_scan(void) {
return retval; return retval;
} }
int db_last_modified(char *path) {
int id;
MP3FILE *pmp3;
int retval=0;
id=db_get_id(path);
pmp3=db_fetch_item(id);
if(pmp3) {
retval=pmp3->db_timestamp;
db_dispose_item(pmp3);
}
return retval;
}
int db_get_id(char *path) {
int retval;
db_readlock();
retval=db_current->dbs_get_id(path);
db_unlock();
return retval;
}
void db_dispose_item(MP3FILE *pmp3) { void db_dispose_item(MP3FILE *pmp3) {
return db_current->dbs_dispose_item(pmp3); return db_current->dbs_dispose_item(pmp3);
} }

View File

@ -138,8 +138,12 @@ extern int db_start_scan(void);
extern int db_end_scan(void); extern int db_end_scan(void);
extern int db_exists(char *path); extern int db_exists(char *path);
extern int db_scanning(void); extern int db_scanning(void);
extern int db_last_modified(char *path);
extern MP3FILE *db_fetch_item(int id);
extern MP3FILE *db_fetch_path(char *path);
/* metatag parsing */
extern MetaField_t db_encode_meta(char *meta); extern MetaField_t db_encode_meta(char *meta);
extern int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo); extern int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo);
@ -155,8 +159,6 @@ extern int db_dmap_add_container(char *where, char *tag, int size);
*/ */
extern int db_get_song_count(void); extern int db_get_song_count(void);
extern int db_get_playlist_count(void); extern int db_get_playlist_count(void);
extern MP3FILE *db_fetch_item(int id);
extern int db_get_id(char *path);
extern void db_dispose_item(MP3FILE *pmp3); extern void db_dispose_item(MP3FILE *pmp3);
#endif /* _DB_GENERIC_H_ */ #endif /* _DB_GENERIC_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ static sqlite *db_sqlite_songs; /**< Database that holds the mp3 info */
static pthread_mutex_t db_sqlite_mutex = PTHREAD_MUTEX_INITIALIZER; /**< sqlite not reentrant */ static pthread_mutex_t db_sqlite_mutex = PTHREAD_MUTEX_INITIALIZER; /**< sqlite not reentrant */
static sqlite_vm *db_sqlite_pvm; static sqlite_vm *db_sqlite_pvm;
static int db_sqlite_in_scan=0; static int db_sqlite_in_scan=0;
static int db_sqlite_reload=0;
/* Forwards */ /* Forwards */
int db_sqlite_get_size(DBQUERYINFO *pinfo, char **valarray); int db_sqlite_get_size(DBQUERYINFO *pinfo, char **valarray);
@ -97,9 +98,14 @@ int db_sqlite_open(char *parameters) {
int db_sqlite_init(int reload) { int db_sqlite_init(int reload) {
int err; int err;
char *perr; char *perr;
int items;
if(reload) { items=db_sqlite_get_count(countSongs);
if((reload) || (!items)) {
DPRINTF(E_LOG,L_DB,"Full reload...\n");
/* this may or may not fail, depending if the index is already in place */ /* this may or may not fail, depending if the index is already in place */
db_sqlite_reload=1;
db_sqlite_lock(); db_sqlite_lock();
sqlite_exec(db_sqlite_songs,"DROP INDEX idx_path",NULL,NULL,&perr); sqlite_exec(db_sqlite_songs,"DROP INDEX idx_path",NULL,NULL,&perr);
err=sqlite_exec(db_sqlite_songs,"DELETE FROM songs",NULL,NULL,&perr); err=sqlite_exec(db_sqlite_songs,"DELETE FROM songs",NULL,NULL,&perr);
@ -129,15 +135,20 @@ int db_sqlite_deinit(void) {
*/ */
int db_sqlite_start_scan(void) { int db_sqlite_start_scan(void) {
char *perr; char *perr;
int err;
db_sqlite_lock(); db_sqlite_lock();
err=sqlite_exec(db_sqlite_songs,"UPDATE songs SET updated=0",NULL,NULL,&perr);
if(db_sqlite_reload) {
sqlite_exec(db_sqlite_songs,"PRAGMA synchronous = OFF;",NULL,NULL,&perr);
sqlite_exec(db_sqlite_songs,"BEGIN TRANSACTION;",NULL,NULL,&perr);
} else {
/* if not a full reload, we'll be doing update checks */
sqlite_exec(db_sqlite_songs,"DROP TABLE updated",NULL,NULL,&perr);
sqlite_exec(db_sqlite_songs,"CREATE TEMP TABLE updated (id int)",NULL,NULL,&perr);
}
db_sqlite_unlock(); db_sqlite_unlock();
if(err != SQLITE_OK)
DPRINTF(E_FATAL,L_DB,"db_sqlite_start_scan: %s\n",perr);
db_sqlite_in_scan=1; db_sqlite_in_scan=1;
return 0; return 0;
@ -148,16 +159,32 @@ int db_sqlite_start_scan(void) {
*/ */
int db_sqlite_end_scan(void) { int db_sqlite_end_scan(void) {
char *perr; char *perr;
int err; int err=0;
db_sqlite_lock(); db_sqlite_lock();
sqlite_exec(db_sqlite_songs,"PRAGMA synchronous=NORMAL",NULL,NULL,&perr); if(db_sqlite_reload) {
err=sqlite_exec(db_sqlite_songs,"DELETE FROM songs WHERE updated=0",NULL,NULL,&perr); err=sqlite_exec(db_sqlite_songs,"COMMIT TRANSACTION;",NULL,NULL,&perr);
if(err != SQLITE_OK) {
DPRINTF(E_FATAL,L_DB,"Could not commit insert transactions\n");
}
DPRINTF(E_LOG,L_DB,"Creating path index...\n");
sqlite_exec(db_sqlite_songs,"CREATE INDEX idx_path ON songs(path)",NULL,NULL,&perr);
sqlite_exec(db_sqlite_songs,"PRAGMA synchronous=NORMAL;",NULL,NULL,&perr);
} else {
err=sqlite_exec(db_sqlite_songs,
"DELETE FROM songs WHERE id NOT IN (SELECT id FROM updated)",
NULL,NULL,&perr);
if(err == SQLITE_OK) {
err |= sqlite_exec(db_sqlite_songs,"DROP TABLE updated",NULL,NULL,&perr);
}
}
db_sqlite_unlock(); db_sqlite_unlock();
if(err != SQLITE_OK) if(err != SQLITE_OK)
DPRINTF(E_FATAL,L_DB,"db_sqlite_end_scan: %s\n",perr); DPRINTF(E_FATAL,L_DB,"db_sqlite_end_scan: %s\n",perr);
db_sqlite_reload=0;
db_sqlite_in_scan=0; db_sqlite_in_scan=0;
return 0; return 0;
} }
@ -174,6 +201,7 @@ int db_sqlite_add(MP3FILE *pmp3) {
DPRINTF(E_SPAM,L_DB,"Entering db_sqlite_add\n"); DPRINTF(E_SPAM,L_DB,"Entering db_sqlite_add\n");
if(!pmp3->time_added) if(!pmp3->time_added)
pmp3->time_added = (int)time(NULL); pmp3->time_added = (int)time(NULL);
@ -221,7 +249,7 @@ int db_sqlite_add(MP3FILE *pmp3) {
"%d," // time_played "%d," // time_played
"%d," // db_timestamp "%d," // db_timestamp
"%d," // disabled "%d," // disabled
"1," // updated "%d," // sample_count
"0)", // force_update "0)", // force_update
NULL,NULL, NULL,NULL,
&perr, &perr,
@ -259,10 +287,27 @@ int db_sqlite_add(MP3FILE *pmp3) {
pmp3->db_timestamp, pmp3->db_timestamp,
pmp3->disabled); pmp3->disabled);
db_sqlite_unlock(); db_sqlite_unlock();
if(err == SQLITE_CONSTRAINT) {
/* probably because the path already exists... */
DPRINTF(E_DBG,L_DB,"Could not add mp3 file: %s... updating instead\n",pmp3->path);
return db_sqlite_update(pmp3);
}
if(err != SQLITE_OK) if(err != SQLITE_OK)
DPRINTF(E_FATAL,L_DB,"Error inserting file %s in database: %s\n", DPRINTF(E_FATAL,L_DB,"Error inserting file %s in database: %s\n",
pmp3->fname,perr); pmp3->fname,perr);
if((db_sqlite_in_scan)&&(!db_sqlite_reload)) {
db_sqlite_lock();
err=sqlite_exec(db_sqlite_songs,"INSERT INTO updated VALUES (last_insert_rowid())",
NULL,NULL,&perr);
if(err != SQLITE_OK)
DPRINTF(E_LOG,L_DB,"Error inserting into update table: %s\n",perr);
db_sqlite_unlock();
}
DPRINTF(E_SPAM,L_DB,"Exiting db_sqlite_add\n"); DPRINTF(E_SPAM,L_DB,"Exiting db_sqlite_add\n");
return 0; return 0;
} }
@ -308,7 +353,7 @@ int db_sqlite_update(MP3FILE *pmp3) {
"bpm=%d," // bpm "bpm=%d," // bpm
"compilation=%d," // compilation "compilation=%d," // compilation
"rating=%d," // rating "rating=%d," // rating
"updated=1" // updated "sample_count=%d,"
" WHERE path='%q'", " WHERE path='%q'",
NULL,NULL, NULL,NULL,
&perr, &perr,
@ -337,12 +382,24 @@ int db_sqlite_update(MP3FILE *pmp3) {
pmp3->bpm, pmp3->bpm,
pmp3->compilation, pmp3->compilation,
pmp3->rating, pmp3->rating,
pmp3->sample_count,
pmp3->path); pmp3->path);
db_sqlite_unlock(); db_sqlite_unlock();
if(err != SQLITE_OK) if(err != SQLITE_OK)
DPRINTF(E_FATAL,L_DB,"Error updating file %s in database: %s\n", DPRINTF(E_FATAL,L_DB,"Error updating file %s in database: %s\n",
pmp3->fname,perr); pmp3->fname,perr);
if((db_sqlite_in_scan) && (!db_sqlite_reload)) {
db_sqlite_lock();
err=sqlite_exec_printf(db_sqlite_songs,
"INSERT INTO updated (id) select id from songs where path='%q')",
NULL,NULL,&perr,pmp3->path);
if(err != SQLITE_OK)
DPRINTF(E_LOG,L_DB,"Error inserting into update table: %s\n",perr);
db_sqlite_unlock();
}
return 0; return 0;
} }
@ -876,33 +933,6 @@ int db_sqlite_build_dmap(DBQUERYINFO *pinfo, char **valarray, char *presult, int
return 0; return 0;
} }
/**
* return the id for a given path (or 0 if it does not exist)
*
* \param path path of the file we are looking for
*/
int db_sqlite_get_id(char *path) {
int err, rows, cols;
char *perr;
char **resarray;
int retval=0;
db_sqlite_lock();
err=sqlite_get_table_printf(db_sqlite_songs,"SELECT * FROM songs WHERE path='%q'",
&resarray, &rows, &cols, &perr, path);
db_sqlite_unlock();
if(rows != 0) {
retval=atoi(resarray[cols]);
}
db_sqlite_lock();
sqlite_free_table(resarray);
db_sqlite_unlock();
return retval;
}
int db_sqlite_atoi(const char *what) { int db_sqlite_atoi(const char *what) {
return what ? atoi(what) : 0; return what ? atoi(what) : 0;
} }
@ -948,6 +978,8 @@ void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3) {
pmp3->time_played=db_sqlite_atoi(valarray[32]); pmp3->time_played=db_sqlite_atoi(valarray[32]);
pmp3->db_timestamp=db_sqlite_atoi(valarray[33]); pmp3->db_timestamp=db_sqlite_atoi(valarray[33]);
pmp3->disabled=db_sqlite_atoi(valarray[34]); pmp3->disabled=db_sqlite_atoi(valarray[34]);
pmp3->sample_count=db_sqlite_atoi(valarray[35]);
pmp3->force_update=db_sqlite_atoi(valarray[36]);
} }
/** /**
@ -976,8 +1008,8 @@ MP3FILE *db_sqlite_fetch_item(int id) {
db_sqlite_lock(); db_sqlite_lock();
sqlite_free_table(resarray); sqlite_free_table(resarray);
if(db_sqlite_in_scan) { if((db_sqlite_in_scan) && (!db_sqlite_reload)) {
sqlite_exec_printf(db_sqlite_songs,"UPDATE songs SET updated=1 WHERE id=%d", sqlite_exec_printf(db_sqlite_songs,"insert into updated values (%d)",
NULL,NULL,&perr,id); NULL,NULL,&perr,id);
} }
db_sqlite_unlock(); db_sqlite_unlock();
@ -985,6 +1017,45 @@ MP3FILE *db_sqlite_fetch_item(int id) {
return pmp3; return pmp3;
} }
/**
* retrieve a MP3FILE struct for the song with a give path
*
* \param path path of the file to retreive
*/
extern MP3FILE *db_sqlite_fetch_path(char *path) {
int err,rows,cols;
char *perr;
char **resarray;
MP3FILE *pmp3=NULL;
/* here's a bit of a hack */
if(db_sqlite_reload) {
return NULL;
}
db_sqlite_lock();
err=sqlite_get_table_printf(db_sqlite_songs,"SELECT * FROM songs WHERE path=%q",
&resarray, &rows, &cols, &perr, path);
db_sqlite_unlock();
if(rows != 0) {
pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
if(!pmp3)
DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sqlite_fetch_item\n");
db_sqlite_build_mp3file((char **)&resarray[cols],pmp3);
}
db_sqlite_lock();
sqlite_free_table(resarray);
if((db_sqlite_in_scan)&&(!db_sqlite_reload)) {
sqlite_exec_printf(db_sqlite_songs,"INSERT INTO updated VALUES (%d)",
NULL,NULL,&perr,pmp3->id);
}
db_sqlite_unlock();
return pmp3;
}
/** /**
* dispose of a MP3FILE struct that was obtained * dispose of a MP3FILE struct that was obtained
@ -993,7 +1064,7 @@ MP3FILE *db_sqlite_fetch_item(int id) {
* \param pmp3 item obtained from db_sqlite_fetch_item * \param pmp3 item obtained from db_sqlite_fetch_item
*/ */
void db_sqlite_dispose_item(MP3FILE *pmp3) { void db_sqlite_dispose_item(MP3FILE *pmp3) {
if(pmp3) if(!pmp3)
return; return;
MAYBEFREE(pmp3->path); MAYBEFREE(pmp3->path);

View File

@ -32,11 +32,11 @@ extern int db_sqlite_enum_size(DBQUERYINFO *pinfo, int *count);
extern int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap); extern int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap);
extern int db_sqlite_enum_reset(DBQUERYINFO *pinfo); extern int db_sqlite_enum_reset(DBQUERYINFO *pinfo);
extern int db_sqlite_enum_end(void); extern int db_sqlite_enum_end(void);
extern int db_sqlite_get_id(char *path);
extern int db_sqlite_start_scan(void); extern int db_sqlite_start_scan(void);
extern int db_sqlite_end_scan(void); extern int db_sqlite_end_scan(void);
extern int db_sqlite_get_count(CountType_t type); extern int db_sqlite_get_count(CountType_t type);
extern MP3FILE *db_sqlite_fetch_item(int id); extern MP3FILE *db_sqlite_fetch_item(int id);
extern MP3FILE *db_sqlite_fetch_path(char *path);
extern void db_sqlite_dispose_item(MP3FILE *pmp3); extern void db_sqlite_dispose_item(MP3FILE *pmp3);
#endif /* _DBS_SQLITE_H_ */ #endif /* _DBS_SQLITE_H_ */

View File

@ -383,6 +383,7 @@ int scan_path(char *path) {
struct stat sb; struct stat sb;
int modified_time; int modified_time;
char *ext; char *ext;
MP3FILE *pmp3;
if((current_dir=opendir(path)) == NULL) { if((current_dir=opendir(path)) == NULL) {
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno)); DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
@ -427,18 +428,19 @@ int scan_path(char *path) {
if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) && if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) &&
config.process_m3u){ config.process_m3u){
/* we found an m3u file */ /* we found an m3u file */
DPRINTF(E_LOG,L_SCAN,"Oops... we aren't doing playlists.. Sorry: %s\n", DPRINTF(E_LOG,L_SCAN,"Oops... no playlists.. Sorry: %s\n",
mp3_path); mp3_path);
// scan_static_playlist(path, pde, &sb); // scan_static_playlist(path, pde, &sb);
} else if (((ext = strrchr(pde->d_name, '.')) != NULL) && } else if (((ext = strrchr(pde->d_name, '.')) != NULL) &&
(strcasestr(config.extensions, ext))) { (strcasestr(config.extensions, ext))) {
/* only scan if it's been changed, or empty db */ /* only scan if it's been changed, or empty db */
modified_time=sb.st_mtime; modified_time=sb.st_mtime;
DPRINTF(E_DBG,L_SCAN,"FS Mod time: %d\n",modified_time); pmp3=db_fetch_path(mp3_path);
DPRINTF(E_DBG,L_SCAN,"DB Mod time: %d\n",db_last_modified(mp3_path));
if(!db_get_id(mp3_path) || if((!pmp3) || (pmp3->db_timestamp < modified_time) ||
db_last_modified(mp3_path) < modified_time) { (pmp3->force_update)) {
scan_music_file(path,pde,&sb); scan_music_file(path,pde,&sb);
db_dispose_item(pmp3);
} else { } else {
DPRINTF(E_DBG,L_SCAN,"Skipping file... not modified\n"); DPRINTF(E_DBG,L_SCAN,"Skipping file... not modified\n");
} }

View File

@ -66,7 +66,8 @@ typedef struct tag_mp3file {
char* description; /* long file type */ char* description; /* long file type */
int item_kind; /* song or movie */ int item_kind; /* song or movie */
int data_kind; /* dmap.datakind (asdk) */ int data_kind; /* dmap.datakind (asdk) */
int force_update;
int sample_count;
char compilation; char compilation;
} MP3FILE; } MP3FILE;