# Database Migrations System

**Version:** v9c
**Implementiert:** 2026-03-21
**Dokumentiert:** 2026-03-21

---

## Übersicht

Mitai Jinkendo verwendet ein automatisches Migrations-System für strukturierte Schema-Änderungen. Alle Migrationen werden beim Container-Start automatisch ausgeführt.

## Architektur

### Migration-Tracking

**Tabelle:** `schema_migrations`
```sql
CREATE TABLE schema_migrations (
    id SERIAL PRIMARY KEY,
    filename VARCHAR(255) UNIQUE NOT NULL,
    applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
)
```

Diese Tabelle trackt, welche Migrationen bereits angewendet wurden.

### Ablauf beim Container-Start

1. **PostgreSQL Connection Check** (`db_init.py`)
2. **Schema-Initialisierung** (falls `profiles` Tabelle nicht existiert)
   - Lädt `backend/schema.sql` (Basis-Schema)
3. **Migrations-System** (`run_migrations()`)
   - Erstellt `schema_migrations` Tabelle (falls nicht vorhanden)
   - Scannt `backend/migrations/` nach `.sql` Dateien
   - Filtert nur nummerierte Dateien: `\d{3}_*.sql` (z.B. `001_feature.sql`)
   - Sortiert alphabetisch (aufsteigende Reihenfolge)
   - Wendet nur noch nicht angewendete Migrationen an
   - Trackt jede erfolgreich angewendete Migration
4. **SQLite-zu-PostgreSQL Migration** (falls vorhanden)

## Datei-Konventionen

### Naming-Pattern

**Format:** `XXX_descriptive_name.sql`

- `XXX` = Dreistellige Nummer (001, 002, 003, ...)
- Unterstrich `_` als Trennzeichen
- Kleinbuchstaben, keine Leerzeichen
- `.sql` Extension

**Beispiele:**
```
✅ 001_subscription_system.sql
✅ 002_fix_features.sql
✅ 003_add_email_verification.sql

❌ v9c_subscription_system.sql    # Keine Nummer
❌ check_features.sql              # Keine Nummer
❌ 1_feature.sql                   # Nur eine Ziffer
❌ 001-feature.sql                 # Bindestrich statt Unterstrich
```

### Datei-Struktur

Jede Migration sollte folgende Struktur haben:

```sql
-- ================================================================
-- Migration XXX: Beschreibung
-- Version: vX.X
-- Date: YYYY-MM-DD
-- ================================================================

-- Beschreibung der Änderung
ALTER TABLE table_name ...

-- Weitere SQL-Statements

-- Kommentare für Dokumentation
COMMENT ON COLUMN table.column IS 'Beschreibung';
```

### SQL-Einschränkungen

**Erlaubt:**
- Standard SQL DDL (CREATE, ALTER, DROP)
- Standard SQL DML (INSERT, UPDATE, DELETE)
- CREATE INDEX, CREATE FUNCTION, etc.
- Multi-Statement-Scripts (durch `;` getrennt)

**Nicht erlaubt:**
- psql Meta-Kommandos (`\echo`, `\set`, `\connect`, etc.)
- Interactive Commands (`\i`, `\include`)
- Transaktions-Kontrolle (automatisch gehandhabt)

## Anwendung

### Automatisch (Production/Dev)

Migrationen werden **automatisch** beim Container-Start angewendet:

```bash
# Container startet
docker compose up -d

# db_init.py wird ausgeführt:
# 1. PostgreSQL ready check
# 2. Schema initialisiert (falls nötig)
# 3. Migrationen ausgeführt
# 4. SQLite-Migration (falls nötig)
```

**Log-Output:**
```
═══════════════════════════════════════════════════════════
MITAI JINKENDO - Database Initialization (v9c)
═══════════════════════════════════════════════════════════

Checking PostgreSQL connection...
✓ PostgreSQL ready

Checking database schema...
✓ Schema already exists

Running database migrations...
  Found 2 pending migration(s)...
  ✓ Applied: 001_subscription_system.sql
  ✓ Applied: 003_add_email_verification.sql

✓ Database initialization complete
```

### Manuell (Entwicklung)

Für Testing während der Entwicklung:

