| name | database-migration-runner |
| description | Safely manage database schema changes for Budget Buddy including listing pending migrations, backing up database, executing migrations sequentially, and providing rollback support. Use when migrating database, changing schema, adding tables/columns, or when the user asks to run migrations. |
| allowed-tools | ["Bash(python*)","Bash(sqlite3:*)","Bash(cp:*)","Bash(ls:*)","Read","Grep","Glob"] |
Database Migration Runner
Overview
This skill helps you safely execute database migrations for Budget Buddy's SQLite database. It ensures backups are created, migrations run in correct order, and provides rollback capabilities if something goes wrong.
Prerequisites
- SQLite database exists:
budget_buddy.db
- Backend dependencies installed
- Working directory:
/Users/franklindickinson/Projects/budget-buddy-2
Quick Start
Step 1: List Pending Migrations
ls -1 backend/database/migrations/*.py backend/database/migrations/*.sql 2>/dev/null | sort
Migrations are in /backend/database/migrations/ as .py or .sql files.
Step 2: Backup Database
CRITICAL: Always backup before running migrations!
cp budget_buddy.db budget_buddy.db.backup.$(date +%Y%m%d_%H%M%S)
Verify backup:
ls -lh budget_buddy.db.backup.*
Step 3: Review Migration Content
Before executing, understand what the migration does:
For Python migrations:
cat backend/database/migrations/add_buddy_insights.py
For SQL migrations:
cat backend/database/migrations/create_buddy_insights_tables.sql
Step 4: Execute Migration
Python Migration:
python backend/database/migrations/add_buddy_insights.py
SQL Migration (using SQLite):
sqlite3 budget_buddy.db < backend/database/migrations/create_buddy_insights_tables.sql
SQL Migration (using Python):
python -c "
import sqlite3
conn = sqlite3.connect('budget_buddy.db')
with open('backend/database/migrations/create_buddy_insights_tables.sql', 'r') as f:
conn.executescript(f.read())
conn.commit()
conn.close()
print('Migration executed successfully')
"
Step 5: Verify Migration
sqlite3 budget_buddy.db ".schema" | grep -i "new_table_name"
Or check table exists:
sqlite3 budget_buddy.db ".tables"
Step 6: Test Application
python -m uvicorn backend.api.main:app --reload --port 8000
curl http://127.0.0.1:8000/api/v2/diagnostics
Key Validation Points
Before Migration
- โ
Database backup created
- โ
Migration file reviewed and understood
- โ
No active connections to database (stop backend)
- โ
Sufficient disk space for backup
During Migration
- โ
No syntax errors in SQL/Python
- โ
Foreign key constraints respected
- โ
Data types compatible
- โ
No data loss
After Migration
- โ
Tables/columns created successfully
- โ
Existing data preserved
- โ
Application starts without errors
- โ
All API endpoints functional
Common Issues & Solutions
Issue: "database is locked"
Cause: Backend or another process has open connection
Solution:
pkill -f "uvicorn.*8000"
sleep 2
python backend/database/migrations/your_migration.py
Issue: Migration fails mid-execution
Cause: Syntax error, constraint violation, or data issue
Solution - Rollback:
cp budget_buddy.db budget_buddy.db.failed
cp budget_buddy.db.backup.YYYYMMDD_HHMMSS budget_buddy.db
Issue: "table already exists"
Cause: Migration was partially executed or run twice
Solution:
sqlite3 budget_buddy.db "SELECT name FROM sqlite_master WHERE type='table' AND name='your_table';"
Issue: Foreign key constraint fails
Cause: Referenced table doesn't exist or data inconsistency
Solution:
sqlite3 budget_buddy.db "PRAGMA foreign_keys;"
sqlite3 budget_buddy.db "PRAGMA foreign_keys = ON;"
sqlite3 budget_buddy.db ".tables"
Migration Examples
Example 1: Add Column to Existing Table
SQL Migration (add_trip_tag_column.sql):
ALTER TABLE transactions ADD COLUMN trip_tag TEXT;
CREATE INDEX IF NOT EXISTS idx_transactions_trip_tag ON transactions(trip_tag);
Execute:
sqlite3 budget_buddy.db < backend/database/migrations/add_trip_tag_column.sql
Example 2: Create New Table with Foreign Key
SQL Migration (create_buddy_insights_tables.sql):
CREATE TABLE IF NOT EXISTS weekly_reflections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
week_start_date DATE NOT NULL,
spending_summary TEXT,
category_insights TEXT,
goal_progress TEXT,
recommendations TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_weekly_reflections_week ON weekly_reflections(week_start_date);
Execute:
sqlite3 budget_buddy.db < backend/database/migrations/create_buddy_insights_tables.sql
Example 3: Python Migration with Data Transform
Python Migration (migrate_dining_out_to_going_out.py):
import sqlite3
def migrate():
conn = sqlite3.connect('budget_buddy.db')
cursor = conn.cursor()
cursor.execute("""
UPDATE budget_allocations
SET category = 'Going Out'
WHERE category = 'Dining Out'
""")
cursor.execute("""
UPDATE transactions
SET bb_category = 'Going Out'
WHERE bb_category = 'Dining Out'
""")
conn.commit()
count = cursor.execute("""
SELECT COUNT(*) FROM transactions WHERE bb_category = 'Going Out'
""").fetchone()[0]
print(f"Migrated {count} transactions to 'Going Out' category")
conn.close()
if __name__ == '__main__':
migrate()
Execute:
python backend/database/migrations/migrate_dining_out_to_going_out.py
Migration Best Practices
-
Always Backup First
cp budget_buddy.db budget_buddy.db.backup.$(date +%Y%m%d_%H%M%S)
-
Use Transactions (in Python migrations):
try:
cursor.execute("...")
conn.commit()
except Exception as e:
conn.rollback()
print(f"Error: {e}")
-
Use IF NOT EXISTS (for idempotency):
CREATE TABLE IF NOT EXISTS new_table (...);
ALTER TABLE ... ADD COLUMN IF NOT EXISTS ... ;
-
Test on Copy First:
cp budget_buddy.db test_migration.db
sqlite3 test_migration.db < migration.sql
-
Document Changes in migration file:
-
Version Migration Files:
001_initial_schema.sql
002_add_buddy_insights.py
003_add_trip_tags.sql
Rollback Procedures
Immediate Rollback (Just Ran Migration)
pkill -f "uvicorn.*8000"
LATEST_BACKUP=$(ls -t budget_buddy.db.backup.* | head -1)
cp budget_buddy.db budget_buddy.db.failed
cp $LATEST_BACKUP budget_buddy.db
sqlite3 budget_buddy.db ".tables"
python -m uvicorn backend.api.main:app --reload --port 8000
Rollback Specific Change (Table/Column)
DROP TABLE IF EXISTS new_table;
Rollback Data Changes
sqlite3 backup.db "SELECT * FROM transactions WHERE id = 123;"
Technical Details
SQLite Limitations
- Can't drop columns (< SQLite 3.35): Must recreate table
- Can't modify columns: Must recreate table
- Limited ALTER TABLE: Only ADD COLUMN, RENAME TABLE, RENAME COLUMN
Workaround:
CREATE TABLE transactions_new (...);
INSERT INTO transactions_new SELECT * FROM transactions;
DROP TABLE transactions;
ALTER TABLE transactions_new RENAME TO transactions;
Migration Files in Project
Current migrations in /backend/database/migrations/:
add_buddy_insights.py
consolidate_goal_dimensions.py
create_buddy_insights_tables.sql
create_transaction_aggregations.py
add_income_savings_to_reflections.sql
- Plus 14+ more migration scripts
Database Schema
14 core tables:
transactions - All bank transactions
budget_allocations - Monthly budget categories
monthly_income - Income tracking
monthly_reflections - Month-end reviews
sinking_funds - Savings goals
sinking_fund_transactions - Fund deposits/withdrawals
institutions - Banks (from Plaid)
accounts - Bank accounts
account_connection_log - Sync history
weekly_summaries - Weekly aggregations
subscription_patterns - Recurring transactions
weekly_reflections - Buddy AI weekly insights
weekly_plans - Weekly spending plans
chat_goals - User goals from chat
Testing the Skill
-
Create test migration:
cat > backend/database/migrations/test_add_column.sql << 'EOF'
-- Test migration
ALTER TABLE transactions ADD COLUMN test_column TEXT;
EOF
-
Backup database:
cp budget_buddy.db budget_buddy.db.test_backup
-
Run migration:
sqlite3 budget_buddy.db < backend/database/migrations/test_add_column.sql
-
Verify:
sqlite3 budget_buddy.db "PRAGMA table_info(transactions);" | grep test_column
-
Rollback:
cp budget_buddy.db.test_backup budget_buddy.db
-
Clean up:
rm backend/database/migrations/test_add_column.sql
rm budget_buddy.db.test_backup
Integration with Other Skills
- Full-Stack Setup - Creates initial database
- Development Diagnostics - Validates schema integrity
- Backend Server Startup - Database must be migrated before server starts
References
/backend/database/migrations/ - All migration files
/backend/database/models.py - SQLAlchemy models (target schema)
/backend/database/session.py - Database connection
budget_buddy.db - SQLite database file
Last Updated
January 1, 2026