Files
vexplor_dev/backend-node/src/utils/pgErrorUtil.ts
kjs 3982aabc24 refactor: Enhance unique constraint validation across data operations
- Integrated `TableManagementService` to validate unique constraints before insert, update, and upsert actions in various controllers, including `dataflowExecutionController`, `dynamicFormController`, and `tableManagementController`.
- Improved error handling in `errorHandler` to provide detailed messages indicating which field has a unique constraint violation.
- Updated the `formatPgError` utility to extract and display specific column labels for unique constraint violations, enhancing user feedback.
- Adjusted the table schema retrieval to include company-specific nullable and unique constraints, ensuring accurate representation of database rules.

These changes improve data integrity by preventing duplicate entries and enhance user experience through clearer error messages related to unique constraints.
2026-03-10 16:15:20 +09:00

81 lines
2.7 KiB
TypeScript

import { query } from "../database/db";
/**
* PostgreSQL 에러를 사용자 친절한 메시지로 변환
* table_type_columns의 column_label을 조회하여 한글 라벨로 표시
*/
export async function formatPgError(
error: any,
companyCode?: string
): Promise<string> {
if (!error || !error.code) {
return error?.message || "데이터 처리 중 오류가 발생했습니다.";
}
switch (error.code) {
case "23502": {
// not_null_violation
const colName = error.column || "";
const tblName = error.table || "";
if (colName && tblName && companyCode) {
try {
const rows = await query(
`SELECT column_label FROM table_type_columns
WHERE table_name = $1 AND column_name = $2 AND company_code = $3
LIMIT 1`,
[tblName, colName, companyCode]
);
const label = rows[0]?.column_label;
if (label) {
return `필수 입력값이 누락되었습니다: ${label}`;
}
} catch {
// 라벨 조회 실패 시 컬럼명으로 폴백
}
}
const detail = colName ? ` [${colName}]` : "";
return `필수 입력값이 누락되었습니다.${detail}`;
}
case "23505": {
// unique_violation
const constraint = error.constraint || "";
const tblName = error.table || "";
// constraint 이름에서 컬럼명 추출 시도 (예: item_mst_item_code_key → item_code)
let colName = "";
if (constraint && tblName) {
const prefix = `${tblName}_`;
const suffix = "_key";
if (constraint.startsWith(prefix) && constraint.endsWith(suffix)) {
colName = constraint.slice(prefix.length, -suffix.length);
}
}
if (colName && tblName && companyCode) {
try {
const rows = await query(
`SELECT column_label FROM table_type_columns
WHERE table_name = $1 AND column_name = $2 AND company_code = $3
LIMIT 1`,
[tblName, colName, companyCode]
);
const label = rows[0]?.column_label;
if (label) {
return `중복된 데이터가 존재합니다: ${label}`;
}
} catch {
// 폴백
}
}
const detail = colName ? ` [${colName}]` : "";
return `중복된 데이터가 존재합니다.${detail}`;
}
case "23503":
return "참조 무결성 제약 조건 위반입니다.";
default:
if (error.code.startsWith("23")) {
return "데이터 무결성 제약 조건 위반입니다.";
}
return error.message || "데이터 처리 중 오류가 발생했습니다.";
}
}