```bash
# Im laufenden Container
docker exec -it dev-mitai-api python3 /app/db_init.py

# Oder direkt mit psql (für einzelne Migrationen)
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
  -f /path/to/migration.sql
```

## Migration erstellen

### Schritt 1: Datei erstellen

```bash
# Nächste freie Nummer ermitteln
ls backend/migrations/ | grep -E '^\d{3}_' | sort | tail -1
# → 003_add_email_verification.sql

# Neue Migration mit Nummer 004
touch backend/migrations/004_add_new_feature.sql
```

### Schritt 2: SQL schreiben

```sql
-- ================================================================
-- Migration 004: Add New Feature
-- Version: v9d
-- Date: 2026-03-22
-- ================================================================

-- Add new column
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS new_feature_enabled BOOLEAN DEFAULT TRUE;

-- Create index if needed
CREATE INDEX IF NOT EXISTS idx_profiles_new_feature
ON profiles(new_feature_enabled)
WHERE new_feature_enabled = TRUE;

-- Update existing data if needed
UPDATE profiles
SET new_feature_enabled = TRUE
WHERE tier = 'premium';

COMMENT ON COLUMN profiles.new_feature_enabled IS 'Feature flag for new feature';
```

### Schritt 3: Testen

```bash
# Lokal testen (Dev-Container)
git add backend/migrations/004_add_new_feature.sql
git commit -m "feat: add migration for new feature"
git push origin develop

# Container wird neu gebaut und Migration automatisch angewendet
```

### Schritt 4: Verifizieren

```bash
# Prüfen ob Migration angewendet wurde
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
  -c "SELECT * FROM schema_migrations ORDER BY applied_at DESC LIMIT 5;"

# Output:
# id | filename                      | applied_at
# ---+------------------------------+------------------------
#  4 | 004_add_new_feature.sql      | 2026-03-22 10:30:15+00
#  3 | 003_add_email_verification.sql| 2026-03-21 15:20:10+00
#  2 | 002_fix_features.sql         | 2026-03-20 12:10:05+00
#  1 | 001_subscription_system.sql  | 2026-03-20 12:10:00+00
```

## Rollback-Strategie

**Automatischer Rollback:** Nicht implementiert

**Manueller Rollback:**

1. **Identifizieren der problematischen Migration:**
   ```bash
   docker logs dev-mitai-api | grep "Failed to apply"
   ```

2. **Migration aus Tracking entfernen:**
   ```sql
   DELETE FROM schema_migrations
   WHERE filename = '004_broken_migration.sql';
   ```

3. **Änderungen manuell rückgängig machen:**
   ```sql
   -- Beispiel: Spalte entfernen
   ALTER TABLE profiles DROP COLUMN new_feature_enabled;
   ```

4. **Migration-Datei korrigieren**
   ```bash
   vim backend/migrations/004_broken_migration.sql
   ```

5. **Container neu starten** (Migration wird erneut ausgeführt)
   ```bash
   docker compose restart api
   ```

## Best Practices

### ✅ DO

- **Immer `IF NOT EXISTS` / `IF EXISTS` verwenden:**
  ```sql
  ALTER TABLE profiles ADD COLUMN IF NOT EXISTS email_verified BOOLEAN;
  CREATE INDEX IF NOT EXISTS idx_profiles_email ON profiles(email);
  ```

- **Idempotente Migrationen schreiben:** Migration kann mehrfach ausgeführt werden ohne Fehler

- **Daten-Migrationen mit Bedacht:**
  ```sql
  UPDATE profiles
  SET email_verified = TRUE
  WHERE email IS NOT NULL AND email_verified IS NULL;
  ```

- **Kommentare für Dokumentation:**
  ```sql
  COMMENT ON COLUMN profiles.email_verified IS 'Whether email has been verified';
  ```

- **Indices für Performance:**
  ```sql
  CREATE INDEX IF NOT EXISTS idx_profiles_verification_token
  ON profiles(verification_token)
  WHERE verification_token IS NOT NULL;  -- Partial index
  ```

### ❌ DON'T

- **Keine psql Meta-Kommandos:**
  ```sql
  \echo "Starting migration"  -- ❌ Funktioniert nicht
  SELECT 'Starting migration';  -- ✅ Funktioniert
  ```

