diff --git a/src/database/upgrade.c b/src/database/upgrade.c index bcf946ea5ba67607530b633378581ad224a08d99..ecc9c9f223a5403ea15d345ce691ea35931f7c53 100644 --- a/src/database/upgrade.c +++ b/src/database/upgrade.c @@ -4,68 +4,84 @@ #include <lektor/database.h> #include <lektor/internal/dbmacro.h> +struct sql_field_description { + const char *name; + const LKT_DATABASE_VERSION minimal_version; +}; + struct sql_table_description { const char *name; - const char *fields[LKT_TABLE_FIELDS_MAX]; + const struct sql_field_description fields[LKT_TABLE_FIELDS_MAX]; const LKT_DATABASE_VERSION minimal_version; }; -#define DCL_SQL_TABLE(tbl_name, ...) \ - UNUSED static struct sql_table_description sql_table_##tbl_name = { \ - .name = #tbl_name, \ - .fields = { __VA_ARGS__, NULL }, \ - .minimal_version = LKT_DATABASE_VERSION_ALPHA, \ - }; - -#define DCL_SQL_TABLE_NAMED(var_name, tbl_name, ...) \ - UNUSED static struct sql_table_description sql_table_##var_name = { \ - .name = #tbl_name, \ - .fields = { __VA_ARGS__, NULL }, \ - .minimal_version = LKT_DATABASE_VERSION_ALPHA, \ - }; - -#define DCL_SQL_TABLE_NAMED_WITH_VERSION(var_name, tbl_name, version, ...) \ - UNUSED static struct sql_table_description sql_table_##var_name = { \ - .name = #tbl_name, \ - .fields = { __VA_ARGS__, NULL }, \ - .minimal_version = LKT_DATABASE_VERSION_##version, \ - }; - -DCL_SQL_TABLE(kara, "id", "song_name", "source_name", "category", "song_type", "song_number", - "language", "file_path", "is_new", "author_name", "available"); -DCL_SQL_TABLE(kara_type, "id", "name"); -DCL_SQL_TABLE(kara_category, "id", "name"); -DCL_SQL_TABLE(language, "id", "name"); -DCL_SQL_TABLE(playlist, "id", "name", "last_update"); -DCL_SQL_TABLE(kara_playlist, "kara_id", "playlist_id"); -DCL_SQL_TABLE(queue, "position", "kara_id", "priority"); -DCL_SQL_TABLE(users, "username", "password"); -DCL_SQL_TABLE(updates, "job", "kara_id"); -DCL_SQL_TABLE(misc, "id", "last_update", "last_end_update", "update_job", "opened", "obfuscation"); -DCL_SQL_TABLE_NAMED(stickers, 'stickers', "id", "name") -DCL_SQL_TABLE_NAMED(stickers_kara, 'stickers.kara', "id", "sticker", "value") -DCL_SQL_TABLE_NAMED(stickers_plt, 'stickers.plt', "id", "sticker", "value") - -DCL_SQL_TABLE_NAMED_WITH_VERSION(kara_mk_7_3, kara, MK_7_3, "repo_timestamp"); - -UNUSED static struct sql_table_description *sql_tables_list[] = { - &sql_table_kara, &sql_table_kara_type, - &sql_table_kara_category, &sql_table_language, - &sql_table_playlist, &sql_table_kara_playlist, - &sql_table_queue, &sql_table_users, - &sql_table_updates, &sql_table_misc, - &sql_table_stickers, &sql_table_stickers_kara, - &sql_table_stickers_plt, NULL, +#define DCL_SQL_FIELD(field_name, version) \ + ((struct sql_field_description){ .name = field_name, \ + .minimal_version = (LKT_DATABASE_VERSION_##version) }) + +#define DCL_SQL_FIELD_NULL ((struct sql_field_description){ .name = NULL, .minimal_version = 0 }) + +#define DCL_SQL_TABLE(tbl_name, ...) \ + ((struct sql_table_description){ \ + .name = #tbl_name, \ + .fields = { __VA_ARGS__, DCL_SQL_FIELD_NULL }, \ + .minimal_version = LKT_DATABASE_VERSION_ALPHA, \ + }) + +#define DCL_SQL_TABLE_VERSION(tbl_name, version, ...) \ + ((struct sql_table_description){ \ + .name = #tbl_name, \ + .fields = { __VA_ARGS__, DCL_SQL_FIELD_NULL }, \ + .minimal_version = LKT_DATABASE_VERSION_##version, \ + }) + +static struct sql_table_description sql_tables_list[] = { + DCL_SQL_TABLE(kara, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("song_name", ALPHA), + DCL_SQL_FIELD("source_name", ALPHA), DCL_SQL_FIELD("category", ALPHA), + DCL_SQL_FIELD("song_type", ALPHA), DCL_SQL_FIELD("song_number", ALPHA), + DCL_SQL_FIELD("language", ALPHA), DCL_SQL_FIELD("file_path", ALPHA), + DCL_SQL_FIELD("is_new", ALPHA), DCL_SQL_FIELD("author_name", ALPHA), + DCL_SQL_FIELD("available", ALPHA), DCL_SQL_FIELD("repo_timestamp", MK_7_3)), + + DCL_SQL_TABLE(kara_type, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("name", ALPHA)), + DCL_SQL_TABLE(kara_category, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("name", ALPHA)), + DCL_SQL_TABLE(language, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("name", ALPHA)), + + DCL_SQL_TABLE(playlist, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("name", ALPHA), + DCL_SQL_FIELD("last_update", ALPHA)), + + DCL_SQL_TABLE(kara_playlist, DCL_SQL_FIELD("kara_id", ALPHA), + DCL_SQL_FIELD("playlist_id", ALPHA)), + + DCL_SQL_TABLE(queue, DCL_SQL_FIELD("position", ALPHA), DCL_SQL_FIELD("kara_id", ALPHA), + DCL_SQL_FIELD("priority", ALPHA)), + + DCL_SQL_TABLE(users, DCL_SQL_FIELD("username", ALPHA), DCL_SQL_FIELD("password", ALPHA)), + DCL_SQL_TABLE(updates, DCL_SQL_FIELD("job", ALPHA), DCL_SQL_FIELD("kara_id", ALPHA)), + + DCL_SQL_TABLE(misc, DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("last_update", ALPHA), + DCL_SQL_FIELD("last_end_update", ALPHA), DCL_SQL_FIELD("update_job", ALPHA), + DCL_SQL_FIELD("opened", ALPHA), DCL_SQL_FIELD("obfuscation", ALPHA)), + + DCL_SQL_TABLE('stickers', DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("name", ALPHA)), + + DCL_SQL_TABLE('stickers.kara', DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("sticker", ALPHA), + DCL_SQL_FIELD("value", ALPHA)), + + DCL_SQL_TABLE('stickers.plt', DCL_SQL_FIELD("id", ALPHA), DCL_SQL_FIELD("sticker", ALPHA), + DCL_SQL_FIELD("value", ALPHA)), }; +static size_t sql_tables_list_count = + sizeof(sql_tables_list) / sizeof(struct sql_table_description); CONSTRUCTOR_FUNCTION ___verify_table_descriptions(void) { - for (size_t i = 0; sql_tables_list[i]; ++i) { - struct sql_table_description *table = sql_tables_list[i]; + for (size_t i = 0; i < sql_tables_list_count; ++i) { + struct sql_table_description *table = &sql_tables_list[i]; size_t j = LKT_TABLE_FIELDS_MAX - 1; while (j) { - if (table->fields[j] == NULL) + if (table->fields[j].name == NULL) break; --j; } @@ -78,7 +94,8 @@ ___verify_table_descriptions(void) } PRIVATE_FUNCTION bool -___compute_upgrade_sql_query(const struct sql_table_description *table, char *buffer, +___compute_upgrade_sql_query(LKT_DATABASE_VERSION base_version, + const struct sql_table_description *table, char *buffer, size_t buffer_length) { /* The query template: @@ -92,9 +109,17 @@ ___compute_upgrade_sql_query(const struct sql_table_description *table, char *bu /* Create the comma separated list for the table's fields */ size_t field_list_length = 0; - for (size_t i = 0; table->fields[i]; ++i) { - field_list_length += 1; /* The comma */ - field_list_length += strlen(table->fields[i]); /* The field name */ + size_t added_fields = 0; + for (size_t i = 0; table->fields[i].name != NULL; ++i) { + if (table->fields[i].minimal_version <= base_version) { + field_list_length += 1; /* The comma */ + field_list_length += strlen(table->fields[i].name); /* The field name */ + added_fields += 1; + } + } + if (added_fields == 0) { + LOG_ERROR("DB-UPGRADE", "No fields where found for the database table %s", table->name); + return false; } if (field_list_length >= LKT_MAX_SQLITE_STATEMENT) { @@ -108,9 +133,17 @@ ___compute_upgrade_sql_query(const struct sql_table_description *table, char *bu char field_list[LKT_MAX_SQLITE_STATEMENT]; memset(field_list, 0, sizeof(field_list)); - for (size_t i = 0; table->fields[i]; ++i) { - strcat(field_list, table->fields[i]); - strcat(field_list, ","); + added_fields = 0; + for (size_t i = 0; table->fields[i].name; ++i) { + if (table->fields[i].minimal_version <= base_version) { + strcat(field_list, table->fields[i].name); + strcat(field_list, ","); + added_fields += 1; + } + } + if (added_fields == 0) { + LOG_ERROR("DB-UPGRADE", "No fields where found for the database table %s", table->name); + return false; } field_list[strlen(field_list) - 1] = '\0'; /* Crop the last "," */ @@ -158,8 +191,8 @@ database_upgrade_scheme(va_list *db_list_ptr) /* Copy the content of the old db into the new one */ char sql_upgrade_query[LKT_MAX_SQLITE_STATEMENT]; - for (size_t i = 0; sql_tables_list[i]; ++i) { - const struct sql_table_description *sql_table = sql_tables_list[i]; + for (size_t i = 0; i < sql_tables_list_count; ++i) { + const struct sql_table_description *sql_table = &sql_tables_list[i]; if (sql_table->minimal_version > version) { LOG_INFO("DB-UPGRADE", "Don't apply upgrade rule '%s' because the minimal database version " @@ -169,8 +202,8 @@ database_upgrade_scheme(va_list *db_list_ptr) continue; } - unless ( - ___compute_upgrade_sql_query(sql_table, sql_upgrade_query, LKT_MAX_SQLITE_STATEMENT)) { + unless (___compute_upgrade_sql_query(version, sql_table, sql_upgrade_query, + LKT_MAX_SQLITE_STATEMENT)) { LOG_ERROR("DB-UPGRADE", "Can't upgrade the database, detach the new one and exit"); database_unprotected_detach(db, LKT_PROTECTED_DATABASE_NEW); return false;