Introduce major and minor schema versioning to improve vers compability

- also make database vacuuming on startup optional
This commit is contained in:
ejurgensen 2015-01-14 20:32:36 +01:00
parent 37d990b390
commit d16cf2fe7c
3 changed files with 77 additions and 62 deletions

View File

@ -179,4 +179,8 @@ sqlite {
# Change the setting of the "synchronous" flag # Change the setting of the "synchronous" flag
# 0: OFF, 1: NORMAL, 2: FULL (default) # 0: OFF, 1: NORMAL, 2: FULL (default)
# pragma_synchronous = 2 # pragma_synchronous = 2
# Should the database be vacuumed on startup? (increases startup time,
# but may reduce database size). Default is yes.
# vacuum = yes
} }

View File

@ -122,6 +122,7 @@ static cfg_opt_t sec_sqlite[] =
CFG_INT("pragma_cache_size_cache", -1, CFGF_NONE), CFG_INT("pragma_cache_size_cache", -1, CFGF_NONE),
CFG_STR("pragma_journal_mode", NULL, CFGF_NONE), CFG_STR("pragma_journal_mode", NULL, CFGF_NONE),
CFG_INT("pragma_synchronous", -1, CFGF_NONE), CFG_INT("pragma_synchronous", -1, CFGF_NONE),
CFG_BOOL("vacuum", cfg_true, CFGF_NONE),
CFG_END() CFG_END()
}; };

134
src/db.c
View File

