# SIMCO: OPERACIONES DDL (Database)

**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Aplica a:** Todo agente que trabaje con base de datos PostgreSQL
**Prioridad:** OBLIGATORIA para operaciones de BD

---

## RESUMEN EJECUTIVO

> **DDL-First: Los archivos DDL son la fuente de verdad. La BD es el resultado de ejecutarlos.**

---

## PRINCIPIO FUNDAMENTAL

```
╔══════════════════════════════════════════════════════════════════════╗
║  POLÍTICA DE CARGA LIMPIA                                            ║
║                                                                       ║
║  1. TODO cambio de BD = modificar archivo DDL                        ║
║  2. NUNCA ejecutar ALTER/CREATE directo en BD                        ║
║  3. SIEMPRE validar con recreación completa                          ║
║  4. La BD debe poder recrearse 100% desde DDL                        ║
╚══════════════════════════════════════════════════════════════════════╝
```

---

## FLUJO DDL-FIRST

```
1. CREAR/MODIFICAR ARCHIVO DDL
         │
         ▼
2. EJECUTAR CARGA LIMPIA COMPLETA
   cd @DB_SCRIPTS && ./{RECREATE_CMD}
         │
         ▼
3. ¿PASA SIN ERRORES?
   ├── SÍ → Continuar a paso 4
   └── NO → Volver a paso 1 (corregir DDL)
         │
         ▼
4. VALIDAR ESTRUCTURA
   \dt, \di, \d tabla
         │
         ▼
5. COMMIT ARCHIVO DDL
```

---

## ESTRUCTURA DE ARCHIVOS DDL

```
{DB_DDL_PATH}/
├── 00-init.sql                          # Extensiones, tipos base
└── schemas/
    ├── {schema_1}/
    │   ├── 00-schema.sql                # CREATE SCHEMA
    │   ├── tables/
    │   │   ├── 01-{tabla}.sql           # Tablas en orden de dependencia
    │   │   └── 02-{tabla}.sql
    │   ├── functions/
    │   │   └── {funcion}.sql
    │   └── triggers/
    │       └── trg_{tabla}_{accion}.sql
    └── {schema_2}/
        └── ...

{DB_SEEDS_PATH}/
├── dev/                                 # Seeds para desarrollo
│   ├── 01-{nombre}.sql
│   └── 02-{nombre}.sql
└── prod/                                # Seeds para producción
    └── 01-{nombre}.sql
```

---

## CONVENCIONES DE NOMENCLATURA

### Schemas
```sql
-- snake_case
CREATE SCHEMA auth_management;
CREATE SCHEMA gamification_system;
CREATE SCHEMA project_management;
```

### Tablas
```sql
-- snake_case, plural
CREATE TABLE users;
CREATE TABLE student_progress;
CREATE TABLE badge_awards;
```

### Columnas
```sql
-- snake_case
user_id UUID
first_name VARCHAR(100)
created_at TIMESTAMP
is_active BOOLEAN
```

### Índices
```sql
-- idx_{tabla}_{columna(s)}
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status_created ON users(status, created_at);
CREATE UNIQUE INDEX idx_users_username ON users(username);
```

### Foreign Keys
```sql
-- fk_{tabla}_to_{tabla_referenciada}
CONSTRAINT fk_students_to_users
    FOREIGN KEY (user_id)
    REFERENCES auth_management.users(id)
```

### Check Constraints
```sql
-- chk_{tabla}_{columna}
CONSTRAINT chk_users_status
    CHECK (status IN ('active', 'inactive', 'suspended'))
```

### Triggers
```sql
-- trg_{tabla}_{accion}
CREATE TRIGGER trg_users_updated
    BEFORE UPDATE ON users
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();
```

### Functions
```sql
-- snake_case con verbo
CREATE FUNCTION calculate_user_level()
CREATE FUNCTION award_badge_to_user()
CREATE FUNCTION update_updated_at_column()
```

---

## TEMPLATE DE ARCHIVO DDL

