Fix: valida la edad del estudiante solo al alta y agrega índices faltantes
Qué
-
chk_edad_rangopasa de CHECK a trigger (validar_edad_estudiante): el rango de 18 a 100 años se exige solo al dar de alta al estudiante o al cambiarle la fecha de nacimiento. - Índices para las 10 FKs que no tenían.
- Índices en
auditoriapor fecha y por entidad.
Por qué
- El CHECK usaba
CURRENT_DATE, así que Postgres lo reevaluaba en cada UPDATE. Cuando un estudiante pasaba los 100 años, la base rechazaba cualquier cambio de su fila (editar datos, desactivarlo, etc.). Pasó en local con estudiantes cargados el 13/10 con 100 años justos. Es el mismo problema que ya se resolvió en recordatorios convalidar_fec_hora_futura, y se usa el mismo patrón. - Postgres no crea índices para las FKs: sin ellos, borrar o actualizar la fila referenciada (un rol, un funcionario, un permiso) recorre la tabla entera, y los listados por autor no tienen índice.
-
auditoriaes la tabla que más crece yAuditoriaServicesiempre la lista porfec_horadescendente, filtrando por entidad o autor. Solo tenía índice en la PK.
Cambios
-
proyecto_schema.sql:- Sale el CHECK
chk_edad_rangoy entran la funciónvalidar_edad_estudiantey el triggertrg_validar_edad_estudiante. El error mantiene el SQLSTATE23514(check_violation) y el nombrechk_edad_rango, así que el manejo de errores del backend no cambia. - Índices de FK nuevos:
idx_estudiantes_rol,idx_funcionarios_rol,idx_itr_carrera_carrera,idx_rol_permisos_permiso,idx_recordatorios_frecuencia,idx_estu_obs_comentarios_autor,idx_estu_obs_confidenciales_autor,idx_inst_comentarios_autor,idx_inst_com_confidenciales_autor. - Índices de auditoría nuevos:
idx_auditoria_fec_hora,idx_auditoria_entidad_fec_hora,idx_auditoria_usuario.
- Sale el CHECK
- Sin cambios en Java.
Cambios en BD
Correr en cada base existente (es idempotente, se puede correr más de una vez):
BEGIN;
ALTER TABLE proyecto.estudiantes DROP CONSTRAINT IF EXISTS chk_edad_rango;
CREATE OR REPLACE FUNCTION proyecto.validar_edad_estudiante()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'INSERT' OR NEW.fec_nacimiento IS DISTINCT FROM OLD.fec_nacimiento)
AND (NEW.fec_nacimiento > CURRENT_DATE - INTERVAL '18 years'
OR NEW.fec_nacimiento < CURRENT_DATE - INTERVAL '100 years') THEN
RAISE EXCEPTION 'El estudiante debe tener entre 18 y 100 años'
USING ERRCODE = 'check_violation', CONSTRAINT = 'chk_edad_rango';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_validar_edad_estudiante ON proyecto.estudiantes;
CREATE TRIGGER trg_validar_edad_estudiante
BEFORE INSERT OR UPDATE ON proyecto.estudiantes
FOR EACH ROW EXECUTE FUNCTION proyecto.validar_edad_estudiante();
CREATE INDEX IF NOT EXISTS idx_estudiantes_rol ON proyecto.estudiantes(id_rol);
CREATE INDEX IF NOT EXISTS idx_funcionarios_rol ON proyecto.funcionarios(id_rol);
CREATE INDEX IF NOT EXISTS idx_itr_carrera_carrera ON proyecto.itr_carrera(id_carrera);
CREATE INDEX IF NOT EXISTS idx_rol_permisos_permiso ON proyecto.rol_permisos(id_permiso);
CREATE INDEX IF NOT EXISTS idx_recordatorios_frecuencia ON proyecto.recordatorios(id_frecuencia);
CREATE INDEX IF NOT EXISTS idx_estu_obs_comentarios_autor ON proyecto.estu_obs_comentarios(id_autor);
CREATE INDEX IF NOT EXISTS idx_estu_obs_confidenciales_autor ON proyecto.estu_obs_confidenciales(id_autor);
CREATE INDEX IF NOT EXISTS idx_inst_comentarios_autor ON proyecto.inst_comentarios(id_autor);
CREATE INDEX IF NOT EXISTS idx_inst_com_confidenciales_autor ON proyecto.inst_com_confidenciales(id_autor);
CREATE INDEX IF NOT EXISTS idx_auditoria_fec_hora ON proyecto.auditoria(fec_hora DESC);
CREATE INDEX IF NOT EXISTS idx_auditoria_entidad_fec_hora ON proyecto.auditoria(entidad, fec_hora DESC);
CREATE INDEX IF NOT EXISTS idx_auditoria_usuario ON proyecto.auditoria(id_usuario);
COMMIT;
Cómo probar
- Correr el script (o recrear la base desde
proyecto_schema.sql). - Dar de alta un estudiante de 17 años: falla con "El estudiante debe tener entre 18 y 100 años".
- Dar de alta uno de 25: funciona. Cambiarle la fecha a una de 10 años atrás: falla.
- Con un estudiante que ya pasó los 100 años, editar otro dato (por ejemplo, la ciudad): ahora funciona.
- Esta consulta debe devolver 0 (FKs sin índice):
select count(*) from pg_constraint c where c.contype='f' and c.connamespace='proyecto'::regnamespace and not exists (select 1 from pg_index i where i.indrelid=c.conrelid and (i.indkey::int2[])[0:array_length(c.conkey,1)-1] @> c.conkey);
Notas
- Probado con una base nueva y con una base de
devmás el script (corrido dos veces): las dos quedan idénticas. -
chk_generacion_valida(grupos) también usa la fecha actual, pero con el tiempo solo se vuelve más permisivo, así que no rompe filas existentes y queda como está.