PostgreSQL y Supabase con IA: SQL, RLS y migraciones

La IA puede redactar SQL, explicar planes y proponer migraciones, pero la integridad, la autorización y la recuperación de los datos exigen decisiones verificables.

Por David Moya · · 18 min lectura

En este artículo

  1. SQL esencial
  2. PostgreSQL por defecto
  3. Supabase
  4. Row Level Security
  5. Migraciones
  6. IA para SQL
  7. JSONB
  8. Copias y optimización
  9. Preguntas frecuentes

SQL esencial para trabajar con IA sin ir a ciegas

La IA escribe consultas con rapidez, pero solo quien entiende el modelo puede decidir si responden a la pregunta correcta. SQL es declarativo: se describe el resultado y el motor elige un plan. Para trabajar con seguridad hay que dominar selección, filtros, uniones, agregaciones y modificaciones dentro de transacciones. También hay que reconocer la diferencia entre ausencia de valor, representada por NULL, y una cadena vacía o un cero.

SELECT p.id, p.name,
       COUNT(t.id) AS pending_tasks
FROM projects AS p
LEFT JOIN tasks AS t
  ON t.project_id = p.id
 AND t.status = 'pending'
WHERE p.owner_id = $1
GROUP BY p.id, p.name
ORDER BY pending_tasks DESC, p.name;

El filtro de estado está en la condición del LEFT JOIN. Si se moviera sin cuidado a WHERE, desaparecerían los proyectos sin tareas pendientes y la consulta dejaría de responder a la misma pregunta. Este tipo de diferencia semántica es justo lo que debe revisar una persona aunque la sintaxis venga de un asistente.

Las consultas parametrizadas separan datos y código SQL. Nunca se interpolan valores del usuario ni texto generado por un modelo. Para escribir se usan restricciones y transacciones: una clave foránea evita referencias huérfanas; un CHECK limita estados; una restricción única expresa identidad. La base de datos debe rechazar estados imposibles aunque una ruta de aplicación olvide validarlos.

Transacciones y concurrencia

BEGIN;
SELECT balance FROM accounts WHERE id = $1 FOR UPDATE;
UPDATE accounts SET balance = balance - $2 WHERE id = $1;
UPDATE accounts SET balance = balance + $2 WHERE id = $3;
COMMIT;

Una transacción agrupa cambios que deben ocurrir juntos. Los bloqueos y el nivel de aislamiento determinan qué puede observar una operación concurrente. La IA puede sugerir un bloque correcto en apariencia, pero no conoce automáticamente qué conflictos acepta el negocio. Para procesos sensibles se diseñan invariantes, reintentos acotados e idempotencia.

PostgreSQL como opción por defecto

PostgreSQL ofrece transacciones, integridad referencial, tipos ricos, extensiones y herramientas maduras en un único motor. Es una elección razonable para aplicaciones web, sistemas internos y productos con relaciones complejas. «Por defecto» no significa «para todo»: una base embebida puede encajar mejor en una aplicación local y un almacén especializado puede resolver una carga muy concreta. La decisión debe partir de consultas y operación, no de la novedad del proveedor.

Su planificador decide cómo ejecutar una consulta según estadísticas e índices. EXPLAIN muestra el plan estimado y EXPLAIN ANALYZE ejecuta la consulta y mide lo ocurrido, por lo que debe usarse con cuidado en escrituras o entornos reales. Los índices ayudan a búsquedas y ordenaciones selectivas, pero ocupan espacio y encarecen cada inserción o actualización. Se crean para patrones observados, no para cada columna.

CREATE INDEX idx_tasks_project_pending
ON tasks (project_id, created_at DESC)
WHERE status = 'pending';

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, title
FROM tasks
WHERE project_id = $1 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 20;

Supabase: PostgreSQL con servicios alrededor

Supabase combina PostgreSQL con autenticación, API de datos, almacenamiento y otras piezas que reducen trabajo de integración. La base sigue siendo PostgreSQL: se pueden usar restricciones, funciones, transacciones e índices. Esa continuidad evita depender de una abstracción cerrada y permite inspeccionar el SQL que sostiene la aplicación.

