# Mejores Prácticas para PostgreSQL en OxiCloud Este documento describe las mejores prácticas para el uso de PostgreSQL en OxiCloud, siguiendo recomendaciones oficiales y la guía ["Don't Do This"](https://wiki.postgresql.org/wiki/Don%27t_Do_This) de PostgreSQL. ## Diseño de Esquema ### Tipos de Datos #### Uso de TEXT en lugar de VARCHAR(n) OxiCloud utiliza el tipo `TEXT` en lugar de `VARCHAR(n)` con límites arbitrarios para campos de texto: ```sql -- Recomendado ✅ username TEXT NOT NULL UNIQUE -- Evitar ❌ username VARCHAR(32) NOT NULL UNIQUE ``` **Razones:** - `TEXT` y `VARCHAR` tienen el mismo rendimiento y ocupan el mismo espacio. - `VARCHAR(n)` impone un límite arbitrario que puede causar errores inesperados. - PostgreSQL optimiza internamente ambos tipos de manera idéntica. #### Uso de TIMESTAMPTZ para Fechas y Horas OxiCloud utiliza `TIMESTAMP WITH TIME ZONE` (o `TIMESTAMPTZ`) para todos los campos de fecha/hora: ```sql -- Recomendado ✅ created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP -- Evitar ❌ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ``` **Razones:** - `TIMESTAMPTZ` almacena un punto en el tiempo unívoco. - Gestiona correctamente las zonas horarias y cambios de horario de verano. - Evita problemas de ambigüedad al trabajar con diferentes husos horarios. #### Evitar CHAR(n) OxiCloud no utiliza el tipo `CHAR(n)` en ningún caso: ```sql -- Recomendado ✅ country_code TEXT NOT NULL CHECK (length(country_code) = 2) -- Evitar ❌ country_code CHAR(2) NOT NULL ``` **Razones:** - `CHAR(n)` rellena con espacios hasta la longitud declarada. - Este comportamiento puede causar problemas sutiles en comparaciones. - Para valores de longitud fija, es mejor usar `TEXT` con una restricción CHECK. #### Usar SERIAL con Precaución OxiCloud utiliza `SERIAL` solo en casos específicos, prefiriendo `IDENTITY` cuando es posible: ```sql -- Recomendado para PostgreSQL 10+ ✅ id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY -- Alternativa aceptable para compatibilidad ✅ id SERIAL PRIMARY KEY ``` **Razones:** - `SERIAL` tiene comportamientos extraños con gestión de dependencias y permisos. - Las columnas `IDENTITY` (PostgreSQL 10+) ofrecen mejor integración con el sistema. ### Índices y Restricciones #### Nombrado Consistente de Índices OxiCloud sigue una convención de nomenclatura para índices: ```sql -- Índice en una columna CREATE INDEX IF NOT EXISTS idx_table_column ON schema.table(column); -- Índice en múltiples columnas CREATE INDEX IF NOT EXISTS idx_table_col1_col2 ON schema.table(col1, col2); ``` #### Uso de Restricciones Explícitas OxiCloud define restricciones explícitas en lugar de depender de convenciones implícitas: ```sql -- Restricción de unicidad UNIQUE(user_id, item_id, item_type) -- Restricción de comprobación CHECK (storage_quota_bytes >= 0) ``` ## Consultas SQL ### Evitar NOT IN con Subconsultas OxiCloud evita el uso de `NOT IN` con subconsultas: ```sql -- Recomendado ✅ SELECT * FROM files WHERE NOT EXISTS (SELECT 1 FROM deleted_files WHERE deleted_files.id = files.id); -- Evitar ❌ SELECT * FROM files WHERE id NOT IN (SELECT id FROM deleted_files); ``` **Razones:** - `NOT IN` se comporta de manera inesperada con valores NULL. - `NOT EXISTS` es más eficiente y predecible. ### Usar BETWEEN con Precaución OxiCloud evita `BETWEEN` para rangos de fechas, prefiriendo comparaciones explícitas: ```sql -- Recomendado ✅ WHERE timestamp_col >= '2025-01-01' AND timestamp_col < '2025-01-02' -- Evitar ❌ WHERE timestamp_col BETWEEN '2025-01-01' AND '2025-01-02' ``` **Razones:** - `BETWEEN` incluye ambos extremos, lo que puede ser problemático para rangos de tiempo. - Usar `>=` y `<` es más claro para expresar rangos de tiempo. ## Transacciones ### Uso Explícito de Transacciones OxiCloud implementa transacciones explícitas para operaciones que deben ser atómicas: ```rust // Ejemplo de transacción explícita let mut tx = pool.begin().await?; // Operaciones dentro de la transacción sqlx::query("INSERT INTO users (id, username) VALUES ($1, $2)") .bind(id) .bind(username) .execute(&mut *tx) .await?; sqlx::query("INSERT INTO profiles (user_id, display_name) VALUES ($1, $2)") .bind(id) .bind(display_name) .execute(&mut *tx) .await?; // Confirmar la transacción tx.commit().await?; ``` ### Manejo de Errores en Transacciones Las transacciones incluyen manejo adecuado de errores con rollback automático: ```rust let result = sqlx::Transaction::begin(&pool).await.and_then(|mut tx| async move { // Operaciones dentro de la transacción let result1 = operation1(&mut tx).await?; let result2 = operation2(&mut tx).await?; // Confirmar la transacción si todo fue exitoso tx.commit().await?; Ok((result1, result2)) }).await; // Si ocurre un error, la transacción se revierte automáticamente if let Err(e) = &result { log::error!("Error en la transacción: {}", e); } ``` ## Migraciones y Gestión de Esquema ### Separación del Esquema del Código OxiCloud separa la definición del esquema del código de la aplicación: ``` OxiCloud/ ├── migrations/ # Archivos SQL de migración ├── src/ ├── bin/migrate.rs # Herramienta de migración ├── common/db.rs # Solo conecta a la BD, no crea esquema ``` ### Uso de Migraciones Versionadas Las migraciones siguen un formato versionado y se aplican secuencialmente: ``` 20250408000000_initial_schema.sql 20250408000001_default_users.sql ``` ## Seguridad ### Uso de Consultas Parametrizadas OxiCloud utiliza consultas parametrizadas para todas las operaciones SQL: ```rust // Recomendado ✅ sqlx::query("SELECT * FROM users WHERE username = $1") .bind(username) .fetch_one(&pool) .await?; // Evitar ❌ sqlx::query(&format!("SELECT * FROM users WHERE username = '{}'", username)) .fetch_one(&pool) .await?; ``` **Razones:** - Previene ataques de inyección SQL. - Permite la reutilización de planes de consulta. - Mejora el rendimiento general. ### Configuración de Autenticación Segura OxiCloud evita el uso de autenticación `trust` para conexiones TCP/IP: ``` # pg_hba.conf recomendado ✅ hostssl all all 0.0.0.0/0 scram-sha-256 # Evitar ❌ host all all 0.0.0.0/0 trust ``` ## Recursos Adicionales - [Wiki PostgreSQL - Don't Do This](https://wiki.postgresql.org/wiki/Don%27t_Do_This) - [Documentación oficial de PostgreSQL](https://www.postgresql.org/docs/) - [Guía de migraciones de OxiCloud](DATABASE-MIGRATIONS.md)