@effect/sql-pg 4.0.0-beta.5 → 4.0.0-beta.50

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