```sql
-- ============================================================================
-- Tabla: {nombre_tabla}
-- Schema: {schema}
-- Descripción: {descripción breve de la tabla y su propósito}
-- Autor: {Agente}
-- Fecha: {YYYY-MM-DD}
-- Dependencias: {lista de tablas de las que depende o "Ninguna"}
-- ============================================================================

-- Eliminar si existe (solo desarrollo)
DROP TABLE IF EXISTS {schema}.{tabla} CASCADE;

-- ============================================================================
-- CREAR TABLA
-- ============================================================================
CREATE TABLE {schema}.{tabla} (
    -- ─────────────────────────────────────────────────────────────────────
    -- Identificador
    -- ─────────────────────────────────────────────────────────────────────
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- ─────────────────────────────────────────────────────────────────────
    -- Campos de Negocio
    -- ─────────────────────────────────────────────────────────────────────
    {campo_1} {TIPO} {NOT NULL} {DEFAULT},
    {campo_2} {TIPO} {NOT NULL} {DEFAULT},

    -- ─────────────────────────────────────────────────────────────────────
    -- Referencias (Foreign Keys inline o al final)
    -- ─────────────────────────────────────────────────────────────────────
    {fk_campo} UUID,

    -- ─────────────────────────────────────────────────────────────────────
    -- Auditoría
    -- ─────────────────────────────────────────────────────────────────────
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    created_by UUID,

    -- ─────────────────────────────────────────────────────────────────────
    -- Constraints
    -- ─────────────────────────────────────────────────────────────────────
    CONSTRAINT fk_{tabla}_to_{ref}
        FOREIGN KEY ({fk_campo})
        REFERENCES {schema_ref}.{tabla_ref}(id)
        ON DELETE {CASCADE|SET NULL|RESTRICT},

    CONSTRAINT chk_{tabla}_{campo}
        CHECK ({condicion})
);

-- ============================================================================
-- COMENTARIOS
-- ============================================================================
COMMENT ON TABLE {schema}.{tabla} IS
    '{Descripción de la tabla}';

COMMENT ON COLUMN {schema}.{tabla}.{campo_1} IS
    '{Descripción del campo, valores válidos, restricciones}';

COMMENT ON COLUMN {schema}.{tabla}.{campo_2} IS
    '{Descripción del campo}';

-- ============================================================================
-- ÍNDICES
-- ============================================================================

-- Índice para búsqueda por {criterio}
CREATE INDEX idx_{tabla}_{campo}
    ON {schema}.{tabla}({campo});

-- Índice único para {criterio}
CREATE UNIQUE INDEX idx_{tabla}_{campo}_unique
    ON {schema}.{tabla}({campo});

-- Índice compuesto para {criterio}
CREATE INDEX idx_{tabla}_{campo1}_{campo2}
    ON {schema}.{tabla}({campo1}, {campo2});

-- ============================================================================
-- TRIGGER DE AUDITORÍA
-- ============================================================================
CREATE TRIGGER trg_{tabla}_updated
    BEFORE UPDATE ON {schema}.{tabla}
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- ============================================================================
-- ROW LEVEL SECURITY (si aplica)
-- ============================================================================
ALTER TABLE {schema}.{tabla} ENABLE ROW LEVEL SECURITY;

CREATE POLICY {tabla}_select_policy
    ON {schema}.{tabla}
    FOR SELECT
    USING ({condicion_rls});

CREATE POLICY {tabla}_insert_policy
    ON {schema}.{tabla}
    FOR INSERT
    WITH CHECK ({condicion_rls});

-- ============================================================================
-- FIN DE ARCHIVO
-- ============================================================================
```

---

## COMANDOS DE VALIDACIÓN

### Carga Limpia (OBLIGATORIO)
```bash
cd @DB_SCRIPTS
./{RECREATE_CMD}
# DEBE completar sin errores
```

### Verificar Estructura
```bash
# Listar schemas
psql -d {DB_NAME} -c "\dn"

# Listar tablas de un schema
psql -d {DB_NAME} -c "\dt {schema}.*"

# Ver estructura de tabla
psql -d {DB_NAME} -c "\d {schema}.{tabla}"

# Ver índices
psql -d {DB_NAME} -c "\di {schema}.*"

# Ver constraints
psql -d {DB_NAME} -c "\d+ {schema}.{tabla}"
```

### Verificar Comentarios
```bash
# Ver comentario de tabla
psql -d {DB_NAME} -c "
SELECT obj_description('{schema}.{tabla}'::regclass);
"

# Ver comentarios de columnas
psql -d {DB_NAME} -c "
SELECT column_name, col_description('{schema}.{tabla}'::regclass, ordinal_position)
FROM information_schema.columns
WHERE table_schema = '{schema}' AND table_name = '{tabla}';
"
```

### Tests Básicos
```bash
# Insert de prueba
psql -d {DB_NAME} -c "
INSERT INTO {schema}.{tabla} ({columnas})
VALUES ({valores});
"

# Verificar constraint CHECK
psql -d {DB_NAME} -c "
INSERT INTO {schema}.{tabla} ({columna})
VALUES ('valor_invalido');
"
# Debe fallar con error de constraint

# Verificar FK
psql -d {DB_NAME} -c "
INSERT INTO {schema}.{tabla} ({fk_columna})
VALUES ('uuid-no-existente');
"
# Debe fallar con error de FK
```

