Files
civitai__civitai/scripts/prisma-prepare-programmability.mjs
T
Justin Maier 37ce3531d6 fix(db): remove dead metric trigger + harden programmability bootstrap (#3031)
* fix(db): make programmability apply fail loudly and deterministically

The db:program bootstrap applied all programmability SQL in one transaction whose catch block only console.error'd and let the process exit 0, so a single bad statement silently no-oped every function/view/trigger. It also relied on fs.readdir order, which is not guaranteed across platforms, to apply IIF.sql before views.sql (which calls iif() 315x).

Now: exit non-zero on failure (process.exitCode = 1), disconnect Prisma, and order files deterministically with helper functions (IIF/months_between/is_new_user) first and views.sql last.

Found during review of daceheg PR #2417.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(db): remove dead metrics_trigger.sql from programmability

add_model_metrics()/add_image_metrics() are legacy PG-trigger metric seeders. add_model_metrics INSERTs into ModelMetric(timeframe,...), but the ModelMetric overhaul (migration 20251123142214) dropped the timeframe column and made it one row per model; migration 20251124081808 then explicitly drops both the add_metrics_after_insert trigger on Model AND the add_model_metrics function. add_image_metrics targets the now-removed ImageMetric table and its own trigger is dropped at the end of the same file.

Re-CREATE-OR-REPLACE-ing them via db:program resurrected the dropped, now-broken trigger, making any Model INSERT throw at runtime during local bootstrap. ModelMetric rows are populated by the metrics processor upsert (src/server/metrics/model.metrics.ts:270, INSERT ... ON CONFLICT DO UPDATE), not this trigger, so the whole file is dead.

Found during review of daceheg PR #2417.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 15:59:25 -06:00

61 lines
1.9 KiB
JavaScript

import {PrismaClient} from '@prisma/client';
import fs from 'fs/promises';
const prisma = new PrismaClient();
const dir = './packages/civitai-db-schema/prisma/programmability';
// Everything is applied in a single transaction, so an object must be created
// before any other object that references it. iif() (IIF.sql) is used
// throughout views.sql, and the other helper functions may be referenced by
// later views/triggers, so force the standalone helper functions to the front
// and views.sql (which depends on iif()) to the back. Filesystem readdir order
// is not guaranteed across platforms, so make the rest deterministic too.
const applyFirst = ['IIF.sql', 'months_between.sql', 'is_new_user.sql'];
const applyLast = ['views.sql'];
function orderFiles(files) {
const middle = files
.filter((file) => !applyFirst.includes(file) && !applyLast.includes(file))
.sort();
return [
...applyFirst.filter((file) => files.includes(file)),
...middle,
...applyLast.filter((file) => files.includes(file)),
];
}
async function main() {
const files = orderFiles(await fs.readdir(dir));
const operations = [];
for (const file of files) {
const content = await fs.readFile(`${dir}/${file}`, 'utf-8');
const commands = content
.split('---')
.map((x) => x.trim())
.filter((x) => x);
commands.forEach((script, i) => operations.push({name: `${file}#${i}`, script}));
}
if (operations.length === 0) {
console.log('No scripts to apply');
return;
}
await prisma.$transaction(
operations.map(({name, script}) => {
console.log(`Applying ${name}...`);
return prisma.$executeRawUnsafe(script);
})
);
console.log(`Applied ${operations.length} statements from ${files.length} files`);
}
main()
.catch((err) => {
console.error(err);
process.exitCode = 1;
})
.finally(() => prisma.$disconnect());