Files
edr-platform/.claude/skills/edr-db/query.cjs
2026-07-09 15:37:06 +00:00

89 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Dev-DB runner for the EDR freight database. psql is NOT installed on this
* machine; this is the sanctioned way to query, EXPLAIN-validate, and inspect
* the remote dev DB. Resolves `pg` from apps/edr-freight-api so it runs from
* anywhere in the repo.
*
* node .claude/skills/edr-db/query.cjs "SELECT ... " run SQL (console.table)
* node .claude/skills/edr-db/query.cjs explain "SELECT..." EXPLAIN-validate only
* node .claude/skills/edr-db/query.cjs columns <table> list freight.<table> columns
* node .claude/skills/edr-db/query.cjs migrations [like] public.migrations rows
* node .claude/skills/edr-db/query.cjs drift <table> columns vs entity check helper
*
* Connection: DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME env vars, falling
* back to the shared dev database.
*/
const path = require('path');
const { createRequire } = require('module');
const repoRoot = path.resolve(__dirname, '..', '..', '..');
const apiRequire = createRequire(
path.join(repoRoot, 'apps', 'edr-freight-api', 'package.json'),
);
const { Client } = apiRequire('pg');
const cfg = {
host: process.env.DB_HOST ?? '10.18.7.207',
port: parseInt(process.env.DB_PORT ?? '5432', 10),
user: process.env.DB_USER ?? 'postgres',
password: process.env.DB_PASSWORD ?? 'dcba@1234',
database: process.env.DB_NAME ?? 'edr_dev',
};
const [, , first, ...rest] = process.argv;
async function main() {
if (!first) {
console.error('usage: query.cjs "<sql>" | explain "<sql>" | columns <table> | migrations [like] | drift <table>');
process.exit(2);
}
const c = new Client(cfg);
await c.connect();
try {
if (first === 'columns') {
const r = await c.query(
`SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema='freight' AND table_name=$1
ORDER BY ordinal_position`,
[rest[0]],
);
console.table(r.rows);
} else if (first === 'migrations') {
const like = rest[0] ? `%${rest[0]}%` : '%';
const r = await c.query(
`SELECT id, timestamp, name FROM public.migrations
WHERE name ILIKE $1 ORDER BY id DESC LIMIT 40`,
[like],
);
console.table(r.rows);
} else if (first === 'drift') {
// Quick drift signal: DB columns for the table. Compare by eye against
// the entity's @Column names; a recorded-but-absent column = drift.
const r = await c.query(
`SELECT column_name FROM information_schema.columns
WHERE table_schema='freight' AND table_name=$1 ORDER BY column_name`,
[rest[0]],
);
console.log(r.rows.map((x) => x.column_name).join('\n'));
} else if (first === 'explain') {
await c.query('EXPLAIN ' + rest.join(' '));
console.log('OK — statement is valid against', cfg.database);
} else {
const sql = [first, ...rest].join(' ');
const started = Date.now();
const r = await c.query(sql);
if (Array.isArray(r.rows) && r.rows.length) console.table(r.rows);
console.log(`${r.rowCount ?? 0} row(s), ${Date.now() - started}ms`);
}
} finally {
await c.end();
}
}
main().catch((e) => {
console.error('FAIL:', e.message);
process.exit(1);
});