@effect/sql-pg 4.0.0-beta.38 → 4.0.0-beta.39
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/dist/PgClient.d.ts +57 -17
- package/dist/PgClient.d.ts.map +1 -1
- package/dist/PgClient.js +291 -198
- package/dist/PgClient.js.map +1 -1
- package/package.json +3 -3
- package/src/PgClient.ts +444 -285
package/dist/PgClient.js
CHANGED
|
@@ -15,6 +15,7 @@ import * as Queue from "effect/Queue";
|
|
|
15
15
|
import * as RcRef from "effect/RcRef";
|
|
16
16
|
import * as Redacted from "effect/Redacted";
|
|
17
17
|
import * as Scope from "effect/Scope";
|
|
18
|
+
import * as Semaphore from "effect/Semaphore";
|
|
18
19
|
import * as ServiceMap from "effect/ServiceMap";
|
|
19
20
|
import * as Stream from "effect/Stream";
|
|
20
21
|
import * as Reactivity from "effect/unstable/reactivity/Reactivity";
|
|
@@ -24,55 +25,6 @@ import * as Statement from "effect/unstable/sql/Statement";
|
|
|
24
25
|
import * as Pg from "pg";
|
|
25
26
|
import * as PgConnString from "pg-connection-string";
|
|
26
27
|
import Cursor from "pg-cursor";
|
|
27
|
-
const ATTR_DB_SYSTEM_NAME = "db.system.name";
|
|
28
|
-
const ATTR_DB_NAMESPACE = "db.namespace";
|
|
29
|
-
const ATTR_SERVER_ADDRESS = "server.address";
|
|
30
|
-
const ATTR_SERVER_PORT = "server.port";
|
|
31
|
-
const pgCodeFromCause = cause => {
|
|
32
|
-
if (typeof cause !== "object" || cause === null || !("code" in cause)) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
const code = cause.code;
|
|
36
|
-
return typeof code === "string" ? code : undefined;
|
|
37
|
-
};
|
|
38
|
-
const classifyError = (cause, message, operation) => {
|
|
39
|
-
const props = {
|
|
40
|
-
cause,
|
|
41
|
-
message,
|
|
42
|
-
operation
|
|
43
|
-
};
|
|
44
|
-
const code = pgCodeFromCause(cause);
|
|
45
|
-
if (code !== undefined) {
|
|
46
|
-
if (code.startsWith("08")) {
|
|
47
|
-
return new ConnectionError(props);
|
|
48
|
-
}
|
|
49
|
-
if (code.startsWith("28")) {
|
|
50
|
-
return new AuthenticationError(props);
|
|
51
|
-
}
|
|
52
|
-
if (code === "42501") {
|
|
53
|
-
return new AuthorizationError(props);
|
|
54
|
-
}
|
|
55
|
-
if (code.startsWith("42")) {
|
|
56
|
-
return new SqlSyntaxError(props);
|
|
57
|
-
}
|
|
58
|
-
if (code.startsWith("23")) {
|
|
59
|
-
return new ConstraintError(props);
|
|
60
|
-
}
|
|
61
|
-
if (code === "40P01") {
|
|
62
|
-
return new DeadlockError(props);
|
|
63
|
-
}
|
|
64
|
-
if (code === "40001") {
|
|
65
|
-
return new SerializationError(props);
|
|
66
|
-
}
|
|
67
|
-
if (code === "55P03") {
|
|
68
|
-
return new LockTimeoutError(props);
|
|
69
|
-
}
|
|
70
|
-
if (code === "57014") {
|
|
71
|
-
return new StatementTimeoutError(props);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return new UnknownError(props);
|
|
75
|
-
};
|
|
76
28
|
/**
|
|
77
29
|
* @category type ids
|
|
78
30
|
* @since 1.0.0
|
|
@@ -128,152 +80,108 @@ export const make = options => fromPool({
|
|
|
128
80
|
return pool;
|
|
129
81
|
})
|
|
130
82
|
});
|
|
83
|
+
/**
|
|
84
|
+
* @category constructors
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
*/
|
|
87
|
+
export const makeClient = options => fromClient({
|
|
88
|
+
...options,
|
|
89
|
+
acquire: Effect.gen(function* () {
|
|
90
|
+
const client = new Pg.Client({
|
|
91
|
+
connectionString: options.url ? Redacted.value(options.url) : undefined,
|
|
92
|
+
user: options.username,
|
|
93
|
+
host: options.host,
|
|
94
|
+
database: options.database,
|
|
95
|
+
password: options.password ? Redacted.value(options.password) : undefined,
|
|
96
|
+
ssl: options.ssl,
|
|
97
|
+
port: options.port,
|
|
98
|
+
...(options.stream ? {
|
|
99
|
+
stream: options.stream
|
|
100
|
+
} : {}),
|
|
101
|
+
application_name: options.applicationName ?? "@effect/sql-pg",
|
|
102
|
+
types: options.types
|
|
103
|
+
});
|
|
104
|
+
yield* Effect.acquireRelease(Effect.tryPromise({
|
|
105
|
+
try: () => client.query("SELECT 1"),
|
|
106
|
+
catch: cause => new SqlError({
|
|
107
|
+
reason: classifyError(cause, "PgClient: Failed to connect", "connect")
|
|
108
|
+
})
|
|
109
|
+
}), () => Effect.promise(() => client.end()).pipe(Effect.timeoutOption(1000))).pipe(Effect.timeoutOrElse({
|
|
110
|
+
duration: options.connectTimeout ?? Duration.seconds(5),
|
|
111
|
+
onTimeout: () => Effect.fail(new SqlError({
|
|
112
|
+
reason: new ConnectionError({
|
|
113
|
+
cause: new Error("Connection timed out"),
|
|
114
|
+
message: "PgClient: Connection timed out",
|
|
115
|
+
operation: "connect"
|
|
116
|
+
})
|
|
117
|
+
}))
|
|
118
|
+
}));
|
|
119
|
+
return client;
|
|
120
|
+
}),
|
|
121
|
+
acquireForStream: options.acquireForStream ?? false
|
|
122
|
+
});
|
|
131
123
|
/**
|
|
132
124
|
* @category constructors
|
|
133
125
|
* @since 1.0.0
|
|
134
126
|
*/
|
|
135
127
|
export const fromPool = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
136
|
-
const compiler = makeCompiler(options.transformQueryNames, options.transformJson);
|
|
137
|
-
const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames, options.transformJson).array : undefined;
|
|
138
128
|
const pool = yield* options.acquire;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
const makeConection = client => new ConnectionImpl(function runWithClient(f) {
|
|
130
|
+
if (client !== undefined) {
|
|
131
|
+
return Effect.callback(resume => {
|
|
132
|
+
f(client, resume);
|
|
133
|
+
return makeCancel(pool, client);
|
|
134
|
+
});
|
|
143
135
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
136
|
+
return Effect.callback(resume => {
|
|
137
|
+
let done = false;
|
|
138
|
+
let cancel = undefined;
|
|
139
|
+
let client = undefined;
|
|
140
|
+
function onError(cause) {
|
|
141
|
+
cleanup(cause);
|
|
142
|
+
resume(Effect.fail(new SqlError({
|
|
143
|
+
reason: classifyError(cause, "Connection error", "acquireConnection")
|
|
144
|
+
})));
|
|
150
145
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
function cleanup(cause) {
|
|
147
|
+
if (!done) client?.release(cause);
|
|
148
|
+
done = true;
|
|
149
|
+
client?.off("error", onError);
|
|
150
|
+
}
|
|
151
|
+
pool.connect((cause, client_) => {
|
|
152
|
+
if (cause) {
|
|
153
|
+
return resume(Effect.fail(new SqlError({
|
|
154
|
+
reason: classifyError(cause, "Failed to acquire connection", "acquireConnection")
|
|
159
155
|
})));
|
|
156
|
+
} else if (!client_) {
|
|
157
|
+
return resume(Effect.fail(new SqlError({
|
|
158
|
+
reason: new ConnectionError({
|
|
159
|
+
message: "Failed to acquire connection",
|
|
160
|
+
cause: new Error("No client returned"),
|
|
161
|
+
operation: "acquireConnection"
|
|
162
|
+
})
|
|
163
|
+
})));
|
|
164
|
+
} else if (done) {
|
|
165
|
+
client_.release();
|
|
166
|
+
return;
|
|
160
167
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (cause) {
|
|
168
|
-
return resume(Effect.fail(new SqlError({
|
|
169
|
-
reason: classifyError(cause, "Failed to acquire connection", "acquireConnection")
|
|
170
|
-
})));
|
|
171
|
-
} else if (!client_) {
|
|
172
|
-
return resume(Effect.fail(new SqlError({
|
|
173
|
-
reason: new ConnectionError({
|
|
174
|
-
message: "Failed to acquire connection",
|
|
175
|
-
cause: new Error("No client returned"),
|
|
176
|
-
operation: "acquireConnection"
|
|
177
|
-
})
|
|
178
|
-
})));
|
|
179
|
-
} else if (done) {
|
|
180
|
-
client_.release();
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
client = client_;
|
|
184
|
-
client.once("error", onError);
|
|
185
|
-
cancel = makeCancel(pool, client);
|
|
186
|
-
f(client, eff => {
|
|
187
|
-
cleanup();
|
|
188
|
-
resume(eff);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
return Effect.suspend(() => {
|
|
192
|
-
if (!cancel) {
|
|
193
|
-
cleanup();
|
|
194
|
-
return Effect.void;
|
|
195
|
-
}
|
|
196
|
-
return Effect.ensuring(cancel, Effect.sync(cleanup));
|
|
168
|
+
client = client_;
|
|
169
|
+
client.once("error", onError);
|
|
170
|
+
cancel = makeCancel(pool, client);
|
|
171
|
+
f(client, eff => {
|
|
172
|
+
cleanup();
|
|
173
|
+
resume(eff);
|
|
197
174
|
});
|
|
198
175
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
206
|
-
})));
|
|
207
|
-
} else {
|
|
208
|
-
// Multi-statement queries return an array of results
|
|
209
|
-
resume(Effect.succeed(Array.isArray(result) ? result.map(r => r.rows ?? []) : result.rows ?? []));
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
execute(sql, params, transformRows) {
|
|
215
|
-
return transformRows ? Effect.map(this.run(sql, params), transformRows) : this.run(sql, params);
|
|
216
|
-
}
|
|
217
|
-
executeRaw(sql, params) {
|
|
218
|
-
return this.runWithClient((client, resume) => {
|
|
219
|
-
client.query(sql, params, (err, result) => {
|
|
220
|
-
if (err) {
|
|
221
|
-
resume(Effect.fail(new SqlError({
|
|
222
|
-
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
223
|
-
})));
|
|
224
|
-
} else {
|
|
225
|
-
resume(Effect.succeed(result));
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
executeWithoutTransform(sql, params) {
|
|
231
|
-
return this.run(sql, params);
|
|
232
|
-
}
|
|
233
|
-
executeValues(sql, params) {
|
|
234
|
-
return this.runWithClient((client, resume) => {
|
|
235
|
-
client.query({
|
|
236
|
-
text: sql,
|
|
237
|
-
rowMode: "array",
|
|
238
|
-
values: params
|
|
239
|
-
}, (err, result) => {
|
|
240
|
-
if (err) {
|
|
241
|
-
resume(Effect.fail(new SqlError({
|
|
242
|
-
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
243
|
-
})));
|
|
244
|
-
} else {
|
|
245
|
-
resume(Effect.succeed(result.rows));
|
|
246
|
-
}
|
|
247
|
-
});
|
|
176
|
+
return Effect.suspend(() => {
|
|
177
|
+
if (!cancel) {
|
|
178
|
+
cleanup();
|
|
179
|
+
return Effect.void;
|
|
180
|
+
}
|
|
181
|
+
return Effect.ensuring(cancel, Effect.sync(cleanup));
|
|
248
182
|
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return this.execute(sql, params, transformRows);
|
|
252
|
-
}
|
|
253
|
-
executeStream(sql, params, transformRows) {
|
|
254
|
-
// oxlint-disable-next-line @typescript-eslint/no-this-alias
|
|
255
|
-
const self = this;
|
|
256
|
-
return Stream.fromChannel(Channel.fromTransform(Effect.fnUntraced(function* (_, scope) {
|
|
257
|
-
const client = self.pg ?? (yield* Scope.provide(reserveRaw, scope));
|
|
258
|
-
yield* Scope.addFinalizer(scope, Effect.promise(() => cursor.close()));
|
|
259
|
-
const cursor = client.query(new Cursor(sql, params));
|
|
260
|
-
// @effect-diagnostics-next-line returnEffectInGen:off
|
|
261
|
-
return Effect.callback(resume => {
|
|
262
|
-
cursor.read(128, (err, rows) => {
|
|
263
|
-
if (err) {
|
|
264
|
-
resume(Effect.fail(new SqlError({
|
|
265
|
-
reason: classifyError(err, "Failed to execute statement", "stream")
|
|
266
|
-
})));
|
|
267
|
-
} else if (Arr.isArrayNonEmpty(rows)) {
|
|
268
|
-
resume(Effect.succeed(transformRows ? transformRows(rows) : rows));
|
|
269
|
-
} else {
|
|
270
|
-
resume(Cause.done());
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
})));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
183
|
+
});
|
|
184
|
+
}, client ? Effect.succeed(client) : reserveRaw);
|
|
277
185
|
const reserveRaw = Effect.callback(resume => {
|
|
278
186
|
const fiber = Fiber.getCurrent();
|
|
279
187
|
const scope = ServiceMap.getUnsafe(fiber.services, Scope.Scope);
|
|
@@ -295,9 +203,9 @@ export const fromPool = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
|
295
203
|
client.on("error", onError);
|
|
296
204
|
});
|
|
297
205
|
});
|
|
298
|
-
const reserve = Effect.map(reserveRaw,
|
|
206
|
+
const reserve = Effect.map(reserveRaw, makeConection);
|
|
299
207
|
const onListenClientError = _ => {};
|
|
300
|
-
const
|
|
208
|
+
const listenAcquirer = yield* RcRef.make({
|
|
301
209
|
acquire: Effect.acquireRelease(Effect.tryPromise({
|
|
302
210
|
try: async () => {
|
|
303
211
|
const client = new Pg.Client(pool.options);
|
|
@@ -340,18 +248,80 @@ export const fromPool = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
|
340
248
|
//
|
|
341
249
|
}
|
|
342
250
|
}
|
|
343
|
-
return
|
|
344
|
-
acquirer: Effect.succeed(
|
|
251
|
+
return yield* makeWith({
|
|
252
|
+
acquirer: Effect.succeed(makeConection()),
|
|
345
253
|
transactionAcquirer: reserve,
|
|
254
|
+
listenAcquirer: RcRef.get(listenAcquirer),
|
|
255
|
+
config,
|
|
256
|
+
spanAttributes: options.spanAttributes,
|
|
257
|
+
transformResultNames: options.transformResultNames,
|
|
258
|
+
transformQueryNames: options.transformQueryNames,
|
|
259
|
+
transformJson: options.transformJson
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
/**
|
|
263
|
+
* @category constructors
|
|
264
|
+
* @since 1.0.0
|
|
265
|
+
*/
|
|
266
|
+
export const fromClient = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
267
|
+
function onError() {}
|
|
268
|
+
const acquireWithErrorHandler = options.acquire.pipe(Effect.tap(client => {
|
|
269
|
+
client.on("error", onError);
|
|
270
|
+
return Effect.addFinalizer(() => {
|
|
271
|
+
client.off("error", onError);
|
|
272
|
+
return Effect.void;
|
|
273
|
+
});
|
|
274
|
+
}));
|
|
275
|
+
const client = yield* acquireWithErrorHandler;
|
|
276
|
+
const semaphore = Semaphore.makeUnsafe(1);
|
|
277
|
+
let streamClient = options.acquireForStream ? acquireWithErrorHandler : Effect.acquireRelease(Effect.as(semaphore.take(1), client), () => semaphore.release(1));
|
|
278
|
+
const makeConection = client => new ConnectionImpl(function runWithClient(f) {
|
|
279
|
+
return Effect.callback(resume => {
|
|
280
|
+
f(client, resume);
|
|
281
|
+
});
|
|
282
|
+
}, streamClient);
|
|
283
|
+
const connection = makeConection(client);
|
|
284
|
+
const acquirer = semaphore.withPermit(Effect.succeed(connection));
|
|
285
|
+
const config = {
|
|
286
|
+
...options,
|
|
287
|
+
host: client.host,
|
|
288
|
+
port: client.port,
|
|
289
|
+
database: client.database,
|
|
290
|
+
username: client.user,
|
|
291
|
+
password: typeof client.password === "string" ? Redacted.make(client.password) : undefined,
|
|
292
|
+
ssl: client.ssl
|
|
293
|
+
};
|
|
294
|
+
return yield* makeWith({
|
|
295
|
+
acquirer,
|
|
296
|
+
transactionAcquirer: acquirer,
|
|
297
|
+
listenAcquirer: streamClient,
|
|
298
|
+
config,
|
|
299
|
+
spanAttributes: options.spanAttributes,
|
|
300
|
+
transformResultNames: options.transformResultNames,
|
|
301
|
+
transformQueryNames: options.transformQueryNames,
|
|
302
|
+
transformJson: options.transformJson
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
/**
|
|
306
|
+
* @category constructors
|
|
307
|
+
* @since 1.0.0
|
|
308
|
+
*/
|
|
309
|
+
export const makeWith = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
310
|
+
const compiler = makeCompiler(options.transformQueryNames, options.transformJson);
|
|
311
|
+
const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames, options.transformJson).array : undefined;
|
|
312
|
+
const config = options.config;
|
|
313
|
+
return Object.assign(yield* Client.make({
|
|
314
|
+
acquirer: options.acquirer,
|
|
315
|
+
transactionAcquirer: options.transactionAcquirer,
|
|
346
316
|
compiler,
|
|
347
317
|
spanAttributes: [...(options.spanAttributes ? Object.entries(options.spanAttributes) : []), [ATTR_DB_SYSTEM_NAME, "postgresql"], [ATTR_DB_NAMESPACE, config.database ?? config.username ?? "postgres"], [ATTR_SERVER_ADDRESS, config.host ?? "localhost"], [ATTR_SERVER_PORT, config.port ?? 5432]],
|
|
348
318
|
transformRows
|
|
349
319
|
}), {
|
|
350
320
|
[TypeId]: TypeId,
|
|
351
|
-
config,
|
|
321
|
+
config: options.config,
|
|
352
322
|
json: _ => Statement.fragment([PgJson(_)]),
|
|
353
323
|
listen: channel => Stream.callback(Effect.fnUntraced(function* (queue) {
|
|
354
|
-
const client = yield*
|
|
324
|
+
const client = yield* options.listenAcquirer;
|
|
355
325
|
function onNotification(msg) {
|
|
356
326
|
if (msg.channel === channel && msg.payload) {
|
|
357
327
|
Queue.offerUnsafe(queue, msg.payload);
|
|
@@ -369,19 +339,93 @@ export const fromPool = /*#__PURE__*/Effect.fnUntraced(function* (options) {
|
|
|
369
339
|
});
|
|
370
340
|
client.on("notification", onNotification);
|
|
371
341
|
})),
|
|
372
|
-
notify: (channel, payload) => Effect.
|
|
373
|
-
|
|
342
|
+
notify: (channel, payload) => Effect.asVoid(Effect.scoped(Effect.flatMap(options.acquirer, conn => conn.executeRaw(`SELECT pg_notify($1, $2)`, [channel, payload]))))
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
class ConnectionImpl {
|
|
346
|
+
constructor(runWithClient, reserve) {
|
|
347
|
+
this.runWithClient = runWithClient;
|
|
348
|
+
this.reserve = reserve;
|
|
349
|
+
}
|
|
350
|
+
runWithClient;
|
|
351
|
+
reserve;
|
|
352
|
+
run(query, params) {
|
|
353
|
+
return this.runWithClient((client, resume) => {
|
|
354
|
+
client.query(query, params, (err, result) => {
|
|
374
355
|
if (err) {
|
|
375
356
|
resume(Effect.fail(new SqlError({
|
|
376
|
-
reason: classifyError(err, "Failed to
|
|
357
|
+
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
377
358
|
})));
|
|
378
359
|
} else {
|
|
379
|
-
|
|
360
|
+
// Multi-statement queries return an array of results
|
|
361
|
+
resume(Effect.succeed(Array.isArray(result) ? result.map(r => r.rows ?? []) : result.rows ?? []));
|
|
380
362
|
}
|
|
381
363
|
});
|
|
382
|
-
})
|
|
383
|
-
}
|
|
384
|
-
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
execute(sql, params, transformRows) {
|
|
367
|
+
return transformRows ? Effect.map(this.run(sql, params), transformRows) : this.run(sql, params);
|
|
368
|
+
}
|
|
369
|
+
executeRaw(sql, params) {
|
|
370
|
+
return this.runWithClient((client, resume) => {
|
|
371
|
+
client.query(sql, params, (err, result) => {
|
|
372
|
+
if (err) {
|
|
373
|
+
resume(Effect.fail(new SqlError({
|
|
374
|
+
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
375
|
+
})));
|
|
376
|
+
} else {
|
|
377
|
+
resume(Effect.succeed(result));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
executeWithoutTransform(sql, params) {
|
|
383
|
+
return this.run(sql, params);
|
|
384
|
+
}
|
|
385
|
+
executeValues(sql, params) {
|
|
386
|
+
return this.runWithClient((client, resume) => {
|
|
387
|
+
client.query({
|
|
388
|
+
text: sql,
|
|
389
|
+
rowMode: "array",
|
|
390
|
+
values: params
|
|
391
|
+
}, (err, result) => {
|
|
392
|
+
if (err) {
|
|
393
|
+
resume(Effect.fail(new SqlError({
|
|
394
|
+
reason: classifyError(err, "Failed to execute statement", "execute")
|
|
395
|
+
})));
|
|
396
|
+
} else {
|
|
397
|
+
resume(Effect.succeed(result.rows));
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
executeUnprepared(sql, params, transformRows) {
|
|
403
|
+
return this.execute(sql, params, transformRows);
|
|
404
|
+
}
|
|
405
|
+
executeStream(sql, params, transformRows) {
|
|
406
|
+
// oxlint-disable-next-line @typescript-eslint/no-this-alias
|
|
407
|
+
const self = this;
|
|
408
|
+
return Stream.fromChannel(Channel.fromTransform(Effect.fnUntraced(function* (_, scope) {
|
|
409
|
+
const client = yield* Scope.provide(self.reserve, scope);
|
|
410
|
+
yield* Scope.addFinalizer(scope, Effect.promise(() => cursor.close()));
|
|
411
|
+
const cursor = client.query(new Cursor(sql, params));
|
|
412
|
+
// @effect-diagnostics-next-line returnEffectInGen:off
|
|
413
|
+
return Effect.callback(resume => {
|
|
414
|
+
cursor.read(128, (err, rows) => {
|
|
415
|
+
if (err) {
|
|
416
|
+
resume(Effect.fail(new SqlError({
|
|
417
|
+
reason: classifyError(err, "Failed to execute statement", "stream")
|
|
418
|
+
})));
|
|
419
|
+
} else if (Arr.isArrayNonEmpty(rows)) {
|
|
420
|
+
resume(Effect.succeed(transformRows ? transformRows(rows) : rows));
|
|
421
|
+
} else {
|
|
422
|
+
resume(Cause.done());
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
})));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
385
429
|
const cancelEffects = /*#__PURE__*/new WeakMap();
|
|
386
430
|
const makeCancel = (pool, client) => {
|
|
387
431
|
if (cancelEffects.has(client)) {
|
|
@@ -403,17 +447,17 @@ const makeCancel = (pool, client) => {
|
|
|
403
447
|
* @category layers
|
|
404
448
|
* @since 1.0.0
|
|
405
449
|
*/
|
|
406
|
-
export const
|
|
450
|
+
export const layerFrom = acquire => Layer.effectServices(Effect.map(acquire, client => ServiceMap.make(PgClient, client).pipe(ServiceMap.add(Client.SqlClient, client)))).pipe(Layer.provide(Reactivity.layer));
|
|
407
451
|
/**
|
|
408
452
|
* @category layers
|
|
409
453
|
* @since 1.0.0
|
|
410
454
|
*/
|
|
411
|
-
export const
|
|
455
|
+
export const layerConfig = config => layerFrom(Effect.flatMap(Config.unwrap(config).asEffect(), make));
|
|
412
456
|
/**
|
|
413
457
|
* @category layers
|
|
414
458
|
* @since 1.0.0
|
|
415
459
|
*/
|
|
416
|
-
export const
|
|
460
|
+
export const layer = config => layerFrom(make(config));
|
|
417
461
|
/**
|
|
418
462
|
* @category constructor
|
|
419
463
|
* @since 1.0.0
|
|
@@ -447,4 +491,53 @@ const escape = /*#__PURE__*/Statement.defaultEscape("\"");
|
|
|
447
491
|
* @since 1.0.0
|
|
448
492
|
*/
|
|
449
493
|
const PgJson = /*#__PURE__*/Statement.custom("PgJson");
|
|
494
|
+
const ATTR_DB_SYSTEM_NAME = "db.system.name";
|
|
495
|
+
const ATTR_DB_NAMESPACE = "db.namespace";
|
|
496
|
+
const ATTR_SERVER_ADDRESS = "server.address";
|
|
497
|
+
const ATTR_SERVER_PORT = "server.port";
|
|
498
|
+
const pgCodeFromCause = cause => {
|
|
499
|
+
if (typeof cause !== "object" || cause === null || !("code" in cause)) {
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
const code = cause.code;
|
|
503
|
+
return typeof code === "string" ? code : undefined;
|
|
504
|
+
};
|
|
505
|
+
const classifyError = (cause, message, operation) => {
|
|
506
|
+
const props = {
|
|
507
|
+
cause,
|
|
508
|
+
message,
|
|
509
|
+
operation
|
|
510
|
+
};
|
|
511
|
+
const code = pgCodeFromCause(cause);
|
|
512
|
+
if (code !== undefined) {
|
|
513
|
+
if (code.startsWith("08")) {
|
|
514
|
+
return new ConnectionError(props);
|
|
515
|
+
}
|
|
516
|
+
if (code.startsWith("28")) {
|
|
517
|
+
return new AuthenticationError(props);
|
|
518
|
+
}
|
|
519
|
+
if (code === "42501") {
|
|
520
|
+
return new AuthorizationError(props);
|
|
521
|
+
}
|
|
522
|
+
if (code.startsWith("42")) {
|
|
523
|
+
return new SqlSyntaxError(props);
|
|
524
|
+
}
|
|
525
|
+
if (code.startsWith("23")) {
|
|
526
|
+
return new ConstraintError(props);
|
|
527
|
+
}
|
|
528
|
+
if (code === "40P01") {
|
|
529
|
+
return new DeadlockError(props);
|
|
530
|
+
}
|
|
531
|
+
if (code === "40001") {
|
|
532
|
+
return new SerializationError(props);
|
|
533
|
+
}
|
|
534
|
+
if (code === "55P03") {
|
|
535
|
+
return new LockTimeoutError(props);
|
|
536
|
+
}
|
|
537
|
+
if (code === "57014") {
|
|
538
|
+
return new StatementTimeoutError(props);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return new UnknownError(props);
|
|
542
|
+
};
|
|
450
543
|
//# sourceMappingURL=PgClient.js.map
|