El cliente público se diseña para operar bajo políticas RLS. Una credencial de servicio con privilegios amplios pertenece exclusivamente a procesos de servidor controlados. No se coloca en JavaScript del navegador, aplicaciones móviles, repositorios ni herramientas compartidas. Supabase no sustituye el diseño de autorización: facilita aplicar reglas cerca de los datos, pero las políticas deben modelar bien actor, tenant y operación.

ComponenteResponsabilidadPrecaución
AuthIdentidad y sesiónLa identidad no concede acceso por sí sola
PostgRESTAPI sobre tablas y vistasExponer solo esquemas y columnas necesarios
StorageObjetos y metadatosPolíticas coherentes con propietarios
Service roleProcesos privilegiadosSolo servidor; puede eludir RLS

Row Level Security: autorización dentro de la base

RLS hace que PostgreSQL evalúe políticas por fila para las operaciones de un rol. Es valioso en aplicaciones multiusuario porque una consulta olvidada no debería devolver datos de otra cuenta. Primero se activa RLS y después se crean políticas explícitas para lectura, inserción, actualización y borrado. Una política demasiado amplia puede ser tan peligrosa como no tener ninguna.

ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

CREATE POLICY tasks_select_own
ON tasks FOR SELECT
USING (owner_id = auth.uid());

CREATE POLICY tasks_insert_own
ON tasks FOR INSERT
WITH CHECK (owner_id = auth.uid());

CREATE POLICY tasks_update_own
ON tasks FOR UPDATE
USING (owner_id = auth.uid())
WITH CHECK (owner_id = auth.uid());

USING controla qué filas existentes son visibles o modificables; WITH CHECK controla el estado nuevo que se intenta crear. Usar solo la primera puede permitir mover una fila a un propietario no autorizado. En un sistema multi-tenant suele comprobarse pertenencia mediante una tabla de miembros, pero esa función debe evitar recursión y mantener un plan eficiente.

Cómo probar políticas

Las políticas se prueban con identidades distintas: propietario, miembro, usuario ajeno y sesión anónima. Para cada rol se ejercitan SELECT, INSERT, UPDATE y DELETE, incluidos cambios de propietario. Las pruebas realizadas únicamente con una credencial que elude RLS dan una falsa sensación de seguridad. También se revisan vistas, funciones con privilegios y buckets de almacenamiento, pues pueden abrir rutas alternativas.

Migraciones: cambios reproducibles y reversibles

Una migración es código versionado que lleva el esquema de un estado conocido al siguiente. Se genera en desarrollo, se revisa como cualquier cambio y se ejecuta primero en un entorno no productivo. Editar manualmente la base de producción rompe la trazabilidad: el repositorio deja de describir el estado real y el siguiente despliegue se vuelve incierto.

-- 001_add_task_due_at.sql
ALTER TABLE tasks ADD COLUMN due_at timestamptz;
CREATE INDEX idx_tasks_due_at
ON tasks (due_at)
WHERE due_at IS NOT NULL AND status != 'done';

Los cambios grandes se dividen. Para hacer obligatoria una columna en una tabla con datos, primero se añade como nullable, se despliega código compatible, se rellena por lotes, se valida y finalmente se aplica NOT NULL. Renombrar o borrar exige comprobar consumidores y copias de seguridad. Una reversión no siempre es un SQL inverso: si se ha perdido información, la estrategia real es restaurar o avanzar con una corrección.

IA para generar y validar SQL

El mejor prompt para SQL incluye el esquema relevante, claves, cardinalidad aproximada, resultado esperado y ejemplos límite. No se entrega toda la base si bastan dos tablas. Se pide una consulta parametrizada y una explicación de supuestos. Después se valida sintaxis en una base aislada, se ejecutan casos conocidos y se inspecciona el plan.

Esquema: projects(id, owner_id, name)
tasks(id, project_id, status, created_at)
Necesito todos los proyectos del propietario, incluidos los que no
contienen tareas pendientes. Devuelve el recuento pendiente.
Usa parámetros PostgreSQL. No modifiques datos.
Explica cómo conserva proyectos con recuento cero.

