import dbHandler from "../db-handler"; let schemaRunState = null; /** * Statements that discard stored data (tables or column values). */ export function isDestructiveSchemaStatement(query) { return /^\s*DROP\s+TABLE\b/i.test(query) || /\bDROP\s+COLUMN\b/i.test(query); } /** * Run `fn` with schema statements recorded but not executed. * Reads (information_schema) still run, so the plan is a close approximation: * steps that depend on earlier changes (e.g. after a table rename) may differ. */ export async function planSchemaQueries(fn) { const previous = schemaRunState; schemaRunState = { dryRun: true, statements: [], allowDestructive: true }; try { await fn(); return schemaRunState.statements; } finally { schemaRunState = previous; } } /** * Run `fn`, refusing any destructive statement that was not approved. */ export async function runSchemaQueriesGuarded(fn, allowDestructive) { const previous = schemaRunState; schemaRunState = { dryRun: false, statements: [], allowDestructive }; try { await fn(); } finally { schemaRunState = previous; } } export default async function runSchemaQuery({ query, values, config, }) { if (schemaRunState) { schemaRunState.statements.push(query); if (schemaRunState.dryRun) { return; } const allowed = schemaRunState.allowDestructive; if (allowed !== true && isDestructiveSchemaStatement(query) && !allowed.has(query)) { throw new Error(`Refusing unapproved destructive statement: ${query}. Re-run with --dry-run to review, or --yes to allow it.`); } } const res = await dbHandler({ query, values, config, }); if (!res.success) { throw new Error(`Database query failed: ${query} ... ERROR: ${res.error || res.msg}`); } } export async function querySchemaRows({ query, values, config, }) { const res = await dbHandler({ query, values, config, }); if (!res.success) { throw new Error(`Database query failed: ${query} ... ERROR: ${res.error || res.msg}`); } return (res.payload || []); }