Tables now have versions and can be upgrade by means of update functions.
errmsg = NULL;
r = sqlite3_exec(db,
+ "CREATE TABLE IF NOT EXISTS lms_internal ("
+ "tab TEXT NOT NULL UNIQUE, "
+ "version INTEGER NOT NULL"
+ ")",
+ NULL, NULL, &errmsg);
+ if (r != SQLITE_OK) {
+ fprintf(stderr, "ERROR: could not create 'lms_internal' table: %s\n",
+ errmsg);
+ sqlite3_free(errmsg);
+ return -1;
+ }
+
+ r = sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS files ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"path BLOB NOT NULL UNIQUE, "
if (r != SQLITE_OK) {
fprintf(stderr, "ERROR: could not create 'files' table: %s\n", errmsg);
sqlite3_free(errmsg);
- return -1;
+ return -2;
}
r = sqlite3_exec(db,
fprintf(stderr, "ERROR: could not create 'files_path_idx' index: %s\n",
errmsg);
sqlite3_free(errmsg);
- return -2;
+ return -3;
}
return 0;
}
static int
-_db_create_tables_if_required(sqlite3 *db)
-{
+_db_table_updater_audios_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
int ret;
ret = _db_create(db, "audios",
if (ret != 0)
goto done;
- ret = _db_create(db, "audio_artists",
- "CREATE TABLE IF NOT EXISTS audio_artists ("
- "id INTEGER PRIMARY KEY, "
- "name TEXT UNIQUE"
- ")");
- if (ret != 0)
- goto done;
-
- ret = _db_create(db, "audio_albums",
- "CREATE TABLE IF NOT EXISTS audio_albums ("
- "id INTEGER PRIMARY KEY, "
- "artist_id INTEGER, "
- "name TEXT"
- ")");
- if (ret != 0)
- goto done;
-
- ret = _db_create(db, "audio_genres",
- "CREATE TABLE IF NOT EXISTS audio_genres ("
- "id INTEGER PRIMARY KEY, "
- "name TEXT UNIQUE"
- ")");
- if (ret != 0)
- goto done;
-
ret = _db_create(db, "audios_title_idx",
"CREATE INDEX IF NOT EXISTS "
"audios_title_idx ON audios (title)");
if (ret != 0)
goto done;
+ ret = lms_db_create_trigger_if_not_exists(db,
+ "delete_audios_on_files_deleted "
+ "DELETE ON files FOR EACH ROW BEGIN"
+ " DELETE FROM audios WHERE id = OLD.id; END;");
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_create_trigger_if_not_exists(db,
+ "delete_files_on_audios_deleted "
+ "DELETE ON audios FOR EACH ROW BEGIN"
+ " DELETE FROM files WHERE id = OLD.id; END;");
+
+ done:
+ return ret;
+}
+
+static lms_db_table_updater_t _db_table_updater_audios[] = {
+ _db_table_updater_audios_0
+};
+
+static int
+_db_table_updater_audio_artists_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
+ int ret;
+
+ ret = _db_create(db, "audio_artists",
+ "CREATE TABLE IF NOT EXISTS audio_artists ("
+ "id INTEGER PRIMARY KEY, "
+ "name TEXT UNIQUE"
+ ")");
+ if (ret != 0)
+ goto done;
+
ret = _db_create(db, "audio_artists_name_idx",
"CREATE INDEX IF NOT EXISTS "
"audio_artists_name_idx ON audio_artists (name)");
+
+ done:
+ return ret;
+}
+
+static lms_db_table_updater_t _db_table_updater_audio_artists[] = {
+ _db_table_updater_audio_artists_0
+};
+
+static int
+_db_table_updater_audio_albums_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
+ int ret;
+
+ ret = _db_create(db, "audio_albums",
+ "CREATE TABLE IF NOT EXISTS audio_albums ("
+ "id INTEGER PRIMARY KEY, "
+ "artist_id INTEGER, "
+ "name TEXT"
+ ")");
if (ret != 0)
goto done;
if (ret != 0)
goto done;
- ret = _db_create(db, "audio_genres_name_idx",
- "CREATE INDEX IF NOT EXISTS "
- "audio_albums_name_idx ON audio_albums (name)");
- if (ret != 0)
- goto done;
-
ret = lms_db_create_trigger_if_not_exists(db,
- "delete_audios_on_files_deleted "
- "DELETE ON files FOR EACH ROW BEGIN"
- " DELETE FROM audios WHERE id = OLD.id; END;");
+ "delete_audios_on_albums_deleted "
+ "DELETE ON audio_albums FOR EACH ROW BEGIN"
+ " DELETE FROM audios WHERE album_id = OLD.id; END;");
if (ret != 0)
goto done;
ret = lms_db_create_trigger_if_not_exists(db,
- "delete_files_on_audios_deleted "
- "DELETE ON audios FOR EACH ROW BEGIN"
- " DELETE FROM files WHERE id = OLD.id; END;");
+ "delete_audio_albums_on_artists_deleted "
+ "DELETE ON audio_artists FOR EACH ROW BEGIN"
+ " DELETE FROM audio_albums WHERE artist_id = OLD.id; END;");
+
+ done:
+ return ret;
+}
+
+static lms_db_table_updater_t _db_table_updater_audio_albums[] = {
+ _db_table_updater_audio_albums_0
+};
+
+static int
+_db_table_updater_audio_genres_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
+ int ret;
+
+ ret = _db_create(db, "audio_genres",
+ "CREATE TABLE IF NOT EXISTS audio_genres ("
+ "id INTEGER PRIMARY KEY, "
+ "name TEXT UNIQUE"
+ ")");
if (ret != 0)
goto done;
- ret = lms_db_create_trigger_if_not_exists(db,
- "delete_audios_on_albums_deleted "
- "DELETE ON audio_albums FOR EACH ROW BEGIN"
- " DELETE FROM audios WHERE album_id = OLD.id; END;");
+ ret = _db_create(db, "audio_genres_name_idx",
+ "CREATE INDEX IF NOT EXISTS "
+ "audio_albums_name_idx ON audio_albums (name)");
if (ret != 0)
goto done;
"delete_audios_on_genres_deleted "
"DELETE ON audio_genres FOR EACH ROW BEGIN"
" DELETE FROM audios WHERE genre_id = OLD.id; END;");
+
+ done:
+ return ret;
+}
+
+static lms_db_table_updater_t _db_table_updater_audio_genres[] = {
+ _db_table_updater_audio_genres_0
+};
+
+#define _DB_T_UPDATE(db, name, array) \
+ lms_db_table_update_if_required(db, name, LMS_ARRAY_SIZE(array), array)
+
+static int
+_db_create_tables_if_required(sqlite3 *db)
+{
+ int ret;
+
+ ret = _DB_T_UPDATE(db, "audios", _db_table_updater_audios);
if (ret != 0)
goto done;
- ret = lms_db_create_trigger_if_not_exists(db,
- "delete_audio_albums_on_artists_deleted "
- "DELETE ON audio_artists FOR EACH ROW BEGIN"
- " DELETE FROM audio_albums WHERE artist_id = OLD.id; END;");
+ ret = _DB_T_UPDATE(db, "audio_artists", _db_table_updater_audio_artists);
+ if (ret != 0)
+ goto done;
+
+ ret = _DB_T_UPDATE(db, "audio_albums", _db_table_updater_audio_albums);
+ if (ret != 0)
+ goto done;
+
+ ret = _DB_T_UPDATE(db, "audio_genres", _db_table_updater_audio_genres);
done:
return ret;
}
+#undef _DB_T_UPDATE
+
lms_db_audio_t *
lms_db_audio_new(sqlite3 *db)
{
return -col;
}
}
+
+int
+lms_db_table_version_get(sqlite3 *db, const char *table)
+{
+ int r, version;
+ sqlite3_stmt *stmt;
+
+ stmt = lms_db_compile_stmt(db,
+ "SELECT version FROM lms_internal WHERE tab = ?");
+ if (!stmt)
+ return -1;
+
+ if (lms_db_bind_text(stmt, 1, table, -1) != 0) {
+ version = -1;
+ goto done;
+ }
+
+ r = sqlite3_step(stmt);
+ if (r == SQLITE_DONE)
+ version = 0;
+ else if (r == SQLITE_ROW)
+ version = sqlite3_column_int(stmt, 1);
+ else {
+ version = -1;
+ fprintf(stderr, "ERROR: could not get table '%s' version: %s",
+ table, sqlite3_errmsg(db));
+ }
+
+ done:
+ lms_db_reset_stmt(stmt);
+ lms_db_finalize_stmt(stmt, "table_version_get");
+
+ return version;
+}
+
+int
+lms_db_table_version_set(sqlite3 *db, const char *table, unsigned int version)
+{
+ int r, ret;
+ sqlite3_stmt *stmt;
+
+ stmt = lms_db_compile_stmt(db,
+ "INSERT OR REPLACE INTO lms_internal (tab, version) VALUES (?, ?)");
+ if (!stmt)
+ return -1;
+
+ ret = lms_db_bind_text(stmt, 1, table, -1);
+ if (ret != 0)
+ goto done;
+
+ ret = lms_db_bind_int(stmt, 2, version);
+ if (ret != 0)
+ goto done;
+
+ r = sqlite3_step(stmt);
+ if (r != SQLITE_DONE) {
+ ret = -1;
+ fprintf(stderr, "ERROR: could not set table '%s' version: %s",
+ table, sqlite3_errmsg(db));
+ }
+
+ done:
+ lms_db_reset_stmt(stmt);
+ lms_db_finalize_stmt(stmt, "table_version_set");
+
+ return ret;
+}
+
+int
+lms_db_table_update(sqlite3 *db, const char *table, unsigned int current_version, unsigned int last_version, const lms_db_table_updater_t *updaters)
+{
+ if (current_version == last_version)
+ return 0;
+ else if (current_version > last_version) {
+ fprintf(stderr,
+ "WARNING: current version (%d) of table '%s' is greater than "
+ "last known version (%d), no updates will be made.\n",
+ current_version, table, last_version);
+ return 0;
+ }
+
+ for (; current_version < last_version; current_version++) {
+ int r, is_last_run;
+
+ is_last_run = current_version == (last_version - 1);
+ r = updaters[current_version](db, table, current_version, is_last_run);
+ if (r != 0) {
+ fprintf(stderr,
+ "ERROR: could not update table '%s' from version %d->%d\n",
+ table, current_version, current_version + 1);
+ return r;
+ }
+ lms_db_table_version_set(db, table, current_version + 1);
+ }
+
+ return 0;
+}
+
+int
+lms_db_table_update_if_required(sqlite3 *db, const char *table, unsigned int last_version, lms_db_table_updater_t *updaters)
+{
+ int current_version;
+
+ current_version = lms_db_table_version_get(db, table);
+ if (current_version < 0)
+ return -1;
+ else
+ return lms_db_table_update(db, table, current_version, last_version,
+ updaters);
+}
static lms_db_image_t *_singleton = NULL;
static int
-_db_create_table_if_required(sqlite3 *db)
-{
+_db_table_updater_images_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
char *errmsg;
int r, ret;
return ret;
}
+static lms_db_table_updater_t _db_table_updater_images[] = {
+ _db_table_updater_images_0
+};
+
+
+static int
+_db_create_table_if_required(sqlite3 *db)
+{
+ return lms_db_table_update_if_required(db, "images",
+ LMS_ARRAY_SIZE(_db_table_updater_images),
+ _db_table_updater_images);
+}
+
lms_db_image_t *
lms_db_image_new(sqlite3 *db)
{
int lms_db_bind_double(sqlite3_stmt *stmt, int col, double value) GNUC_NON_NULL(1);
int lms_db_create_trigger_if_not_exists(sqlite3 *db, const char *sql) GNUC_NON_NULL(1, 2);
+int lms_db_table_version_get(sqlite3 *db, const char *table) GNUC_NON_NULL(1, 2);
+int lms_db_table_version_set(sqlite3 *db, const char *table, unsigned int version) GNUC_NON_NULL(1, 2);
+
+typedef int (*lms_db_table_updater_t)(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run);
+
+int lms_db_table_update(sqlite3 *db, const char *table, unsigned int current_version, unsigned int last_version, const lms_db_table_updater_t *updaters) GNUC_NON_NULL(1, 2, 5);
+int lms_db_table_update_if_required(sqlite3 *db, const char *table, unsigned int last_version, lms_db_table_updater_t *updaters) GNUC_NON_NULL(1, 2, 4);
+
#endif /* _LIGHTMEDIASCANNER_DB_PRIVATE_H_ */
static lms_db_video_t *_singleton = NULL;
static int
-_db_create_table_if_required(sqlite3 *db)
-{
+_db_table_updater_videos_0(sqlite3 *db, const char *table, unsigned int current_version, int is_last_run) {
char *errmsg;
int r, ret;
return ret;
}
+static lms_db_table_updater_t _db_table_updater_videos[] = {
+ _db_table_updater_videos_0
+};
+
+
+static int
+_db_create_table_if_required(sqlite3 *db)
+{
+ return lms_db_table_update_if_required(db, "videos",
+ LMS_ARRAY_SIZE(_db_table_updater_videos),
+ _db_table_updater_videos);
+}
+
lms_db_video_t *
lms_db_video_new(sqlite3 *db)
{