- **Keine Breaking Changes ohne Staging:**
  ```sql
  ALTER TABLE profiles DROP COLUMN tier;  -- ❌ App wird brechen
  ```

- **Keine Hardcoded Values für produktive Daten:**
  ```sql
  INSERT INTO profiles (id, email, ...) VALUES (1, 'admin@example.com', ...);  -- ❌
  ```

- **Keine Foreign Keys ohne Fallback:**
  ```sql
  -- ❌ Ohne ON DELETE Behavior
  ALTER TABLE subscriptions
  ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id) REFERENCES profiles(id);

  -- ✅ Mit Cascade
  ALTER TABLE subscriptions
  ADD CONSTRAINT fk_profile FOREIGN KEY (profile_id)
  REFERENCES profiles(id) ON DELETE CASCADE;
  ```

## Troubleshooting

### Migration schlägt fehl

**Symptom:** Container startet nicht, Logs zeigen `✗ Failed to apply XXX.sql`

**Lösung:**
1. Logs prüfen: `docker logs dev-mitai-api | tail -50`
2. Fehlerhafte Migration identifizieren
3. Aus Tracking entfernen + Schema manuell korrigieren
4. Migration-Datei fixen
5. Container neu starten

### Migration wurde nicht angewendet

**Symptom:** Neue Migration-Datei wird ignoriert

**Ursachen:**
- Datei entspricht nicht dem Pattern `\d{3}_*.sql`
- Datei wurde bereits in `schema_migrations` getrackt
- Datei ist nicht im Container (`backend/migrations/` Volume gemountet?)

**Lösung:**
```bash
# Prüfen ob Datei gemountet ist
docker exec -it dev-mitai-api ls -la /app/migrations/

# Prüfen ob bereits getrackt
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
  -c "SELECT * FROM schema_migrations WHERE filename = '004_feature.sql';"

# Falls fälschlich getrackt, entfernen
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
  -c "DELETE FROM schema_migrations WHERE filename = '004_feature.sql';"

# Container neu starten
docker compose restart api
```

### Datenbank-Schema inkonsistent

**Symptom:** `schema_migrations` zeigt Migration als angewendet, aber Änderungen fehlen

**Ursachen:**
- Migration wurde manuell ausgeführt, aber Tracking war kaputt
- Datenbank wurde zurückgesetzt, aber Tracking-Tabelle nicht

**Lösung:**
```bash
# Schema neu aufbauen (ACHTUNG: Datenverlust!)
docker compose down -v  # Löscht Volumes
docker compose up -d    # Baut alles neu auf

# ODER: Tracking-Tabelle manuell korrigieren
docker exec -it dev-mitai-db psql -U mitai_dev -d mitai_dev \
  -c "TRUNCATE schema_migrations; -- Alle Tracking-Einträge löschen"

# Container neu starten → Alle Migrationen werden erneut angewendet
docker compose restart api
```

## Migrations-Historie

| Nr. | Datei | Version | Datum | Beschreibung |
|-----|-------|---------|-------|--------------|
| 003 | `003_add_email_verification.sql` | v9c | 2026-03-21 | Email-Verifizierung (verification_token, email_verified, verification_expires) |
| 002 | `002_fix_features.sql` (manuell) | v9c | 2026-03-20 | Feature-System Fixes |
| 001 | `001_subscription_system.sql` (manuell) | v9c | 2026-03-20 | Membership-System (tiers, subscriptions, coupons, access_grants) |

**Hinweis:** Migrationen 001 und 002 wurden vor Einführung des automatischen Systems manuell angewendet und sind nicht nummeriert. Ab Migration 003 läuft alles automatisch.

---

## Referenzen

- **Code:** `backend/db_init.py` (Zeilen 94-200)
- **Migrations-Ordner:** `backend/migrations/`
- **Tracking-Tabelle:** `schema_migrations`
- **Startup-Script:** `backend/startup.sh` (ruft `db_init.py` auf)
- **Dokumentation:** `.claude/docs/technical/MIGRATIONS.md` (diese Datei)

---

**Dokumentiert:** 2026-03-21
**Letzte Änderung:** 2026-03-21
