@effect/sql-pg 4.0.0-beta.6 → 4.0.0-beta.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/PgClient.d.ts +59 -19
- package/dist/PgClient.d.ts.map +1 -1
- package/dist/PgClient.js +347 -185
- package/dist/PgClient.js.map +1 -1
- package/dist/PgMigrator.d.ts +8 -4
- package/dist/PgMigrator.d.ts.map +1 -1
- package/dist/PgMigrator.js +49 -56
- package/dist/PgMigrator.js.map +1 -1
- package/package.json +9 -9
- package/src/PgClient.ts +540 -253
- package/src/PgMigrator.ts +74 -60
package/src/PgClient.ts
CHANGED
|
@@ -5,21 +5,36 @@ import * as Arr from "effect/Array"
|
|
|
5
5
|
import * as Cause from "effect/Cause"
|
|
6
6
|
import * as Channel from "effect/Channel"
|
|
7
7
|
import * as Config from "effect/Config"
|
|
8
|
+
import * as Context from "effect/Context"
|
|
8
9
|
import * as Duration from "effect/Duration"
|
|
9
10
|
import * as Effect from "effect/Effect"
|
|
10
11
|
import * as Fiber from "effect/Fiber"
|
|
11
12
|
import * as Layer from "effect/Layer"
|
|
12
13
|
import * as Number from "effect/Number"
|
|
14
|
+
import * as Option from "effect/Option"
|
|
13
15
|
import * as Queue from "effect/Queue"
|
|
14
16
|
import * as RcRef from "effect/RcRef"
|
|
15
17
|
import * as Redacted from "effect/Redacted"
|
|
16
18
|
import * as Scope from "effect/Scope"
|
|
17
|
-
import * as
|
|
19
|
+
import * as Semaphore from "effect/Semaphore"
|
|
18
20
|
import * as Stream from "effect/Stream"
|
|
19
21
|
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
|
|
20
22
|
import * as Client from "effect/unstable/sql/SqlClient"
|
|
21
23
|
import type { Connection } from "effect/unstable/sql/SqlConnection"
|
|
22
|
-
import
|
|
24
|
+
import type * as SqlConnection from "effect/unstable/sql/SqlConnection"
|
|
25
|
+
import {
|
|
26
|
+
AuthenticationError,
|
|
27
|
+
AuthorizationError,
|
|
28
|
+
ConnectionError,
|
|
29
|
+
ConstraintError,
|
|
30
|
+
DeadlockError,
|
|
31
|
+
LockTimeoutError,
|
|
32
|
+
SerializationError,
|
|
33
|
+
SqlError,
|
|
34
|
+
SqlSyntaxError,
|
|
35
|
+
StatementTimeoutError,
|
|
36
|
+
UnknownError
|
|
37
|
+
} from "effect/unstable/sql/SqlError"
|
|
23
38
|
import type { Custom, Fragment } from "effect/unstable/sql/Statement"
|
|
24
39
|
import * as Statement from "effect/unstable/sql/Statement"
|
|
25
40
|
import type { Duplex } from "node:stream"
|
|
@@ -28,11 +43,6 @@ import * as Pg from "pg"
|
|
|
28
43
|
import * as PgConnString from "pg-connection-string"
|
|
29
44
|
import Cursor from "pg-cursor"
|
|
30
45
|
|
|
31
|
-
const ATTR_DB_SYSTEM_NAME = "db.system.name"
|
|
32
|
-
const ATTR_DB_NAMESPACE = "db.namespace"
|
|
33
|
-
const ATTR_SERVER_ADDRESS = "server.address"
|
|
34
|
-
const ATTR_SERVER_PORT = "server.port"
|
|
35
|
-
|
|
36
46
|
/**
|
|
37
47
|
* @category type ids
|
|
38
48
|
* @since 1.0.0
|
|
@@ -61,7 +71,7 @@ export interface PgClient extends Client.SqlClient {
|
|
|
61
71
|
* @category tags
|
|
62
72
|
* @since 1.0.0
|
|
63
73
|
*/
|
|
64
|
-
export const PgClient =
|
|
74
|
+
export const PgClient = Context.Service<PgClient>("@effect/sql-pg/PgClient")
|
|
65
75
|
|
|
66
76
|
/**
|
|
67
77
|
* @category constructors
|
|
@@ -78,14 +88,9 @@ export interface PgClientConfig {
|
|
|
78
88
|
readonly username?: string | undefined
|
|
79
89
|
readonly password?: Redacted.Redacted | undefined
|
|
80
90
|
|
|
81
|
-
readonly
|
|
82
|
-
|
|
83
|
-
readonly idleTimeout?: Duration.DurationInput | undefined
|
|
84
|
-
readonly connectTimeout?: Duration.DurationInput | undefined
|
|
91
|
+
readonly connectTimeout?: Duration.Input | undefined
|
|
85
92
|
|
|
86
|
-
readonly
|
|
87
|
-
readonly minConnections?: number | undefined
|
|
88
|
-
readonly connectionTTL?: Duration.DurationInput | undefined
|
|
93
|
+
readonly stream?: (() => Duplex) | undefined
|
|
89
94
|
|
|
90
95
|
readonly applicationName?: string | undefined
|
|
91
96
|
readonly spanAttributes?: Record<string, unknown> | undefined
|
|
@@ -100,9 +105,19 @@ export interface PgClientConfig {
|
|
|
100
105
|
* @category constructors
|
|
101
106
|
* @since 1.0.0
|
|
102
107
|
*/
|
|
103
|
-
export
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
export interface PgPoolConfig extends PgClientConfig {
|
|
109
|
+
readonly idleTimeout?: Duration.Input | undefined
|
|
110
|
+
|
|
111
|
+
readonly maxConnections?: number | undefined
|
|
112
|
+
readonly minConnections?: number | undefined
|
|
113
|
+
readonly connectionTTL?: Duration.Input | undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @category constructors
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
export const make = (options: PgPoolConfig): Effect.Effect<PgClient, SqlError, Scope.Scope | Reactivity.Reactivity> =>
|
|
106
121
|
fromPool({
|
|
107
122
|
...options,
|
|
108
123
|
acquire: Effect.gen(function*() {
|
|
@@ -116,15 +131,15 @@ export const make = (
|
|
|
116
131
|
port: options.port,
|
|
117
132
|
...(options.stream ? { stream: options.stream } : {}),
|
|
118
133
|
connectionTimeoutMillis: options.connectTimeout
|
|
119
|
-
? Duration.toMillis(Duration.
|
|
134
|
+
? Duration.toMillis(Duration.fromInputUnsafe(options.connectTimeout))
|
|
120
135
|
: undefined,
|
|
121
136
|
idleTimeoutMillis: options.idleTimeout
|
|
122
|
-
? Duration.toMillis(Duration.
|
|
137
|
+
? Duration.toMillis(Duration.fromInputUnsafe(options.idleTimeout))
|
|
123
138
|
: undefined,
|
|
124
139
|
max: options.maxConnections,
|
|
125
140
|
min: options.minConnections,
|
|
126
141
|
maxLifetimeSeconds: options.connectionTTL
|
|
127
|
-
? Duration.toSeconds(Duration.
|
|
142
|
+
? Duration.toSeconds(Duration.fromInputUnsafe(options.connectionTTL))
|
|
128
143
|
: undefined,
|
|
129
144
|
application_name: options.applicationName ?? "@effect/sql-pg",
|
|
130
145
|
types: options.types
|
|
@@ -135,20 +150,24 @@ export const make = (
|
|
|
135
150
|
yield* Effect.acquireRelease(
|
|
136
151
|
Effect.tryPromise({
|
|
137
152
|
try: () => pool.query("SELECT 1"),
|
|
138
|
-
catch: (cause) => new SqlError({ cause,
|
|
153
|
+
catch: (cause) => new SqlError({ reason: classifyError(cause, "PgClient: Failed to connect", "connect") })
|
|
139
154
|
}),
|
|
140
155
|
() =>
|
|
141
156
|
Effect.promise(() => pool.end()).pipe(
|
|
142
157
|
Effect.timeoutOption(1000)
|
|
143
|
-
)
|
|
158
|
+
),
|
|
159
|
+
{ interruptible: true }
|
|
144
160
|
).pipe(
|
|
145
161
|
Effect.timeoutOrElse({
|
|
146
162
|
duration: options.connectTimeout ?? Duration.seconds(5),
|
|
147
|
-
|
|
163
|
+
orElse: () =>
|
|
148
164
|
Effect.fail(
|
|
149
165
|
new SqlError({
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
reason: new ConnectionError({
|
|
167
|
+
cause: new Error("Connection timed out"),
|
|
168
|
+
message: "PgClient: Connection timed out",
|
|
169
|
+
operation: "connect"
|
|
170
|
+
})
|
|
152
171
|
})
|
|
153
172
|
)
|
|
154
173
|
})
|
|
@@ -158,6 +177,64 @@ export const make = (
|
|
|
158
177
|
})
|
|
159
178
|
})
|
|
160
179
|
|
|
180
|
+
/**
|
|
181
|
+
* @category constructors
|
|
182
|
+
* @since 1.0.0
|
|
183
|
+
*/
|
|
184
|
+
export const makeClient = (
|
|
185
|
+
options: PgClientConfig & {
|
|
186
|
+
/**
|
|
187
|
+
* Whether to acquire a separate client for each sql.stream / sql.listen
|
|
188
|
+
*/
|
|
189
|
+
readonly acquireForStream?: boolean | undefined
|
|
190
|
+
}
|
|
191
|
+
): Effect.Effect<PgClient, SqlError, Scope.Scope | Reactivity.Reactivity> =>
|
|
192
|
+
fromClient({
|
|
193
|
+
...options,
|
|
194
|
+
acquire: Effect.gen(function*() {
|
|
195
|
+
const client = new Pg.Client({
|
|
196
|
+
connectionString: options.url ? Redacted.value(options.url) : undefined,
|
|
197
|
+
user: options.username,
|
|
198
|
+
host: options.host,
|
|
199
|
+
database: options.database,
|
|
200
|
+
password: options.password ? Redacted.value(options.password) : undefined,
|
|
201
|
+
ssl: options.ssl,
|
|
202
|
+
port: options.port,
|
|
203
|
+
...(options.stream ? { stream: options.stream } : {}),
|
|
204
|
+
application_name: options.applicationName ?? "@effect/sql-pg",
|
|
205
|
+
types: options.types
|
|
206
|
+
})
|
|
207
|
+
yield* Effect.acquireRelease(
|
|
208
|
+
Effect.tryPromise({
|
|
209
|
+
try: () => client.query("SELECT 1"),
|
|
210
|
+
catch: (cause) => new SqlError({ reason: classifyError(cause, "PgClient: Failed to connect", "connect") })
|
|
211
|
+
}),
|
|
212
|
+
() =>
|
|
213
|
+
Effect.promise(() => client.end()).pipe(
|
|
214
|
+
Effect.timeoutOption(1000)
|
|
215
|
+
),
|
|
216
|
+
{ interruptible: true }
|
|
217
|
+
).pipe(
|
|
218
|
+
Effect.timeoutOrElse({
|
|
219
|
+
duration: options.connectTimeout ?? Duration.seconds(5),
|
|
220
|
+
orElse: () =>
|
|
221
|
+
Effect.fail(
|
|
222
|
+
new SqlError({
|
|
223
|
+
reason: new ConnectionError({
|
|
224
|
+
cause: new Error("Connection timed out"),
|
|
225
|
+
message: "PgClient: Connection timed out",
|
|
226
|
+
operation: "connect"
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
)
|
|
230
|
+
})
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return client
|
|
234
|
+
}),
|
|
235
|
+
acquireForStream: options.acquireForStream ?? false
|
|
236
|
+
})
|
|
237
|
+
|
|
161
238
|
/**
|
|
162
239
|
* @category constructors
|
|
163
240
|
* @since 1.0.0
|
|
@@ -175,197 +252,149 @@ export const fromPool = Effect.fnUntraced(function*(
|
|
|
175
252
|
readonly types?: Pg.CustomTypesConfig | undefined
|
|
176
253
|
}
|
|
177
254
|
): Effect.fn.Return<PgClient, SqlError, Scope.Scope | Reactivity.Reactivity> {
|
|
178
|
-
const compiler = makeCompiler(
|
|
179
|
-
options.transformQueryNames,
|
|
180
|
-
options.transformJson
|
|
181
|
-
)
|
|
182
|
-
const transformRows = options.transformResultNames ?
|
|
183
|
-
Statement.defaultTransforms(
|
|
184
|
-
options.transformResultNames,
|
|
185
|
-
options.transformJson
|
|
186
|
-
).array :
|
|
187
|
-
undefined
|
|
188
|
-
|
|
189
255
|
const pool = yield* options.acquire
|
|
190
256
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (this.pg !== undefined) {
|
|
199
|
-
return Effect.callback<A, SqlError>((resume) => {
|
|
200
|
-
f(this.pg!, resume)
|
|
201
|
-
return makeCancel(pool, this.pg!)
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
return Effect.callback<A, SqlError>((resume) => {
|
|
205
|
-
let done = false
|
|
206
|
-
let cancel: Effect.Effect<void> | undefined = undefined
|
|
207
|
-
let client: Pg.PoolClient | undefined = undefined
|
|
208
|
-
function onError(cause: Error) {
|
|
209
|
-
cleanup(cause)
|
|
210
|
-
resume(Effect.fail(new SqlError({ cause, message: "Connection error" })))
|
|
211
|
-
}
|
|
212
|
-
function cleanup(cause?: Error) {
|
|
213
|
-
if (!done) client?.release(cause)
|
|
214
|
-
done = true
|
|
215
|
-
client?.off("error", onError)
|
|
216
|
-
}
|
|
217
|
-
pool.connect((cause, client_) => {
|
|
218
|
-
if (cause) {
|
|
219
|
-
return resume(Effect.fail(new SqlError({ cause, message: "Failed to acquire connection" })))
|
|
220
|
-
} else if (!client_) {
|
|
221
|
-
return resume(
|
|
222
|
-
Effect.fail(
|
|
223
|
-
new SqlError({ message: "Failed to acquire connection", cause: new Error("No client returned") })
|
|
224
|
-
)
|
|
225
|
-
)
|
|
226
|
-
} else if (done) {
|
|
227
|
-
client_.release()
|
|
228
|
-
return
|
|
229
|
-
}
|
|
230
|
-
client = client_
|
|
231
|
-
client.once("error", onError)
|
|
232
|
-
cancel = makeCancel(pool, client)
|
|
233
|
-
f(client, (eff) => {
|
|
234
|
-
cleanup()
|
|
235
|
-
resume(eff)
|
|
257
|
+
const makeConection = (client?: Pg.PoolClient) =>
|
|
258
|
+
new ConnectionImpl(
|
|
259
|
+
function runWithClient<A>(f: (client: Pg.ClientBase, resume: (_: Effect.Effect<A, SqlError>) => void) => void) {
|
|
260
|
+
if (client !== undefined) {
|
|
261
|
+
return Effect.callback<A, SqlError>((resume) => {
|
|
262
|
+
f(client!, resume)
|
|
263
|
+
return makeCancel(pool, client!)
|
|
236
264
|
})
|
|
237
|
-
}
|
|
238
|
-
return Effect.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private run(query: string, params: ReadonlyArray<unknown>) {
|
|
249
|
-
return this.runWithClient<ReadonlyArray<any>>((client, resume) => {
|
|
250
|
-
client.query(query, params as any, (err, result) => {
|
|
251
|
-
if (err) {
|
|
252
|
-
resume(Effect.fail(new SqlError({ cause: err, message: "Failed to execute statement" })))
|
|
253
|
-
} else {
|
|
254
|
-
// Multi-statement queries return an array of results
|
|
255
|
-
resume(Effect.succeed(
|
|
256
|
-
Array.isArray(result)
|
|
257
|
-
? result.map((r) => r.rows ?? [])
|
|
258
|
-
: result.rows ?? []
|
|
259
|
-
))
|
|
265
|
+
}
|
|
266
|
+
return Effect.callback<A, SqlError>((resume) => {
|
|
267
|
+
let done = false
|
|
268
|
+
let cancel: Effect.Effect<void> | undefined = undefined
|
|
269
|
+
let client: Pg.PoolClient | undefined = undefined
|
|
270
|
+
function onError(cause: Error) {
|
|
271
|
+
cleanup(cause)
|
|
272
|
+
resume(Effect.fail(new SqlError({ reason: classifyError(cause, "Connection error", "acquireConnection") })))
|
|
260
273
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
execute(
|
|
266
|
-
sql: string,
|
|
267
|
-
params: ReadonlyArray<unknown>,
|
|
268
|
-
transformRows: (<A extends object>(row: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined
|
|
269
|
-
) {
|
|
270
|
-
return transformRows
|
|
271
|
-
? Effect.map(this.run(sql, params), transformRows)
|
|
272
|
-
: this.run(sql, params)
|
|
273
|
-
}
|
|
274
|
-
executeRaw(sql: string, params: ReadonlyArray<unknown>) {
|
|
275
|
-
return this.runWithClient<Pg.Result>((client, resume) => {
|
|
276
|
-
client.query(sql, params as any, (err, result) => {
|
|
277
|
-
if (err) {
|
|
278
|
-
resume(Effect.fail(new SqlError({ cause: err, message: "Failed to execute statement" })))
|
|
279
|
-
} else {
|
|
280
|
-
resume(Effect.succeed(result))
|
|
274
|
+
function cleanup(cause?: Error) {
|
|
275
|
+
if (!done) client?.release(cause)
|
|
276
|
+
done = true
|
|
277
|
+
client?.off("error", onError)
|
|
281
278
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
279
|
+
pool.connect((cause, client_) => {
|
|
280
|
+
if (cause) {
|
|
281
|
+
return resume(
|
|
282
|
+
Effect.fail(
|
|
283
|
+
new SqlError({
|
|
284
|
+
reason: classifyError(cause, "Failed to acquire connection", "acquireConnection")
|
|
285
|
+
})
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
} else if (!client_) {
|
|
289
|
+
return resume(
|
|
290
|
+
Effect.fail(
|
|
291
|
+
new SqlError({
|
|
292
|
+
reason: new ConnectionError({
|
|
293
|
+
message: "Failed to acquire connection",
|
|
294
|
+
cause: new Error("No client returned"),
|
|
295
|
+
operation: "acquireConnection"
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
} else if (done) {
|
|
301
|
+
client_.release()
|
|
302
|
+
return
|
|
301
303
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
sql: string,
|
|
315
|
-
params: ReadonlyArray<unknown>,
|
|
316
|
-
transformRows: (<A extends object>(row: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined
|
|
317
|
-
) {
|
|
318
|
-
// oxlint-disable-next-line @typescript-eslint/no-this-alias
|
|
319
|
-
const self = this
|
|
320
|
-
return Stream.fromChannel(Channel.fromTransform(Effect.fnUntraced(function*(_, scope) {
|
|
321
|
-
const client = self.pg ?? (yield* Scope.provide(reserveRaw, scope))
|
|
322
|
-
yield* Scope.addFinalizer(scope, Effect.promise(() => cursor.close()))
|
|
323
|
-
const cursor = client.query(new Cursor(sql, params as any))
|
|
324
|
-
// @effect-diagnostics-next-line returnEffectInGen:off
|
|
325
|
-
return Effect.callback<Arr.NonEmptyReadonlyArray<any>, SqlError | Cause.Done>((resume) => {
|
|
326
|
-
cursor.read(128, (err, rows) => {
|
|
327
|
-
if (err) {
|
|
328
|
-
resume(Effect.fail(new SqlError({ cause: err, message: "Failed to execute statement" })))
|
|
329
|
-
} else if (Arr.isArrayNonEmpty(rows)) {
|
|
330
|
-
resume(Effect.succeed(transformRows ? transformRows(rows) as any : rows))
|
|
331
|
-
} else {
|
|
332
|
-
resume(Cause.done())
|
|
304
|
+
client = client_
|
|
305
|
+
client.once("error", onError)
|
|
306
|
+
cancel = makeCancel(pool, client)
|
|
307
|
+
f(client, (eff) => {
|
|
308
|
+
cleanup()
|
|
309
|
+
resume(eff)
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
return Effect.suspend(() => {
|
|
313
|
+
if (!cancel) {
|
|
314
|
+
cleanup()
|
|
315
|
+
return Effect.void
|
|
333
316
|
}
|
|
317
|
+
return Effect.ensuring(cancel, Effect.sync(cleanup))
|
|
334
318
|
})
|
|
335
319
|
})
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
320
|
+
},
|
|
321
|
+
client ? Effect.succeed(client) : reserveRaw
|
|
322
|
+
)
|
|
339
323
|
|
|
340
324
|
const reserveRaw = Effect.callback<Pg.PoolClient, SqlError, Scope.Scope>((resume) => {
|
|
341
325
|
const fiber = Fiber.getCurrent()!
|
|
342
|
-
const scope =
|
|
326
|
+
const scope = Context.getUnsafe(fiber.context, Scope.Scope)
|
|
343
327
|
let cause: Error | undefined = undefined
|
|
328
|
+
function onError(cause_: Error) {
|
|
329
|
+
cause = cause_
|
|
330
|
+
}
|
|
344
331
|
pool.connect((err, client, release) => {
|
|
345
332
|
if (err) {
|
|
346
|
-
resume(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
333
|
+
return resume(
|
|
334
|
+
Effect.fail(
|
|
335
|
+
new SqlError({
|
|
336
|
+
reason: classifyError(
|
|
337
|
+
err,
|
|
338
|
+
"Failed to acquire connection for transaction",
|
|
339
|
+
"acquireConnection"
|
|
340
|
+
)
|
|
354
341
|
})
|
|
355
|
-
)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
} else if (!client) {
|
|
345
|
+
return resume(
|
|
346
|
+
Effect.fail(
|
|
347
|
+
new SqlError({
|
|
348
|
+
reason: new ConnectionError({
|
|
349
|
+
message: "Failed to acquire connection for transaction",
|
|
350
|
+
cause: new Error("No client returned"),
|
|
351
|
+
operation: "acquireConnection"
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
)
|
|
355
|
+
)
|
|
361
356
|
}
|
|
362
|
-
client
|
|
357
|
+
client.on("error", onError)
|
|
358
|
+
resume(Effect.as(
|
|
359
|
+
Scope.addFinalizer(
|
|
360
|
+
scope,
|
|
361
|
+
Effect.sync(() => {
|
|
362
|
+
client.off("error", onError)
|
|
363
|
+
release(cause)
|
|
364
|
+
})
|
|
365
|
+
),
|
|
366
|
+
client
|
|
367
|
+
))
|
|
363
368
|
})
|
|
364
369
|
})
|
|
365
|
-
const reserve = Effect.map(reserveRaw,
|
|
370
|
+
const reserve = Effect.map(reserveRaw, makeConection)
|
|
366
371
|
|
|
367
|
-
const
|
|
368
|
-
|
|
372
|
+
const onListenClientError = (_: Error) => {
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const listenAcquirer = yield* RcRef.make({
|
|
376
|
+
acquire: Effect.acquireRelease(
|
|
377
|
+
Effect.tryPromise({
|
|
378
|
+
try: async () => {
|
|
379
|
+
const client = new Pg.Client(pool.options)
|
|
380
|
+
await client.connect()
|
|
381
|
+
client.on("error", onListenClientError)
|
|
382
|
+
return client
|
|
383
|
+
},
|
|
384
|
+
catch: (cause) =>
|
|
385
|
+
new SqlError({
|
|
386
|
+
reason: classifyError(cause, "Failed to acquire connection for listen", "acquireConnection")
|
|
387
|
+
})
|
|
388
|
+
}),
|
|
389
|
+
(client) =>
|
|
390
|
+
Effect.promise(() => {
|
|
391
|
+
client.off("error", onListenClientError)
|
|
392
|
+
return client.end()
|
|
393
|
+
}).pipe(
|
|
394
|
+
Effect.timeoutOption(1000)
|
|
395
|
+
),
|
|
396
|
+
{ interruptible: true }
|
|
397
|
+
)
|
|
369
398
|
})
|
|
370
399
|
|
|
371
400
|
let config: PgClientConfig = {
|
|
@@ -386,7 +415,7 @@ export const fromPool = Effect.fnUntraced(function*(
|
|
|
386
415
|
config = {
|
|
387
416
|
...config,
|
|
388
417
|
host: config.host ?? parsed.host ?? undefined,
|
|
389
|
-
port: config.port ?? (parsed.port ? Number.parse(parsed.port) : undefined),
|
|
418
|
+
port: config.port ?? (parsed.port ? Option.getOrUndefined(Number.parse(parsed.port)) : undefined),
|
|
390
419
|
username: config.username ?? parsed.user ?? undefined,
|
|
391
420
|
password: config.password ?? (parsed.password ? Redacted.make(parsed.password) : undefined),
|
|
392
421
|
database: config.database ?? parsed.database ?? undefined
|
|
@@ -396,10 +425,127 @@ export const fromPool = Effect.fnUntraced(function*(
|
|
|
396
425
|
}
|
|
397
426
|
}
|
|
398
427
|
|
|
428
|
+
return yield* makeWith({
|
|
429
|
+
acquirer: Effect.succeed(makeConection()),
|
|
430
|
+
transactionAcquirer: reserve,
|
|
431
|
+
listenAcquirer: RcRef.get(listenAcquirer),
|
|
432
|
+
config,
|
|
433
|
+
spanAttributes: options.spanAttributes,
|
|
434
|
+
transformResultNames: options.transformResultNames,
|
|
435
|
+
transformQueryNames: options.transformQueryNames,
|
|
436
|
+
transformJson: options.transformJson
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* @category constructors
|
|
442
|
+
* @since 1.0.0
|
|
443
|
+
*/
|
|
444
|
+
export const fromClient = Effect.fnUntraced(function*(
|
|
445
|
+
options: {
|
|
446
|
+
readonly acquire: Effect.Effect<Pg.Client, SqlError, Scope.Scope>
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Whether to acquire a separate client for each sql.stream / sql.listen.
|
|
450
|
+
*/
|
|
451
|
+
readonly acquireForStream: boolean
|
|
452
|
+
|
|
453
|
+
readonly applicationName?: string | undefined
|
|
454
|
+
readonly spanAttributes?: Record<string, unknown> | undefined
|
|
455
|
+
|
|
456
|
+
readonly transformResultNames?: ((str: string) => string) | undefined
|
|
457
|
+
readonly transformQueryNames?: ((str: string) => string) | undefined
|
|
458
|
+
readonly transformJson?: boolean | undefined
|
|
459
|
+
readonly types?: Pg.CustomTypesConfig | undefined
|
|
460
|
+
}
|
|
461
|
+
): Effect.fn.Return<PgClient, SqlError, Scope.Scope | Reactivity.Reactivity> {
|
|
462
|
+
function onError() {}
|
|
463
|
+
const acquireWithErrorHandler = options.acquire.pipe(
|
|
464
|
+
Effect.tap((client) => {
|
|
465
|
+
client.on("error", onError)
|
|
466
|
+
return Effect.addFinalizer(() => {
|
|
467
|
+
client.off("error", onError)
|
|
468
|
+
return Effect.void
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
)
|
|
472
|
+
const client = yield* acquireWithErrorHandler
|
|
473
|
+
|
|
474
|
+
const semaphore = Semaphore.makeUnsafe(1)
|
|
475
|
+
let streamClient = options.acquireForStream ? acquireWithErrorHandler : Effect.acquireRelease(
|
|
476
|
+
Effect.as(semaphore.take(1), client),
|
|
477
|
+
() => semaphore.release(1)
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
const makeConection = (client: Pg.Client) =>
|
|
481
|
+
new ConnectionImpl(
|
|
482
|
+
function runWithClient<A>(f: (client: Pg.ClientBase, resume: (_: Effect.Effect<A, SqlError>) => void) => void) {
|
|
483
|
+
return Effect.callback<A, SqlError>((resume) => {
|
|
484
|
+
f(client, resume)
|
|
485
|
+
})
|
|
486
|
+
},
|
|
487
|
+
streamClient
|
|
488
|
+
)
|
|
489
|
+
const connection = makeConection(client)
|
|
490
|
+
const acquirer = semaphore.withPermit(Effect.succeed(connection))
|
|
491
|
+
|
|
492
|
+
const config: PgClientConfig = {
|
|
493
|
+
...options,
|
|
494
|
+
host: client.host,
|
|
495
|
+
port: client.port,
|
|
496
|
+
database: client.database,
|
|
497
|
+
username: client.user,
|
|
498
|
+
password: typeof client.password === "string" ? Redacted.make(client.password) : undefined,
|
|
499
|
+
ssl: client.ssl
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return yield* makeWith({
|
|
503
|
+
acquirer,
|
|
504
|
+
transactionAcquirer: acquirer,
|
|
505
|
+
listenAcquirer: streamClient,
|
|
506
|
+
config,
|
|
507
|
+
spanAttributes: options.spanAttributes,
|
|
508
|
+
transformResultNames: options.transformResultNames,
|
|
509
|
+
transformQueryNames: options.transformQueryNames,
|
|
510
|
+
transformJson: options.transformJson
|
|
511
|
+
})
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* @category constructors
|
|
516
|
+
* @since 1.0.0
|
|
517
|
+
*/
|
|
518
|
+
export const makeWith = Effect.fnUntraced(function*(
|
|
519
|
+
options: {
|
|
520
|
+
readonly acquirer: SqlConnection.Acquirer
|
|
521
|
+
readonly transactionAcquirer: SqlConnection.Acquirer
|
|
522
|
+
readonly listenAcquirer: Effect.Effect<Pg.ClientBase, SqlError, Scope.Scope>
|
|
523
|
+
|
|
524
|
+
readonly config: PgClientConfig
|
|
525
|
+
readonly spanAttributes?: Record<string, unknown> | undefined
|
|
526
|
+
|
|
527
|
+
readonly transformResultNames?: ((str: string) => string) | undefined
|
|
528
|
+
readonly transformQueryNames?: ((str: string) => string) | undefined
|
|
529
|
+
readonly transformJson?: boolean | undefined
|
|
530
|
+
}
|
|
531
|
+
): Effect.fn.Return<PgClient, SqlError, Scope.Scope | Reactivity.Reactivity> {
|
|
532
|
+
const compiler = makeCompiler(
|
|
533
|
+
options.transformQueryNames,
|
|
534
|
+
options.transformJson
|
|
535
|
+
)
|
|
536
|
+
const transformRows = options.transformResultNames ?
|
|
537
|
+
Statement.defaultTransforms(
|
|
538
|
+
options.transformResultNames,
|
|
539
|
+
options.transformJson
|
|
540
|
+
).array :
|
|
541
|
+
undefined
|
|
542
|
+
|
|
543
|
+
const config = options.config
|
|
544
|
+
|
|
399
545
|
return Object.assign(
|
|
400
546
|
yield* Client.make({
|
|
401
|
-
acquirer:
|
|
402
|
-
transactionAcquirer:
|
|
547
|
+
acquirer: options.acquirer,
|
|
548
|
+
transactionAcquirer: options.transactionAcquirer,
|
|
403
549
|
compiler,
|
|
404
550
|
spanAttributes: [
|
|
405
551
|
...(options.spanAttributes ? Object.entries(options.spanAttributes) : []),
|
|
@@ -412,11 +558,11 @@ export const fromPool = Effect.fnUntraced(function*(
|
|
|
412
558
|
}),
|
|
413
559
|
{
|
|
414
560
|
[TypeId]: TypeId as TypeId,
|
|
415
|
-
config,
|
|
561
|
+
config: options.config,
|
|
416
562
|
json: (_: unknown) => Statement.fragment([PgJson(_)]),
|
|
417
563
|
listen: (channel: string) =>
|
|
418
564
|
Stream.callback<string, SqlError>(Effect.fnUntraced(function*(queue) {
|
|
419
|
-
const client = yield*
|
|
565
|
+
const client = yield* options.listenAcquirer
|
|
420
566
|
function onNotification(msg: Pg.Notification) {
|
|
421
567
|
if (msg.channel === channel && msg.payload) {
|
|
422
568
|
Queue.offerUnsafe(queue, msg.payload)
|
|
@@ -430,24 +576,133 @@ export const fromPool = Effect.fnUntraced(function*(
|
|
|
430
576
|
)
|
|
431
577
|
yield* Effect.tryPromise({
|
|
432
578
|
try: () => client.query(`LISTEN ${Pg.escapeIdentifier(channel)}`),
|
|
433
|
-
catch: (cause) => new SqlError({ cause,
|
|
579
|
+
catch: (cause) => new SqlError({ reason: classifyError(cause, "Failed to listen", "listen") })
|
|
434
580
|
})
|
|
435
581
|
client.on("notification", onNotification)
|
|
436
582
|
})),
|
|
437
583
|
notify: (channel: string, payload: string) =>
|
|
438
|
-
Effect.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} else {
|
|
443
|
-
resume(Effect.void)
|
|
444
|
-
}
|
|
445
|
-
})
|
|
446
|
-
})
|
|
584
|
+
Effect.asVoid(Effect.scoped(Effect.flatMap(
|
|
585
|
+
options.acquirer,
|
|
586
|
+
(conn) => conn.executeRaw(`SELECT pg_notify($1, $2)`, [channel, payload])
|
|
587
|
+
)))
|
|
447
588
|
}
|
|
448
589
|
)
|
|
449
590
|
})
|
|
450
591
|
|
|
592
|
+
class ConnectionImpl implements Connection {
|
|
593
|
+
constructor(
|
|
594
|
+
runWithClient: <A>(
|
|
595
|
+
f: (client: Pg.ClientBase, resume: (_: Effect.Effect<A, SqlError>) => void) => void
|
|
596
|
+
) => Effect.Effect<A, SqlError>,
|
|
597
|
+
reserve: Effect.Effect<Pg.ClientBase, SqlError, Scope.Scope>
|
|
598
|
+
) {
|
|
599
|
+
this.runWithClient = runWithClient
|
|
600
|
+
this.reserve = reserve
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private readonly runWithClient: <A>(
|
|
604
|
+
f: (client: Pg.ClientBase, resume: (_: Effect.Effect<A, SqlError>) => void) => void
|
|
605
|
+
) => Effect.Effect<A, SqlError>
|
|
606
|
+
private readonly reserve: Effect.Effect<Pg.ClientBase, SqlError, Scope.Scope>
|
|
607
|
+
|
|
608
|
+
private run(query: string, params: ReadonlyArray<unknown>) {
|
|
609
|
+
return this.runWithClient<ReadonlyArray<any>>((client, resume) => {
|
|
610
|
+
client.query(query, params as any, (err, result) => {
|
|
611
|
+
if (err) {
|
|
612
|
+
resume(
|
|
613
|
+
Effect.fail(new SqlError({ reason: classifyError(err, "Failed to execute statement", "execute") }))
|
|
614
|
+
)
|
|
615
|
+
} else {
|
|
616
|
+
// Multi-statement queries return an array of results
|
|
617
|
+
resume(Effect.succeed(
|
|
618
|
+
Array.isArray(result)
|
|
619
|
+
? result.map((r) => r.rows ?? [])
|
|
620
|
+
: result.rows ?? []
|
|
621
|
+
))
|
|
622
|
+
}
|
|
623
|
+
})
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
execute(
|
|
628
|
+
sql: string,
|
|
629
|
+
params: ReadonlyArray<unknown>,
|
|
630
|
+
transformRows: (<A extends object>(row: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined
|
|
631
|
+
) {
|
|
632
|
+
return transformRows
|
|
633
|
+
? Effect.map(this.run(sql, params), transformRows)
|
|
634
|
+
: this.run(sql, params)
|
|
635
|
+
}
|
|
636
|
+
executeRaw(sql: string, params: ReadonlyArray<unknown>) {
|
|
637
|
+
return this.runWithClient<Pg.Result>((client, resume) => {
|
|
638
|
+
client.query(sql, params as any, (err, result) => {
|
|
639
|
+
if (err) {
|
|
640
|
+
resume(
|
|
641
|
+
Effect.fail(new SqlError({ reason: classifyError(err, "Failed to execute statement", "execute") }))
|
|
642
|
+
)
|
|
643
|
+
} else {
|
|
644
|
+
resume(Effect.succeed(result))
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
})
|
|
648
|
+
}
|
|
649
|
+
executeWithoutTransform(sql: string, params: ReadonlyArray<unknown>) {
|
|
650
|
+
return this.run(sql, params)
|
|
651
|
+
}
|
|
652
|
+
executeValues(sql: string, params: ReadonlyArray<unknown>) {
|
|
653
|
+
return this.runWithClient<ReadonlyArray<any>>((client, resume) => {
|
|
654
|
+
client.query(
|
|
655
|
+
{
|
|
656
|
+
text: sql,
|
|
657
|
+
rowMode: "array",
|
|
658
|
+
values: params as Array<string>
|
|
659
|
+
},
|
|
660
|
+
(err, result) => {
|
|
661
|
+
if (err) {
|
|
662
|
+
resume(
|
|
663
|
+
Effect.fail(new SqlError({ reason: classifyError(err, "Failed to execute statement", "execute") }))
|
|
664
|
+
)
|
|
665
|
+
} else {
|
|
666
|
+
resume(Effect.succeed(result.rows))
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
})
|
|
671
|
+
}
|
|
672
|
+
executeUnprepared(
|
|
673
|
+
sql: string,
|
|
674
|
+
params: ReadonlyArray<unknown>,
|
|
675
|
+
transformRows: (<A extends object>(row: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined
|
|
676
|
+
) {
|
|
677
|
+
return this.execute(sql, params, transformRows)
|
|
678
|
+
}
|
|
679
|
+
executeStream(
|
|
680
|
+
sql: string,
|
|
681
|
+
params: ReadonlyArray<unknown>,
|
|
682
|
+
transformRows: (<A extends object>(row: ReadonlyArray<A>) => ReadonlyArray<A>) | undefined
|
|
683
|
+
) {
|
|
684
|
+
// oxlint-disable-next-line @typescript-eslint/no-this-alias
|
|
685
|
+
const self = this
|
|
686
|
+
return Stream.fromChannel(Channel.fromTransform(Effect.fnUntraced(function*(_, scope) {
|
|
687
|
+
const client = yield* Scope.provide(self.reserve, scope)
|
|
688
|
+
yield* Scope.addFinalizer(scope, Effect.promise(() => cursor.close()))
|
|
689
|
+
const cursor = client.query(new Cursor(sql, params as any))
|
|
690
|
+
// @effect-diagnostics-next-line returnEffectInGen:off
|
|
691
|
+
return Effect.callback<Arr.NonEmptyReadonlyArray<any>, SqlError | Cause.Done>((resume) => {
|
|
692
|
+
cursor.read(128, (err, rows) => {
|
|
693
|
+
if (err) {
|
|
694
|
+
resume(Effect.fail(new SqlError({ reason: classifyError(err, "Failed to execute statement", "stream") })))
|
|
695
|
+
} else if (Arr.isArrayNonEmpty(rows)) {
|
|
696
|
+
resume(Effect.succeed(transformRows ? transformRows(rows) as any : rows))
|
|
697
|
+
} else {
|
|
698
|
+
resume(Cause.done())
|
|
699
|
+
}
|
|
700
|
+
})
|
|
701
|
+
})
|
|
702
|
+
})))
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
451
706
|
const cancelEffects = new WeakMap<Pg.PoolClient, Effect.Effect<void> | undefined>()
|
|
452
707
|
const makeCancel = (pool: Pg.Pool, client: Pg.PoolClient) => {
|
|
453
708
|
if (cancelEffects.has(client)) {
|
|
@@ -474,57 +729,37 @@ const makeCancel = (pool: Pg.Pool, client: Pg.PoolClient) => {
|
|
|
474
729
|
* @category layers
|
|
475
730
|
* @since 1.0.0
|
|
476
731
|
*/
|
|
477
|
-
export const
|
|
478
|
-
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
ServiceMap.make(PgClient, client).pipe(
|
|
487
|
-
ServiceMap.add(Client.SqlClient, client)
|
|
488
|
-
)
|
|
489
|
-
)
|
|
490
|
-
)
|
|
491
|
-
).pipe(Layer.provide(Reactivity.layer))
|
|
732
|
+
export const layerFrom = <E, R>(
|
|
733
|
+
acquire: Effect.Effect<PgClient, E, R>
|
|
734
|
+
): Layer.Layer<PgClient | Client.SqlClient, E, Exclude<R, Scope.Scope | Reactivity.Reactivity>> =>
|
|
735
|
+
Layer.effectContext(
|
|
736
|
+
Effect.map(acquire, (client) =>
|
|
737
|
+
Context.make(PgClient, client).pipe(
|
|
738
|
+
Context.add(Client.SqlClient, client)
|
|
739
|
+
))
|
|
740
|
+
).pipe(Layer.provide(Reactivity.layer)) as any
|
|
492
741
|
|
|
493
742
|
/**
|
|
494
743
|
* @category layers
|
|
495
744
|
* @since 1.0.0
|
|
496
745
|
*/
|
|
497
|
-
export const
|
|
498
|
-
config:
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
)
|
|
746
|
+
export const layerConfig: (
|
|
747
|
+
config: Config.Wrap<PgPoolConfig>
|
|
748
|
+
) => Layer.Layer<PgClient | Client.SqlClient, Config.ConfigError | SqlError> = (
|
|
749
|
+
config: Config.Wrap<PgPoolConfig>
|
|
750
|
+
): Layer.Layer<PgClient | Client.SqlClient, Config.ConfigError | SqlError> =>
|
|
751
|
+
layerFrom(Effect.flatMap(
|
|
752
|
+
Config.unwrap(config).asEffect(),
|
|
753
|
+
make
|
|
754
|
+
))
|
|
506
755
|
|
|
507
756
|
/**
|
|
508
757
|
* @category layers
|
|
509
758
|
* @since 1.0.0
|
|
510
759
|
*/
|
|
511
|
-
export const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
readonly applicationName?: string | undefined
|
|
515
|
-
readonly spanAttributes?: Record<string, unknown> | undefined
|
|
516
|
-
|
|
517
|
-
readonly transformResultNames?: ((str: string) => string) | undefined
|
|
518
|
-
readonly transformQueryNames?: ((str: string) => string) | undefined
|
|
519
|
-
readonly transformJson?: boolean | undefined
|
|
520
|
-
readonly types?: Pg.CustomTypesConfig | undefined
|
|
521
|
-
}): Layer.Layer<PgClient | Client.SqlClient, SqlError> =>
|
|
522
|
-
Layer.effectServices(
|
|
523
|
-
Effect.map(fromPool(options), (client) =>
|
|
524
|
-
ServiceMap.make(PgClient, client).pipe(
|
|
525
|
-
ServiceMap.add(Client.SqlClient, client)
|
|
526
|
-
))
|
|
527
|
-
).pipe(Layer.provide(Reactivity.layer))
|
|
760
|
+
export const layer = (
|
|
761
|
+
config: PgPoolConfig
|
|
762
|
+
): Layer.Layer<PgClient | Client.SqlClient, SqlError> => layerFrom(make(config))
|
|
528
763
|
|
|
529
764
|
/**
|
|
530
765
|
* @category constructor
|
|
@@ -591,3 +826,55 @@ interface PgJson extends Custom<"PgJson", unknown> {}
|
|
|
591
826
|
* @since 1.0.0
|
|
592
827
|
*/
|
|
593
828
|
const PgJson = Statement.custom<PgJson>("PgJson")
|
|
829
|
+
|
|
830
|
+
const ATTR_DB_SYSTEM_NAME = "db.system.name"
|
|
831
|
+
const ATTR_DB_NAMESPACE = "db.namespace"
|
|
832
|
+
const ATTR_SERVER_ADDRESS = "server.address"
|
|
833
|
+
const ATTR_SERVER_PORT = "server.port"
|
|
834
|
+
|
|
835
|
+
const pgCodeFromCause = (cause: unknown): string | undefined => {
|
|
836
|
+
if (typeof cause !== "object" || cause === null || !("code" in cause)) {
|
|
837
|
+
return undefined
|
|
838
|
+
}
|
|
839
|
+
const code = cause.code
|
|
840
|
+
return typeof code === "string" ? code : undefined
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const classifyError = (
|
|
844
|
+
cause: unknown,
|
|
845
|
+
message: string,
|
|
846
|
+
operation: string
|
|
847
|
+
) => {
|
|
848
|
+
const props = { cause, message, operation }
|
|
849
|
+
const code = pgCodeFromCause(cause)
|
|
850
|
+
if (code !== undefined) {
|
|
851
|
+
if (code.startsWith("08")) {
|
|
852
|
+
return new ConnectionError(props)
|
|
853
|
+
}
|
|
854
|
+
if (code.startsWith("28")) {
|
|
855
|
+
return new AuthenticationError(props)
|
|
856
|
+
}
|
|
857
|
+
if (code === "42501") {
|
|
858
|
+
return new AuthorizationError(props)
|
|
859
|
+
}
|
|
860
|
+
if (code.startsWith("42")) {
|
|
861
|
+
return new SqlSyntaxError(props)
|
|
862
|
+
}
|
|
863
|
+
if (code.startsWith("23")) {
|
|
864
|
+
return new ConstraintError(props)
|
|
865
|
+
}
|
|
866
|
+
if (code === "40P01") {
|
|
867
|
+
return new DeadlockError(props)
|
|
868
|
+
}
|
|
869
|
+
if (code === "40001") {
|
|
870
|
+
return new SerializationError(props)
|
|
871
|
+
}
|
|
872
|
+
if (code === "55P03") {
|
|
873
|
+
return new LockTimeoutError(props)
|
|
874
|
+
}
|
|
875
|
+
if (code === "57014") {
|
|
876
|
+
return new StatementTimeoutError(props)
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return new UnknownError(props)
|
|
880
|
+
}
|