16static int migrate_ensure_table(sqlite3 *db) {
17 const char *sql =
"CREATE TABLE IF NOT EXISTS _cwist_migrations ("
18 " version INTEGER PRIMARY KEY,"
19 " name TEXT NOT NULL,"
20 " applied_at TEXT NOT NULL DEFAULT (datetime('now','utc'))"
23 int rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
24 if (rc != SQLITE_OK) {
25 fprintf(stderr,
"[migrate] ensure table: %s\n", errmsg ? errmsg :
"?");
40static int migrate_exec_sql(sqlite3 *db,
const char *sql,
int version,
const char *direction) {
42 int rc = sqlite3_exec(db,
"BEGIN;", NULL, NULL, &errmsg);
43 if (rc != SQLITE_OK) {
44 fprintf(stderr,
"[migrate] BEGIN failed (v%d %s): %s\n", version, direction,
45 errmsg ? errmsg :
"?");
50 rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
51 if (rc != SQLITE_OK) {
52 fprintf(stderr,
"[migrate] SQL failed (v%d %s): %s\n", version, direction,
53 errmsg ? errmsg :
"?");
55 sqlite3_exec(db,
"ROLLBACK;", NULL, NULL, NULL);
59 rc = sqlite3_exec(db,
"COMMIT;", NULL, NULL, &errmsg);
60 if (rc != SQLITE_OK) {
61 fprintf(stderr,
"[migrate] COMMIT failed (v%d %s): %s\n", version, direction,
62 errmsg ? errmsg :
"?");
64 sqlite3_exec(db,
"ROLLBACK;", NULL, NULL, NULL);
76static int migration_cmp_asc(
const void *a,
const void *b) {
88static int migration_cmp_desc(
const void *a,
const void *b) {
103 sqlite3_stmt *stmt = NULL;
104 int rc = sqlite3_prepare_v2(db,
"SELECT COALESCE(MAX(version), 0) FROM _cwist_migrations;", -1,
106 if (rc != SQLITE_OK)
return -1;
109 if (sqlite3_step(stmt) == SQLITE_ROW) {
110 version = sqlite3_column_int(stmt, 0);
112 sqlite3_finalize(stmt);
138 for (
int i = 0; i <
count; i++) {
139 if (sorted[i].version <= current)
continue;
140 if (!sorted[i].up_sql)
continue;
142 int rc = migrate_exec_sql(db, sorted[i].up_sql, sorted[i].version,
"up");
150 sqlite3_stmt *ins = NULL;
151 if (sqlite3_prepare_v2(db,
152 "INSERT INTO _cwist_migrations (version, name) VALUES (?, ?);",
153 -1, &ins, NULL) == SQLITE_OK) {
154 sqlite3_bind_int(ins, 1, sorted[i].version);
155 sqlite3_bind_text(ins, 2, sorted[i].name ? sorted[i].name :
"", -1, SQLITE_STATIC);
157 sqlite3_finalize(ins);
190 int limit = (steps <= 0) ?
count : steps;
192 for (
int i = 0; i <
count && rolled < limit; i++) {
193 if (sorted[i].version > current)
continue;
196 sqlite3_stmt *chk = NULL;
198 if (sqlite3_prepare_v2(db,
"SELECT 1 FROM _cwist_migrations WHERE version = ?;", -1, &chk,
199 NULL) == SQLITE_OK) {
200 sqlite3_bind_int(chk, 1, sorted[i].version);
201 if (sqlite3_step(chk) == SQLITE_ROW) found = 1;
202 sqlite3_finalize(chk);
204 if (!found)
continue;
208 if (!sorted[i].down_sql)
continue;
210 int rc = migrate_exec_sql(db, sorted[i].down_sql, sorted[i].version,
"down");
218 sqlite3_stmt *del = NULL;
219 if (sqlite3_prepare_v2(db,
"DELETE FROM _cwist_migrations WHERE version = ?;", -1, &del,
220 NULL) == SQLITE_OK) {
221 sqlite3_bind_int(del, 1, sorted[i].version);
223 sqlite3_finalize(del);
int cwist_migrate_version(sqlite3 *db)
Query the highest applied migration version recorded in the database.
int cwist_migrate_up(sqlite3 *db, const cwist_migration_t *migrations, int count)
Apply all pending up migrations after sorting them by version.
int cwist_migrate_down(sqlite3 *db, const cwist_migration_t *migrations, int count, int steps)
Roll back applied migrations in reverse version order.
Schema migration system with version history and rollback.
#define CWIST_MIGRATE_ERR_GENERIC
#define CWIST_MIGRATE_OK
Return codes.
#define CWIST_MIGRATE_ERR_SQL
#define CWIST_MIGRATE_ERR_ARGS