mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
37ce3531d6
* 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>
61 lines
1.9 KiB
JavaScript
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());
|