-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.ts
More file actions
129 lines (115 loc) · 3.42 KB
/
errors.ts
File metadata and controls
129 lines (115 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import {
ConstraintError,
DeadlockError,
QuerySyntaxError,
SqlConnectionError,
SqlError,
type SqlErrorOptions,
} from "@probitas/client-sql";
/**
* PostgreSQL SQLSTATE class codes for error categorization.
*
* @see https://www.postgresql.org/docs/current/errcodes-appendix.html
*/
const SQLSTATE_CLASS = {
/** Syntax Error or Access Rule Violation */
SYNTAX_ERROR: "42",
/** Integrity Constraint Violation */
CONSTRAINT_VIOLATION: "23",
/** Transaction Rollback */
TRANSACTION_ROLLBACK: "40",
/** Connection Exception */
CONNECTION_EXCEPTION: "08",
/** Operator Intervention */
OPERATOR_INTERVENTION: "57",
} as const;
/**
* Specific SQLSTATE codes for fine-grained error handling.
*/
const SQLSTATE = {
/** Serialization failure (40001) */
SERIALIZATION_FAILURE: "40001",
/** Deadlock detected (40P01) */
DEADLOCK_DETECTED: "40P01",
} as const;
/**
* PostgreSQL error structure from the driver.
*/
export interface PostgresErrorLike {
readonly message: string;
readonly code?: string;
readonly constraint?: string;
}
/**
* Check if an error is a connection-level error based on SQLSTATE or error characteristics.
*/
export function isConnectionError(error: PostgresErrorLike): boolean {
const sqlState = error.code;
// Check SQLSTATE class
if (sqlState) {
if (sqlState.startsWith(SQLSTATE_CLASS.CONNECTION_EXCEPTION)) {
return true;
}
if (sqlState.startsWith(SQLSTATE_CLASS.OPERATOR_INTERVENTION)) {
return true;
}
}
// Check error message patterns for connection-related errors
const message = error.message.toLowerCase();
const connectionPatterns = [
"connection refused",
"connection reset",
"connection terminated",
"timed out",
"timeout",
"econnrefused",
"econnreset",
"etimedout",
"ehostunreach",
"enetunreach",
"socket",
"authentication failed",
"password authentication failed",
"pool",
];
return connectionPatterns.some((pattern) => message.includes(pattern));
}
/**
* Maps a PostgreSQL error to the appropriate SqlError subclass.
*
* @param error - PostgreSQL error from the driver
* @returns Mapped SqlError or subclass
*/
export function mapPostgresError(error: PostgresErrorLike): SqlError {
const sqlState = error.code;
const options: SqlErrorOptions = { sqlState, cause: error };
// Check for connection errors first
if (isConnectionError(error)) {
return new SqlConnectionError(error.message, options);
}
if (!sqlState) {
return new SqlError(error.message, "unknown", options);
}
// Check for deadlock and serialization errors first (class 40)
if (sqlState.startsWith(SQLSTATE_CLASS.TRANSACTION_ROLLBACK)) {
if (
sqlState === SQLSTATE.DEADLOCK_DETECTED ||
sqlState === SQLSTATE.SERIALIZATION_FAILURE
) {
return new DeadlockError(error.message, options);
}
// Other transaction rollback errors
return new SqlError(error.message, "unknown", options);
}
// Syntax errors (class 42)
if (sqlState.startsWith(SQLSTATE_CLASS.SYNTAX_ERROR)) {
return new QuerySyntaxError(error.message, options);
}
// Constraint violations (class 23)
if (sqlState.startsWith(SQLSTATE_CLASS.CONSTRAINT_VIOLATION)) {
const constraint = error.constraint ?? "unknown";
return new ConstraintError(error.message, constraint, options);
}
// Default to generic SqlError for unknown error types
return new SqlError(error.message, "unknown", options);
}