53 lines
2.1 KiB
TypeScript
53 lines
2.1 KiB
TypeScript
const UNIQUE_VIOLATION = "23505";
|
|
const FOREIGN_KEY_VIOLATION = "23503";
|
|
const MAX_CAUSE_DEPTH = 4;
|
|
|
|
/**
|
|
* Recognizes a Postgres unique-constraint violation (SQLSTATE 23505).
|
|
*
|
|
* Drizzle wraps driver failures in its own `Failed query: ...` error, so the
|
|
* Postgres error that carries the code and the constraint name is reachable
|
|
* only through `cause` — inspecting the thrown error directly misses it. The
|
|
* constraint name itself lands on `constraint_name` under postgres.js and on
|
|
* `constraint` under node-postgres, and is not always surfaced at all, so fall
|
|
* back to the driver message.
|
|
*/
|
|
export function isUniqueViolation(error: unknown, constraintName?: string): boolean {
|
|
let current: unknown = error;
|
|
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current && typeof current === "object"; depth += 1) {
|
|
const candidate = current as {
|
|
code?: unknown;
|
|
constraint?: unknown;
|
|
constraint_name?: unknown;
|
|
message?: unknown;
|
|
cause?: unknown;
|
|
};
|
|
if (candidate.code === UNIQUE_VIOLATION) {
|
|
if (!constraintName) return true;
|
|
const constraint = candidate.constraint ?? candidate.constraint_name;
|
|
if (constraint === constraintName) return true;
|
|
if (typeof candidate.message === "string" && candidate.message.includes(constraintName)) return true;
|
|
}
|
|
current = candidate.cause;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Recognizes a Postgres foreign-key-constraint violation (SQLSTATE 23503).
|
|
*
|
|
* A delete that leaves an orphan reference raises this code. Drizzle wraps the
|
|
* driver failure in its own `Failed query: ...` error, so the Postgres error
|
|
* that carries the code is reachable only through `cause`. This helper walks
|
|
* the `cause` chain, the same way `isUniqueViolation` does.
|
|
*/
|
|
export function isForeignKeyViolation(error: unknown): boolean {
|
|
let current: unknown = error;
|
|
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current && typeof current === "object"; depth += 1) {
|
|
const candidate = current as { code?: unknown; cause?: unknown };
|
|
if (candidate.code === FOREIGN_KEY_VIOLATION) return true;
|
|
current = candidate.cause;
|
|
}
|
|
return false;
|
|
}
|