Para escrituras generadas por IA se exige todavía más disciplina: transacción, límite de filas, copia de seguridad cuando corresponda y revisión humana. El modelo no ejecuta directamente contra producción. Un rol de análisis es de solo lectura, tiene timeout y acceso únicamente al esquema necesario. Las consultas se registran sin credenciales ni datos sensibles.

Lista de validación de una consulta

JSONB: flexibilidad con límites

jsonb almacena documentos binarios consultables e indexables. Es útil para metadatos variables, payloads externos o configuraciones cuya forma evoluciona. No debería sustituir columnas para campos esenciales que participan en relaciones, restricciones y consultas frecuentes. Si todos los registros tienen status, owner_id y created_at, esas propiedades merecen columnas tipadas.

CREATE TABLE events (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  tenant_id uuid NOT NULL,
  kind text NOT NULL,
  payload jsonb NOT NULL CHECK (jsonb_typeof(payload) = 'object'),
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX idx_events_payload_gin ON events USING gin (payload);

SELECT id, payload->>'source' AS source
FROM events
WHERE tenant_id = $1
  AND payload @> '{"severity":"high"}'::jsonb;

Los índices GIN permiten buscar contenido, pero también tienen coste de escritura. Para una propiedad JSON consultada constantemente puede resultar mejor una columna generada o normalizada. La aplicación valida la estructura antes de guardar, porque el tipo jsonb por sí solo no garantiza que aparezcan las claves esperadas.

Copias, recuperación y optimización responsable

Una copia no está verificada hasta que se restaura. El plan define frecuencia, retención, cifrado, ubicación separada y responsables. También establece cuánto dato puede perderse y cuánto tiempo puede estar el servicio detenido. Las migraciones destructivas y los incidentes requieren un procedimiento ensayado, no la esperanza de que el proveedor conserve todo.

La optimización comienza midiendo consultas lentas y tiempos reales. Se actualizan estadísticas, se revisan planes, se reducen filas leídas y se añaden índices concretos. La IA puede explicar un plan y proponer alternativas, pero cada propuesta se compara en datos representativos. La consulta más corta no siempre es la más rápida y el índice que mejora una lectura puede empeorar una carga de escritura.

Una base sólida combina SQL entendido, restricciones, autorización por fila, migraciones reproducibles y operación recuperable. La IA acelera borradores y análisis, pero la responsabilidad de los datos sigue siendo del sistema y su equipo. El módulo de Bases de datos e IA ofrece ejercicios para llevar estas decisiones a PostgreSQL y Supabase.

Preguntas frecuentes

¿Por qué elegir PostgreSQL como base de datos por defecto?

Porque combina transacciones, integridad, relaciones, tipos avanzados y operación madura. Debe sustituirse solo cuando una necesidad concreta justifique otro motor.

¿RLS reemplaza la autorización de la aplicación?

La refuerza cerca de los datos, pero no elimina la autenticación, las reglas de negocio ni la revisión de funciones y credenciales privilegiadas.

¿Cuál es la diferencia entre USING y WITH CHECK?

USING limita las filas existentes que una operación puede ver o modificar. WITH CHECK valida el estado nuevo que se intenta insertar o dejar tras una actualización.

¿Puede la IA ejecutar SQL en producción?

No debería hacerlo directamente. Se genera y prueba en un entorno aislado, se revisa el alcance y se ejecuta con roles y controles acordes al riesgo.

¿Cuándo usar JSONB en lugar de columnas?

Para metadatos variables o payloads externos. Los campos esenciales, relacionados y consultados con frecuencia suelen funcionar mejor como columnas tipadas.

¿Cómo saber si una migración es segura?

Debe ser reproducible, revisada, probada con datos representativos y compatible con el despliegue. Para cambios destructivos necesita copia y estrategia de recuperación.

📚 Aprende más en el curso

Este artículo complementa el Módulo M15: Bases de datos e IA. Incluye vídeo, quiz, flashcards con repaso espaciado y proyecto práctico.

Ir al módulo M15Repasar con FSRS