This document describes the database migration system in the Inference Gateway CLI.
The CLI uses a migration system to ensure smooth upgrades between versions. Migrations are automatically applied when the database is initialized, ensuring your schema is always up to date.
The migration system tracks which migrations have been applied using a schema_migrations table. Each migration has:
- Version: A unique identifier (e.g., "001", "002")
- Description: Human-readable description of what the migration does
- UpSQL: SQL statements to apply the migration
- DownSQL: SQL statements to rollback the migration (optional)
Migrations are automatically applied in the following scenarios:
- First time initialization: When you run
infer init - Database connection: When the CLI connects to the database for the first time
- Version upgrades: When upgrading to a new CLI version with schema changes
The schema_migrations table tracks applied migrations:
CREATE TABLE schema_migrations (
version VARCHAR(255) PRIMARY KEY,
description TEXT NOT NULL,
applied_at TIMESTAMP NOT NULL
);The migration system supports:
- SQLite: Default storage backend
- PostgreSQL: Production-ready relational database
- Redis: In-memory storage (no migrations needed)
- Memory: In-memory storage for testing (no migrations needed)
Migrations are automatically run when connecting to the database. To manually trigger migrations:
# Run pending migrations
infer migrate
# Show migration status without applying
infer migrate --statusWhen initializing a new project, migrations are run automatically:
# Initialize and run migrations (default)
infer init --overwrite
# Initialize without running migrations
infer init --overwrite --skip-migrationsCheck which migrations have been applied:
infer migrate --statusOutput example:
SQLite Migration Status:
✅ Version 001: Initial schema - conversations table (Applied)
❌ Version 002: Add user preferences table (Pending)
When upgrading the CLI to a newer version:
- Stop the CLI: Close any running chat sessions
- Upgrade the binary: Install the new version
- Run the CLI: Migrations apply automatically on first use
- Verify: Check status with
infer migrate --status
If you prefer to run migrations manually:
# After upgrading, run migrations explicitly
infer migrate
# Verify all migrations applied
infer migrate --statusThe migration system does not support automatic rollback. If a migration fails:
- The transaction is rolled back automatically
- No partial state is left in the database
- The migration must be fixed before proceeding
- Downgrade the CLI if needed and restore from backup
When adding schema changes:
-
Create migration file:
- SQLite:
internal/infra/storage/migrations/sqlite_migrations.go - PostgreSQL:
internal/infra/storage/migrations/postgres_migrations.go
- SQLite:
-
Add migration to the list:
// SQLite example
{
Version: "002",
Description: "Add user preferences table",
UpSQL: `
CREATE TABLE user_preferences (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
settings TEXT NOT NULL
);
CREATE INDEX idx_user_preferences_user_id ON user_preferences(user_id);
`,
DownSQL: `
DROP INDEX IF EXISTS idx_user_preferences_user_id;
DROP TABLE IF EXISTS user_preferences;
`,
}- Test the migration:
# Run tests
go test ./internal/infra/storage/migrations/...
# Test manually with a fresh database
rm ~/.infer/conversations.db
infer chat- Document the change: Update this file and CHANGELOG.md
- Incremental versions: Use sequential numbers (001, 002, 003)
- Idempotent SQL: Use
IF NOT EXISTSandIF EXISTSclauses - Transactional: Keep migrations atomic (all or nothing)
- Backwards compatible: Avoid breaking changes when possible
- Test thoroughly: Test on fresh databases and with existing data
- Document schema changes: Update docs and CHANGELOG
If a migration fails:
# Check migration status
infer migrate --status
# Review error message
infer migrateCommon issues:
- Database locked: Close all CLI instances
- Permission denied: Check file/directory permissions
- Syntax error: Review migration SQL
- Constraint violation: Check for existing data conflicts
To start fresh (
# SQLite (default)
rm ~/.infer/conversations.db
infer migrate
# PostgreSQL
psql -c "DROP DATABASE infer_gateway; CREATE DATABASE infer_gateway;"
infer migrateIf automatic migration fails, you can apply migrations manually:
# SQLite
sqlite3 ~/.infer/conversations.db < migration.sql
# PostgreSQL
psql infer_gateway < migration.sqlThe current schema version can be determined by:
# Show applied migrations
infer migrate --status
# Query directly (SQLite)
sqlite3 ~/.infer/conversations.db "SELECT version, description, applied_at FROM schema_migrations ORDER BY version;"
# Query directly (PostgreSQL)
psql infer_gateway -c "SELECT version, description, applied_at FROM schema_migrations ORDER BY version;"SQLite:
- Created
conversationstable with all required columns - Added index on
updated_atfor efficient queries
PostgreSQL:
- Created
conversationstable (single-table schema with messages embedded as a JSON blob, mirroring SQLite; the shared SQL core drives both dialects) - Added index on
updated_atfor efficient queries