---

## PROHIBICIONES

```
╔══════════════════════════════════════════════════════════════════════╗
║  ❌ PROHIBIDO                                                        ║
╠══════════════════════════════════════════════════════════════════════╣
║                                                                       ║
║  • Ejecutar ALTER TABLE directo en BD sin actualizar DDL             ║
║  • Crear carpeta migrations/                                         ║
║  • Crear archivos fix-*.sql o patch-*.sql                            ║
║  • Crear archivos migration-*.sql                                    ║
║  • Dejar BD y DDL desincronizados                                    ║
║  • Ejecutar psql -c "CREATE/ALTER..." sin archivo DDL                ║
║  • Marcar tarea completa sin carga limpia exitosa                    ║
║                                                                       ║
╚══════════════════════════════════════════════════════════════════════╝
```

---

## MODIFICAR TABLA EXISTENTE

### Proceso Correcto
```bash
# 1. Editar archivo DDL existente (NO crear migration)
vim @DDL/{schema}/tables/{NN}-{tabla}.sql

# 2. Agregar/modificar columna en CREATE TABLE
# (NO usar ALTER TABLE como cambio principal)

# 3. Ejecutar carga limpia completa
cd @DB_SCRIPTS
./{RECREATE_CMD}

# 4. Verificar cambios
psql -d {DB_NAME} -c "\d {schema}.{tabla}"

# 5. Commit cambios
git add @DDL/{schema}/tables/{NN}-{tabla}.sql
git commit -m "feat(db): add column to {tabla}"
```

### Proceso INCORRECTO
```bash
# ❌ NO hacer esto
psql -d {DB_NAME} -c "ALTER TABLE {schema}.{tabla} ADD COLUMN..."

# ❌ NO crear migration
echo "ALTER TABLE..." > migrations/002-add-column.sql

# ❌ NO crear fix temporal
echo "ALTER TABLE..." > fix-add-column.sql
```

---

## CHECKLIST DDL

```
ANTES DE CREAR
├── [ ] Verificar que tabla no existe (@INVENTORY, grep)
├── [ ] Verificar schema existe (00-schema.sql)
├── [ ] Identificar dependencias (FKs a otras tablas)
├── [ ] Determinar número de archivo (secuencial)
└── [ ] Leer tabla similar como referencia

DURANTE CREACIÓN
├── [ ] Encabezado completo (descripción, autor, fecha, deps)
├── [ ] Columnas con tipos correctos
├── [ ] Campos de auditoría (created_at, updated_at)
├── [ ] Constraints nombrados correctamente
├── [ ] Índices necesarios
├── [ ] COMMENT ON para tabla y columnas importantes
└── [ ] Trigger de updated_at (si aplica)

VALIDACIÓN
├── [ ] Carga limpia exitosa
├── [ ] Estructura verificada (\d tabla)
├── [ ] Índices verificados (\di)
├── [ ] Insert de prueba exitoso
├── [ ] Constraints funcionan (test de validación)
└── [ ] Comentarios presentes

DOCUMENTACIÓN
├── [ ] @INVENTORY actualizado
├── [ ] @TRAZA_DB actualizada
└── [ ] Entity correspondiente identificada para Backend
```

---

## ERRORES COMUNES

| Error | Causa | Solución |
|-------|-------|----------|
| FK falla | Tabla referenciada no existe | Verificar orden de creación |
| Tipo incorrecto | No alineado con diseño | Consultar especificación |
| Sin comentarios | Olvidó COMMENT ON | Template incluye comentarios |
| Sin índices | No pensó en queries | Agregar para columnas de búsqueda |
| Carga limpia falla | DDL con errores | Corregir DDL, no fix manual |

---

## REFERENCIAS

- **Crear archivos:** @CREAR (SIMCO-CREAR.md)
- **Validar:** @VALIDAR (SIMCO-VALIDAR.md)
- **Documentar:** @DOCUMENTAR (SIMCO-DOCUMENTAR.md)
- **Política carga limpia:** @DIRECTIVAS/DIRECTIVA-POLITICA-CARGA-LIMPIA.md
- **Diseño de BD:** @DIRECTIVAS/DIRECTIVA-DISENO-BASE-DATOS.md
- **Nomenclatura:** @DIRECTIVAS/ESTANDARES-NOMENCLATURA-BASE.md

---

**Versión:** 1.0.0 | **Sistema:** SIMCO | **Mantenido por:** Tech Lead
