You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
senzapaura_es/scripts/db/probar-migracion.mjs

75 lines
2.6 KiB

Fase 2: el esquema entero en PostgreSQL 52 tablas nuevas, una vista y las guardas que Drizzle no sabe expresar. El sitio sigue leyendo de los archivos: esto solo crea el sitio donde caben. El esquema pasa de archivo a carpeta —con el catálogo dentro pasaría de las dos mil líneas—, y el corte se hizo por rangos de línea para no reescribir los comentarios que explican decisiones. Se comprobó que era un no-op: «No schema changes, nothing to migrate». Lo que Drizzle no genera va escrito a mano al final de la migración, y es justo lo que impide que el modelo mienta: la clave foránea de las versiones contra su propia tabla, dos columnas generadas y una clave compuesta que impiden a la vez la versión de una versión y los ciclos, la vista `cancion_principal`, el disparador que exige que el reparto de autoría sume cien, el que impide ciclos en el linaje de géneros, y los índices parciales del estilo principal y de la arista principal. Y las guardas se comprueban intentando lo que deben rechazar, en `scripts/db/comprobar-guardas.mjs`. Que una restricción exista en `pg_constraint` no significa que impida nada. Tres cosas que salieron de hacerlo y no de suponerlo: - `creado_en` era obligatorio SIN valor por defecto en la base: lo ponía JavaScript al insertar con Drizzle, así que cualquier INSERT escrito a mano fallaba y la invariante la sostenía la aplicación, no la tabla. Ahora es `defaultNow()`. - `drizzle-kit migrate` se atasca con los cuerpos `$$` de plpgsql. La migración se aplicó con `scripts/db/probar-migracion.mjs`, que además dice en qué sentencia falla, y se anotó en su registro. - Dos pruebas pasaban por el motivo equivocado: una por clave duplicada en vez de por la suma, y otra porque un disparador aplazado no salta dentro de un savepoint que nunca se cierra. Las dos corregidas; una guarda dada por buena sin ejecutarse es peor que no tenerla. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4 weeks ago
/**
* Ejecuta la migración en una transacción y la deshace.
*
* `drizzle-kit migrate` se queda colgado sin decir por qué, así que hay que
* preguntarle directamente a Postgres.
*
* El progreso se escribe a disco de forma SÍNCRONA. La salida normal de Node
* se acumula en un buffer cuando va a un pipe o a un archivo, y si el proceso
* se mata por tiempo ese buffer se pierde entero: es justo lo que hacía que
* pareciera que no llegaba a arrancar.
*/
import fs from 'node:fs';
import pg from 'pg';
const ARCHIVO = 'drizzle/0002_catalogo_en_la_base.sql';
const DIARIO = '.diario-migracion.txt';
const APLICAR = process.argv.includes('--aplicar');
fs.writeFileSync(DIARIO, '');
const apuntar = (linea) => fs.appendFileSync(DIARIO, linea + '\n');
const sql = fs.readFileSync(ARCHIVO, 'utf8');
const sentencias = sql
.split('--> statement-breakpoint')
.map((s) => s.trim())
// Sin expresión regular. `(--[^\n]*\n?)+` tarda catorce segundos en un
// comentario de quinientos caracteres: cuando no encaja, puede repartir el
// mismo texto de exponencialmente muchas formas antes de rendirse. Línea a
// línea es lineal, y además se lee.
.filter((s) => s && s.split('\n').some((l) => l.trim() && !l.trim().startsWith('--')));
apuntar(`${sentencias.length} sentencias`);
const cliente = new pg.Client({
connectionString: process.env.DATABASE_URL,
// Que no se quede esperando eternamente si el servidor no contesta.
connectionTimeoutMillis: 10_000,
query_timeout: 30_000
});
apuntar('conectando...');
await cliente.connect();
apuntar('conectado');
await cliente.query('BEGIN');
// Que falle rápido en vez de colgarse esperando un bloqueo que no llega.
await cliente.query("SET LOCAL lock_timeout = '5s'");
await cliente.query("SET LOCAL statement_timeout = '30s'");
apuntar('transacción abierta');
let hechas = 0;
let rota = null;
for (const [i, sentencia] of sentencias.entries()) {
const resumen = sentencia.split('\n').find((l) => l.trim() && !l.trim().startsWith('--')) ?? '';
apuntar(`[${i + 1}/${sentencias.length}] ${resumen.trim().slice(0, 80)}`);
try {
await cliente.query(sentencia);
hechas++;
} catch (fallo) {
rota = { i, sentencia, fallo };
apuntar(`\n FALLA: ${fallo.message}`);
if (fallo.hint) apuntar(` pista: ${fallo.hint}`);
if (fallo.position) apuntar(` posición: ${fallo.position}`);
apuntar('\n' + sentencia.slice(0, 600));
break;
}
}
const completa = !rota && hechas === sentencias.length;
await cliente.query(APLICAR && completa ? 'COMMIT' : 'ROLLBACK');
apuntar(completa ? `\nLas ${hechas} sentencias pasan.` : `\nParó en la ${hechas + 1}.`);
apuntar(APLICAR && completa ? 'APLICADA.' : '(deshecha)');
await cliente.end();

Powered by TurnKey Linux.