Support DB upgrades.
authorGustavo Sverzut Barbieri <barbieri@gmail.com>
Fri, 30 Nov 2007 23:40:57 +0000 (20:40 -0300)
committerGustavo Sverzut Barbieri <barbieri@gmail.com>
Fri, 30 Nov 2007 23:40:57 +0000 (20:40 -0300)
Tables now have versions and can be upgrade by means of update functions.

src/lib/lightmediascanner.c
src/lib/lightmediascanner_db_audio.c
src/lib/lightmediascanner_db_common.c
src/lib/lightmediascanner_db_image.c
src/lib/lightmediascanner_db_private.h
src/lib/lightmediascanner_db_video.c

index 8d1c7c6..745b2a8 100644 (file)
@@ -199,6 +199,19 @@ _db_create_tables_if_required(sqlite3 *db)
 
     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, "
@@ -209,7 +222,7 @@ _db_create_tables_if_required(sqlite3 *db)
     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,
@@ -221,7 +234,7 @@ _db_create_tables_if_required(sqlite3 *db)
         fprintf(stderr, "ERROR: could not create 'files_path_idx' index: %s\n",
                 errmsg);
         sqlite3_free(errmsg);
-        return -2;
+        return -3;
     }
 
     return 0;
index f704472..501b636 100644 (file)
@@ -35,8 +35,7 @@ _db_create(sqlite3 *db, const char *name, const char *sql)
 }
 
 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",
@@ -51,31 +50,6 @@ _db_create_tables_if_required(sqlite3 *db)
     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)");
@@ -94,9 +68,60 @@ _db_create_tables_if_required(sqlite3 *db)
     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;
 
@@ -112,30 +137,41 @@ _db_create_tables_if_required(sqlite3 *db)
     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;
 
@@ -143,18 +179,43 @@ _db_create_tables_if_required(sqlite3 *db)
         "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)
 {
index 580da94..444ea0a 100644 (file)
@@ -245,3 +245,113 @@ lms_db_bind_double(sqlite3_stmt *stmt, int col, double value)
         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);
+}
index c4c1ce3..95ff20c 100644 (file)
@@ -13,8 +13,7 @@ struct lms_db_image {
 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;
 
@@ -69,6 +68,19 @@ _db_create_table_if_required(sqlite3 *db)
     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)
 {
index e6eed13..99cc8da 100644 (file)
@@ -49,4 +49,12 @@ int lms_db_bind_int(sqlite3_stmt *stmt, int col, int value) GNUC_NON_NULL(1);
 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_ */
index 0bca4fa..b781bab 100644 (file)
@@ -13,8 +13,7 @@ struct lms_db_video {
 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;
 
@@ -48,6 +47,19 @@ _db_create_table_if_required(sqlite3 *db)
     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)
 {