@ -3393,7 +3393,7 @@ db_admin_get(const char *key)
ret = db_blocking_prepare_v2(query, strlen(query) + 1, &stmt, NULL); ret = db_blocking_prepare_v2(query, strlen(query) + 1, &stmt, NULL);
if (ret != SQLITE_OK) if (ret != SQLITE_OK)
{ {
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); DPRINTF(E_WARN, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl));
sqlite3_free(query); sqlite3_free(query);
return NULL; return NULL;
@ -3405,7 +3405,7 @@ db_admin_get(const char *key)
if (ret == SQLITE_DONE) if (ret == SQLITE_DONE)
DPRINTF(E_DBG, L_DB, "No results\n"); DPRINTF(E_DBG, L_DB, "No results\n");
else else
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); DPRINTF(E_WARN, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl));
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
sqlite3_free(query); sqlite3_free(query);
@ -4372,8 +4372,10 @@ db_perthread_deinit(void)
" VALUES(8, 'Purchased', 0, 'media_kind = 1024', 0, '', 0, 8);" " VALUES(8, 'Purchased', 0, 'media_kind = 1024', 0, '', 0, 8);"
*/ */
#define SCHEMA_VERSION 15 #define SCHEMA_VERSION_MAJOR 15
#define Q_SCVER \ #define SCHEMA_VERSION_MINOR 00
// Q_SCVER should be deprecated/removed at v16
#define Q_SCVER \
"INSERT INTO admin (key, value) VALUES ('schema_version', '15');" "INSERT INTO admin (key, value) VALUES ('schema_version', '15');"
struct db_init_query { struct db_init_query {
@ -4402,7 +4404,7 @@ static const struct db_init_query db_init_table_queries[] =
{ Q_PL5, "create default smart playlist 'Podcasts'" }, { Q_PL5, "create default smart playlist 'Podcasts'" },
{ Q_PL6, "create default smart playlist 'Audiobooks'" }, { Q_PL6, "create default smart playlist 'Audiobooks'" },
{ Q_SCVER, "set schema version" }, { Q_SCVER, "set schema version" },
}; };
@ -5409,48 +5411,62 @@ db_upgrade_v15(void)
static int static int
db_check_version(void) db_check_version(void)
{ {
#define Q_VER "SELECT value FROM admin WHERE key = 'schema_version';"
#define Q_VACUUM "VACUUM;" #define Q_VACUUM "VACUUM;"
sqlite3_stmt *stmt; char *buf;
char *errmsg; char *errmsg;
int cur_ver; int db_ver_major;
int db_ver_minor;
int db_ver;
int vacuum;
int ret; int ret;
DPRINTF(E_DBG, L_DB, "Running query '%s'\n", Q_VER); vacuum = cfg_getbool(cfg_getsec(cfg, "sqlite"), "vacuum");
ret = sqlite3_prepare_v2(hdl, Q_VER, strlen(Q_VER) + 1, &stmt, NULL); buf = db_admin_get("schema_version_major");
if (ret != SQLITE_OK) if (!buf)
buf = db_admin_get("schema_version"); // Pre schema v15.1
if (!buf)
return 1; // Will create new database
safe_atoi32(buf, &db_ver_major);
free(buf);
buf = db_admin_get("schema_version_minor");
if (buf)
{ {
DPRINTF(E_LOG, L_DB, "Could not prepare statement: %s\n", sqlite3_errmsg(hdl)); safe_atoi32(buf, &db_ver_minor);
return 1; free(buf);
} }
else
db_ver_minor = 0;
ret = sqlite3_step(stmt); db_ver = db_ver_major * 100 + db_ver_minor;
if (ret != SQLITE_ROW)
if (db_ver_major < 10)
{ {
DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); DPRINTF(E_FATAL, L_DB, "Database schema v%d too old, cannot upgrade\n", db_ver_major);
sqlite3_finalize(stmt);
return -1;
}
cur_ver = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
if (cur_ver < 10)
{
DPRINTF(E_FATAL, L_DB, "Database schema v%d too old, cannot upgrade\n", cur_ver);
return -1; return -1;
} }
else if (cur_ver < SCHEMA_VERSION) else if (db_ver_major > SCHEMA_VERSION_MAJOR)
{ {
DPRINTF(E_LOG, L_DB, "Database schema outdated, schema upgrade needed v%d -> v%d\n", cur_ver, SCHEMA_VERSION); DPRINTF(E_FATAL, L_DB, "Database schema v%d is newer than the supported version\n", db_ver_major);
switch (cur_ver) return -1;
}
else if (db_ver < (SCHEMA_VERSION_MAJOR * 100 + SCHEMA_VERSION_MINOR))
{
DPRINTF(E_LOG, L_DB, "Database schema outdated, schema upgrade needed v%d.%d -> v%d.%d\n",
db_ver_major, db_ver_minor, SCHEMA_VERSION_MAJOR, SCHEMA_VERSION_MINOR);
ret = db_drop_indices();
if (ret < 0)
return -1;
switch (db_ver)
{ {
case 10: case 1000:
ret = db_generic_upgrade(db_upgrade_v11_queries, sizeof(db_upgrade_v11_queries) / sizeof(db_upgrade_v11_queries[0])); ret = db_generic_upgrade(db_upgrade_v11_queries, sizeof(db_upgrade_v11_queries) / sizeof(db_upgrade_v11_queries[0]));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -5461,7 +5477,7 @@ db_check_version(void)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 11: case 1100:
ret = db_upgrade_v12(); ret = db_upgrade_v12();
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -5472,14 +5488,14 @@ db_check_version(void)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 12: case 1200:
ret = db_generic_upgrade(db_upgrade_v13_queries, sizeof(db_upgrade_v13_queries) / sizeof(db_upgrade_v13_queries[0])); ret = db_generic_upgrade(db_upgrade_v13_queries, sizeof(db_upgrade_v13_queries) / sizeof(db_upgrade_v13_queries[0]));
if (ret < 0) if (ret < 0)
return -1; return -1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 13: case 1300:
ret = db_upgrade_v14(); ret = db_upgrade_v14();
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -5490,7 +5506,7 @@ db_check_version(void)
/* FALLTHROUGH */ /* FALLTHROUGH */
case 14: case 1400:
ret = db_upgrade_v15(); ret = db_upgrade_v15();
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -5502,39 +5518,33 @@ db_check_version(void)
break; break;
default: default:
DPRINTF(E_LOG, L_DB, "No upgrade path from DB schema v%d to v%d\n", cur_ver, SCHEMA_VERSION); DPRINTF(E_FATAL, L_DB, "No upgrade path from the current DB schema\n");
return -1; return -1;
} }
vacuum = 1;
ret = db_create_indices();
if (ret < 0)
return -1;
} }
else if (cur_ver > SCHEMA_VERSION)
if (vacuum)
{ {
DPRINTF(E_FATAL, L_DB, "Database schema is newer than the supported version\n"); DPRINTF(E_INFO, L_DB, "Now vacuuming database, this may take some time...\n");
return -1;
ret = sqlite3_exec(hdl, Q_VACUUM, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
DPRINTF(E_LOG, L_DB, "Could not VACUUM database: %s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
} }
/* Drop and create indices on startup so that change of a index can be done without a schema update */
ret = db_drop_indices();
if (ret < 0)
return -1;
DPRINTF(E_INFO, L_DB, "Now vacuuming database, this may take some time...\n");
ret = sqlite3_exec(hdl, Q_VACUUM, NULL, NULL, &errmsg);
if (ret != SQLITE_OK)
{
DPRINTF(E_LOG, L_DB, "Could not VACUUM database: %s\n", errmsg);
sqlite3_free(errmsg);
return -1;
}
ret = db_create_indices();
if (ret < 0)
return -1;
return 0; return 0;
#undef Q_VER
#undef Q_VACUUM #undef Q_VACUUM
} }
@ -5583,7 +5593,7 @@ db_init(void)
} }
else if (ret > 0) else if (ret > 0)
{ {
DPRINTF(E_FATAL, L_DB, "Could not check database version, trying DB init\n"); DPRINTF(E_LOG, L_DB, "Could not check database version, trying DB init\n");
ret = db_create_tables(); ret = db_create_tables();
if (ret < 0) if (ret < 0)