# Migration Files

Migration files are the heart of DBWarden. They contain SQL statements that modify your database schema.

## File Structure

### Naming Convention

Migration files follow this naming pattern:

```
{number}_{description}.sql
```

**Components:**
- `{number}`: 4-digit sequential number (auto-incremented)
- `{description}`: Lowercase, underscore-separated description

**Examples:**
```
0001_initial_schema.sql
0002_create_users_table.sql
0003_add_user_posts_relationship.sql
```

### Internal Structure

Each migration file has two sections:

```sql
-- upgrade

-- Your upgrade SQL statements here

-- rollback

-- Your rollback SQL statements here
```

## Example Migrations

### Create Table

```sql
-- migrations/0001_create_users.sql

-- upgrade

CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(255) NOT NULL UNIQUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_users_email ON users(email);

-- rollback

DROP INDEX idx_users_email;
DROP TABLE users;
```

### Add Column

```sql
-- migrations/0002_add_user_profile.sql

-- upgrade

ALTER TABLE users ADD COLUMN bio TEXT;
ALTER TABLE users ADD COLUMN avatar_url VARCHAR(500);

-- rollback

ALTER TABLE users DROP COLUMN avatar_url;
ALTER TABLE users DROP COLUMN bio;
```

### Complex Migration with Transactions

```sql
-- migrations/0003_migrate_to_new_schema.sql

-- upgrade

BEGIN;

-- Create new table
CREATE TABLE posts_new (
    id INTEGER PRIMARY KEY,
    author_id INTEGER NOT NULL,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Copy data
INSERT INTO posts_new (id, author_id, title, content, created_at)
SELECT id, user_id, title, content, created_at FROM posts;

-- Drop old table
DROP TABLE posts;

-- Rename new table
ALTER TABLE posts_new RENAME TO posts;

COMMIT;

-- rollback

BEGIN;

-- Reverse the migration
CREATE TABLE posts_old (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO posts_old (id, user_id, title, content, created_at)
SELECT id, author_id, title, content, created_at FROM posts;

DROP TABLE posts;

ALTER TABLE posts_old RENAME TO posts;

COMMIT;
```

## SQL Support

### Supported SQL Statements

- `CREATE TABLE`
- `ALTER TABLE ADD/DROP/MODIFY COLUMN`
- `DROP TABLE`
- `CREATE INDEX`
- `DROP INDEX`
- `CREATE/DROP VIEW`
- `CREATE/DROP TRIGGER`
- `INSERT`, `UPDATE`, `DELETE` (for data migration)
- `BEGIN`, `COMMIT`, `ROLLBACK` (transactions)

### Database-Specific SQL

DBWarden supports database-specific SQL:

```sql
-- PostgreSQL
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- MySQL
ALTER TABLE users ENGINE=InnoDB;

-- SQLite
PRAGMA foreign_keys = ON;
```

## Version Management

### Version Formats

**Sequential Number (Django-style):**
```
0001_description.sql
```
- Unique: Auto-incremented sequential number
- Ordered: Naturally sorted numerically
- Automatic: Generated by `make-migrations`

**Custom Number:**
```
9999_custom_description.sql
```
- Manual: Set with `--version`
- Explicit: You control numbering
- Use when: Need to insert migration between existing ones

### Version Ordering

DBWarden sorts migrations:
- Numerically by the 4-digit prefix
- Allows: `0001`, `0002`, ..., `9999`

## Best Practices

### 1. One Migration Per Feature

```
GOOD:
├── 0001_initial_schema.sql
├── 0002_create_users.sql
├── 0003_create_posts.sql
└── 0004_add_comments.sql

BAD:
└── 0001_users_posts_comments_all.sql
```

### 2. Descriptive Names

```
GOOD: 0004_add_user_email_verification.sql
BAD:  0004_changes.sql
```

### 3. Always Include Rollback

```sql
-- GOOD: Has rollback
-- upgrade
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));
-- rollback
DROP TABLE users;

-- BAD: No rollback
-- upgrade
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));
```

### 4. Idempotent Migrations

```sql
-- GOOD: Idempotent
CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY);

-- GOOD: Safe to run twice
DROP INDEX IF EXISTS idx_users_email;
CREATE INDEX idx_users_email ON users(email);
```

### 5. Use Transactions for Complex Changes

```sql
-- Wrap in transaction for atomicity
BEGIN;
-- All changes
COMMIT;
```

## Migration Lifecycle

```
┌─────────────┐
│ New File    │ Created by make-migrations or new
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ Pending     │ Not yet applied
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ Applied     │ migrate command executed
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ Recorded    │ Added to dbwarden_migrations table
└─────────────┘
```

## Directory Structure

```
project/
├── migrations/
│   ├── 0001_initial_schema.sql
│   ├── 0002_create_users.sql
│   ├── 0003_create_posts.sql
│   └── .gitkeep          # Keep directory in git
├── models/
│   └── user.py
├── warden.toml
└── pyproject.toml
```

## Git Integration

### Add to Version Control

```bash
git add migrations/
git commit -m "Add database migrations"
```

### Migration File Rules

1. **Never modify applied migrations**
2. **Always create new migrations for changes**
3. **Review migration SQL before applying**

### Handling Migration Conflicts

When merging branches:

1. Review both migration sequences
2. Combine if needed (or use squash)
3. Ensure order is preserved
4. Test after merge

## Troubleshooting

### Migration Not Found

Check file naming:
```
WRONG: desc.sql                      (missing number)
WRONG: 1_desc.sql                    (not 4 digits)
RIGHT: 0001_desc.sql
```

### Parsing Errors

Ensure proper section markers:
```sql
-- upgrade         (lowercase, no extra spaces)
-- rollback        (lowercase)
```

### Duplicate Versions

Use unique version numbers:
```
ERROR: 0001_feature.sql already exists
FIX: Use 0002_feature.sql or let auto-numbering handle it
```

## See Also

- [make-migrations](commands/make-migrations.md): Auto-generate migrations
- [new](commands/new.md): Create manual migrations
- [migrate](commands/migrate.md): Apply migrations
- [rollback](commands/rollback.md): Revert migrations
