[db] Move calculation of artist/album id's from sqlite to code
The purpose of this is to support library backends making their own calculation of these id's, which is relevant if they have more information available than just album_artist and album. This also removes a bunch of sqlite extension code plus some triggers, which in itself is probably an improvement.
This commit is contained in:
parent
c36df4fb8e
commit
d56215f4e6
186
sqlext/sqlext.c
186
sqlext/sqlext.c
|
@ -31,183 +31,6 @@
|
|||
#include <sqlite3ext.h>
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
|
||||
/*
|
||||
* MurmurHash2, 64-bit versions, by Austin Appleby
|
||||
*
|
||||
* Code released under the public domain, as per
|
||||
* <http://murmurhash.googlepages.com/>
|
||||
* as of 2010-01-03.
|
||||
*/
|
||||
|
||||
#if SIZEOF_VOID_P == 8 /* 64bit platforms */
|
||||
|
||||
static uint64_t
|
||||
murmur_hash64(const void *key, int len, uint32_t seed)
|
||||
{
|
||||
const int r = 47;
|
||||
const uint64_t m = 0xc6a4a7935bd1e995;
|
||||
|
||||
const uint64_t *data;
|
||||
const uint64_t *end;
|
||||
const unsigned char *data_tail;
|
||||
uint64_t h;
|
||||
uint64_t k;
|
||||
|
||||
h = seed ^ (len * m);
|
||||
data = (const uint64_t *)key;
|
||||
end = data + (len / 8);
|
||||
|
||||
while (data != end)
|
||||
{
|
||||
k = *data++;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
data_tail = (const unsigned char *)data;
|
||||
|
||||
switch (len & 7)
|
||||
{
|
||||
case 7:
|
||||
h ^= (uint64_t)(data_tail[6]) << 48;
|
||||
case 6:
|
||||
h ^= (uint64_t)(data_tail[5]) << 40;
|
||||
case 5:
|
||||
h ^= (uint64_t)(data_tail[4]) << 32;
|
||||
case 4:
|
||||
h ^= (uint64_t)(data_tail[3]) << 24;
|
||||
case 3:
|
||||
h ^= (uint64_t)(data_tail[2]) << 16;
|
||||
case 2:
|
||||
h ^= (uint64_t)(data_tail[1]) << 8;
|
||||
case 1:
|
||||
h ^= (uint64_t)(data_tail[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#elif SIZEOF_VOID_P == 4 /* 32bit platforms */
|
||||
|
||||
static uint64_t
|
||||
murmur_hash64(const void *key, int len, uint32_t seed)
|
||||
{
|
||||
const int r = 24;
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
|
||||
const uint32_t *data;
|
||||
const unsigned char *data_tail;
|
||||
uint32_t k1;
|
||||
uint32_t h1;
|
||||
uint32_t k2;
|
||||
uint32_t h2;
|
||||
|
||||
uint64_t h;
|
||||
|
||||
h1 = seed ^ len;
|
||||
h2 = 0;
|
||||
|
||||
data = (const uint32_t *)key;
|
||||
|
||||
while (len >= 8)
|
||||
{
|
||||
k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
|
||||
k2 = *data++;
|
||||
k2 *= m; k2 ^= k2 >> r; k2 *= m;
|
||||
h2 *= m; h2 ^= k2;
|
||||
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
if (len >= 4)
|
||||
{
|
||||
k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
data_tail = (const unsigned char *)data;
|
||||
|
||||
switch(len)
|
||||
{
|
||||
case 3:
|
||||
h2 ^= (uint32_t)(data_tail[2]) << 16;
|
||||
case 2:
|
||||
h2 ^= (uint32_t)(data_tail[1]) << 8;
|
||||
case 1:
|
||||
h2 ^= (uint32_t)(data_tail[0]);
|
||||
h2 *= m;
|
||||
};
|
||||
|
||||
h1 ^= h2 >> 18; h1 *= m;
|
||||
h2 ^= h1 >> 22; h2 *= m;
|
||||
h1 ^= h2 >> 17; h1 *= m;
|
||||
h2 ^= h1 >> 19; h2 *= m;
|
||||
|
||||
h = h1;
|
||||
h = (h << 32) | h2;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#else
|
||||
# error Platform not supported
|
||||
#endif
|
||||
|
||||
static void
|
||||
sqlext_daap_songalbumid_xfunc(sqlite3_context *pv, int n, sqlite3_value **ppv)
|
||||
{
|
||||
const char *album_artist;
|
||||
const char *album;
|
||||
char *hashbuf;
|
||||
sqlite3_int64 result;
|
||||
|
||||
if (n != 2)
|
||||
{
|
||||
sqlite3_result_error(pv, "daap_songalbumid() requires 2 parameters, album_artist and album", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sqlite3_value_type(ppv[0]) != SQLITE_TEXT)
|
||||
|| (sqlite3_value_type(ppv[1]) != SQLITE_TEXT))
|
||||
{
|
||||
sqlite3_result_error(pv, "daap_songalbumid() requires 2 text parameters", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
album_artist = (const char *)sqlite3_value_text(ppv[0]);
|
||||
album = (const char *)sqlite3_value_text(ppv[1]);
|
||||
|
||||
hashbuf = sqlite3_mprintf("%s==%s", (album_artist) ? album_artist : "", (album) ? album : "");
|
||||
if (!hashbuf)
|
||||
{
|
||||
sqlite3_result_error(pv, "daap_songalbumid() out of memory for hashbuf", -1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Limit hash length to 63 bits, due to signed type in sqlite */
|
||||
result = murmur_hash64(hashbuf, strlen(hashbuf), 0) >> 1;
|
||||
|
||||
sqlite3_free(hashbuf);
|
||||
|
||||
sqlite3_result_int64(pv, result);
|
||||
}
|
||||
|
||||
static void
|
||||
sqlext_daap_no_zero_xfunc(sqlite3_context *pv, int n, sqlite3_value **ppv)
|
||||
{
|
||||
|
@ -278,15 +101,6 @@ sqlite3_extension_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines
|
|||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
int ret;
|
||||
|
||||
ret = sqlite3_create_function(db, "daap_songalbumid", 2, SQLITE_UTF8, NULL, sqlext_daap_songalbumid_xfunc, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
if (pzErrMsg)
|
||||
*pzErrMsg = sqlite3_mprintf("Could not create daap_songalbumid function: %s\n", sqlite3_errmsg(db));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_create_function(db, "daap_no_zero", 2, SQLITE_UTF8, NULL, sqlext_daap_no_zero_xfunc, NULL, NULL);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
|
|
19
src/db.c
19
src/db.c
|
@ -92,6 +92,8 @@ enum fixup_type {
|
|||
DB_FIXUP_COMPOSER_SORT,
|
||||
DB_FIXUP_TIME_ADDED,
|
||||
DB_FIXUP_TIME_MODIFIED,
|
||||
DB_FIXUP_SONGARTISTID,
|
||||
DB_FIXUP_SONGALBUMID,
|
||||
};
|
||||
|
||||
struct db_unlock {
|
||||
|
@ -200,8 +202,8 @@ static const struct col_type_map mfi_cols_map[] =
|
|||
{ "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 },
|
||||
{ "songartistid", mfi_offsetof(songartistid), DB_TYPE_INT64, DB_FIXUP_STANDARD, DB_FLAG_AUTO },
|
||||
{ "songalbumid", mfi_offsetof(songalbumid), DB_TYPE_INT64, DB_FIXUP_STANDARD, DB_FLAG_AUTO },
|
||||
{ "songartistid", mfi_offsetof(songartistid), DB_TYPE_INT64, DB_FIXUP_SONGARTISTID },
|
||||
{ "songalbumid", mfi_offsetof(songalbumid), DB_TYPE_INT64, DB_FIXUP_SONGALBUMID },
|
||||
{ "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 },
|
||||
|
@ -726,7 +728,7 @@ sort_tag_create(char **sort_tag, const char *src_tag)
|
|||
|
||||
if (*sort_tag)
|
||||
{
|
||||
DPRINTF(E_DBG, L_LIB, "Existing sort tag will be normalized: %s\n", *sort_tag);
|
||||
DPRINTF(E_DBG, L_DB, "Existing sort tag will be normalized: %s\n", *sort_tag);
|
||||
o_ptr = u8_normalize(UNINORM_NFD, (uint8_t *)*sort_tag, strlen(*sort_tag) + 1, NULL, &len);
|
||||
free(*sort_tag);
|
||||
*sort_tag = (char *)o_ptr;
|
||||
|
@ -841,7 +843,6 @@ fixup_sanitize(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx)
|
|||
*tag = ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -851,6 +852,16 @@ fixup_defaults(char **tag, enum fixup_type fixup, struct fixup_ctx *ctx)
|
|||
|
||||
switch(fixup)
|
||||
{
|
||||
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;
|
||||
|
||||
case DB_FIXUP_TITLE:
|
||||
if (*tag)
|
||||
break;
|
||||
|
|
|
@ -400,22 +400,6 @@ static const struct db_init_query db_init_index_queries[] =
|
|||
|
||||
/* Triggers must be prefixed with trg_ for db_drop_triggers() to id them */
|
||||
|
||||
#define TRG_FILES_INSERT_SONGIDS \
|
||||
"CREATE TRIGGER trg_files_insert_songids AFTER INSERT ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" UPDATE files SET songartistid = daap_songalbumid(LOWER(NEW.album_artist), ''), " \
|
||||
" songalbumid = daap_songalbumid(LOWER(NEW.album_artist), LOWER(NEW.album))" \
|
||||
" WHERE id = NEW.id;" \
|
||||
" END;"
|
||||
|
||||
#define TRG_FILES_UPDATE_SONGIDS \
|
||||
"CREATE TRIGGER trg_files_update_songids AFTER UPDATE OF album_artist, album ON files FOR EACH ROW" \
|
||||
" BEGIN" \
|
||||
" UPDATE files SET songartistid = daap_songalbumid(LOWER(NEW.album_artist), ''), " \
|
||||
" songalbumid = daap_songalbumid(LOWER(NEW.album_artist), LOWER(NEW.album))" \
|
||||
" WHERE id = NEW.id;" \
|
||||
" END;"
|
||||
|
||||
#define TRG_GROUPS_UPDATE \
|
||||
"CREATE TRIGGER trg_groups_update AFTER UPDATE OF songartistid, songalbumid ON files FOR EACH ROW" \
|
||||
" WHEN (NEW.songartistid != 0 AND NEW.songalbumid != 0)" \
|
||||
|
@ -426,8 +410,6 @@ static const struct db_init_query db_init_index_queries[] =
|
|||
|
||||
static const struct db_init_query db_init_trigger_queries[] =
|
||||
{
|
||||
{ TRG_FILES_INSERT_SONGIDS, "create trigger trg_files_insert_songids" },
|
||||
{ TRG_FILES_UPDATE_SONGIDS, "create trigger trg_files_update_songids" },
|
||||
{ TRG_GROUPS_UPDATE, "create trigger trg_groups_update" },
|
||||
};
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* is a major upgrade. In other words minor version upgrades permit downgrading
|
||||
* forked-daapd after the database was upgraded. */
|
||||
#define SCHEMA_VERSION_MAJOR 20
|
||||
#define SCHEMA_VERSION_MINOR 01
|
||||
#define SCHEMA_VERSION_MINOR 02
|
||||
|
||||
int
|
||||
db_init_indices(sqlite3 *hdl);
|
||||
|
|
|
@ -983,6 +983,15 @@ static const struct db_upgrade_query db_upgrade_v2001_queries[] =
|
|||
{ U_V2001_SCVER_MINOR, "set schema_version_minor to 01" },
|
||||
};
|
||||
|
||||
#define U_V2002_SCVER_MINOR \
|
||||
"UPDATE admin SET value = '02' WHERE key = 'schema_version_minor';"
|
||||
|
||||
// This upgrade removes some triggers (will be done automatically by db_drop...)
|
||||
static const struct db_upgrade_query db_upgrade_v2002_queries[] =
|
||||
{
|
||||
{ U_V2002_SCVER_MINOR, "set schema_version_minor to 02" },
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
db_upgrade(sqlite3 *hdl, int db_ver)
|
||||
|
@ -1124,6 +1133,13 @@ db_upgrade(sqlite3 *hdl, int db_ver)
|
|||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case 2001:
|
||||
ret = db_generic_upgrade(hdl, db_upgrade_v2002_queries, ARRAY_SIZE(db_upgrade_v2002_queries));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
23
src/misc.c
23
src/misc.c
|
@ -747,6 +747,29 @@ djb_hash(const void *data, size_t len)
|
|||
return hash;
|
||||
}
|
||||
|
||||
int64_t
|
||||
two_str_hash(const char *a, const char *b)
|
||||
{
|
||||
char hashbuf[2048];
|
||||
int64_t hash;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = snprintf(hashbuf, sizeof(hashbuf), "%s==%s", (a) ? a : "", (b) ? b : "");
|
||||
if (ret < 0 || ret == sizeof(hashbuf))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MISC, "Buffer too large to calculate hash: '%s==%s'\n", a, b);
|
||||
return 999999; // Stand-in hash...
|
||||
}
|
||||
|
||||
for (i = 0; hashbuf[i]; i++)
|
||||
hashbuf[i] = tolower(hashbuf[i]);
|
||||
|
||||
// Limit hash length to 63 bits, due to signed type in sqlite
|
||||
hash = murmur_hash64(hashbuf, strlen(hashbuf), 0) >> 1;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned char b64_decode_table[256];
|
||||
|
||||
|
|
|
@ -130,6 +130,9 @@ swap_pointers(char **a, char **b);
|
|||
uint32_t
|
||||
djb_hash(const void *data, size_t len);
|
||||
|
||||
int64_t
|
||||
two_str_hash(const char *a, const char *b);
|
||||
|
||||
char *
|
||||
b64_decode(const char *b64);
|
||||
|
||||
|
|
Loading…
Reference in New Issue