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