@effect/sql-pg 4.0.0-rc.112 → 4.0.0-rc.113

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.
Files changed (60) hide show
  1. package/AGENTS.md +24 -9
  2. package/CLAUDE.md +24 -9
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +18 -5
  5. package/ai-docs/src/01_effect/01_basics/index.md +5 -3
  6. package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +1 -1
  7. package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +2 -2
  8. package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +2 -2
  9. package/ai-docs/src/03_stream/30_encoding.ts +5 -7
  10. package/ai-docs/src/08_observability/10_logging.ts +1 -1
  11. package/ai-docs/src/70_cli/10_basics.ts +7 -7
  12. package/ai-docs/src/71_ai/10_language-model.ts +2 -2
  13. package/ai-docs/src/71_ai/20_tools.ts +1 -1
  14. package/ai-docs/src/71_ai/30_chat.ts +1 -1
  15. package/dist/PgAuth.d.ts +24 -0
  16. package/dist/PgAuth.d.ts.map +1 -1
  17. package/dist/PgAuth.js +29 -2
  18. package/dist/PgAuth.js.map +1 -1
  19. package/dist/PgClient.d.ts +49 -82
  20. package/dist/PgClient.d.ts.map +1 -1
  21. package/dist/PgClient.js +58 -510
  22. package/dist/PgClient.js.map +1 -1
  23. package/dist/PgConnection.d.ts +177 -0
  24. package/dist/PgConnection.d.ts.map +1 -0
  25. package/dist/PgConnection.js +1786 -0
  26. package/dist/PgConnection.js.map +1 -0
  27. package/dist/PgPool.d.ts +107 -0
  28. package/dist/PgPool.d.ts.map +1 -0
  29. package/dist/PgPool.js +124 -0
  30. package/dist/PgPool.js.map +1 -0
  31. package/dist/PgProtocol.d.ts +63 -30
  32. package/dist/PgProtocol.d.ts.map +1 -1
  33. package/dist/PgProtocol.js +64 -22
  34. package/dist/PgProtocol.js.map +1 -1
  35. package/dist/PgTypes.d.ts +85 -17
  36. package/dist/PgTypes.d.ts.map +1 -1
  37. package/dist/PgTypes.js +166 -169
  38. package/dist/PgTypes.js.map +1 -1
  39. package/dist/index.d.ts +8 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +8 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/internal/connection.d.ts +2 -0
  44. package/dist/internal/connection.d.ts.map +1 -0
  45. package/dist/internal/connection.js +5 -0
  46. package/dist/internal/connection.js.map +1 -0
  47. package/dist/internal/sqlError.d.ts +10 -0
  48. package/dist/internal/sqlError.d.ts.map +1 -0
  49. package/dist/internal/sqlError.js +57 -0
  50. package/dist/internal/sqlError.js.map +1 -0
  51. package/package.json +6 -14
  52. package/src/PgAuth.ts +33 -2
  53. package/src/PgClient.ts +138 -681
  54. package/src/PgConnection.ts +2294 -0
  55. package/src/PgPool.ts +231 -0
  56. package/src/PgProtocol.ts +93 -50
  57. package/src/PgTypes.ts +254 -186
  58. package/src/index.ts +10 -0
  59. package/src/internal/connection.ts +26 -0
  60. package/src/internal/sqlError.ts +75 -0
package/dist/PgClient.js CHANGED
@@ -1,532 +1,141 @@
1
- /**
2
- * Connects Effect SQL to PostgreSQL using the `pg` package.
3
- *
4
- * This module provides constructors and layers for building a PostgreSQL
5
- * client from pool settings, a managed `pg.Client`, an existing `pg.Pool`, or
6
- * custom connection code. The client runs Effect SQL queries against
7
- * PostgreSQL, including transactions and streamed results, and adds helpers for
8
- * JSON values and LISTEN/NOTIFY messages. It also maps common PostgreSQL
9
- * failures, such as connection, authentication, constraint, timeout, and
10
- * deadlock errors, into Effect SQL errors.
11
- *
12
- * @since 4.0.0
13
- */
14
- import * as Arr from "effect/Array";
15
- import * as Cause from "effect/Cause";
16
- import * as Channel from "effect/Channel";
17
1
  import * as Config from "effect/Config";
18
2
  import * as Context from "effect/Context";
19
- import * as Duration from "effect/Duration";
20
3
  import * as Effect from "effect/Effect";
21
- import * as Fiber from "effect/Fiber";
22
4
  import * as Layer from "effect/Layer";
23
- import * as Number from "effect/Number";
24
- import * as Option from "effect/Option";
25
- import * as Queue from "effect/Queue";
26
- import * as RcRef from "effect/RcRef";
27
- import * as Redacted from "effect/Redacted";
28
- import * as Scope from "effect/Scope";
29
- import * as Semaphore from "effect/Semaphore";
30
5
  import * as Stream from "effect/Stream";
31
6
  import * as Reactivity from "effect/unstable/reactivity/Reactivity";
32
7
  import * as Client from "effect/unstable/sql/SqlClient";
33
- import { AuthenticationError, AuthorizationError, ConnectionError, ConstraintError, DeadlockError, LockTimeoutError, SerializationError, SqlError, SqlSyntaxError, StatementTimeoutError, UniqueViolation, UnknownError } from "effect/unstable/sql/SqlError";
34
8
  import * as Statement from "effect/unstable/sql/Statement";
35
- import * as Pg from "pg";
36
- import * as PgConnString from "pg-connection-string";
37
- import Cursor from "pg-cursor";
9
+ import { validateChannelName } from "./internal/sqlError.js";
10
+ import * as PgConnection from "./PgConnection.js";
11
+ import * as PgPool from "./PgPool.js";
12
+ import * as PgTypes from "./PgTypes.js";
38
13
  /**
39
- * Runtime type identifier used to mark `PgClient` values.
14
+ * The runtime type identifier for `PgClient`.
40
15
  *
41
16
  * @category type IDs
42
17
  * @since 4.0.0
43
18
  */
44
19
  export const TypeId = "~@effect/sql-pg/PgClient";
45
20
  /**
46
- * Service tag for the PostgreSQL client service.
47
- *
48
- * **When to use**
49
- *
50
- * Use to access or provide a PostgreSQL client through the Effect context.
21
+ * The service tag for `PgClient`.
51
22
  *
52
23
  * @category services
53
24
  * @since 4.0.0
54
25
  */
55
26
  export const PgClient = /*#__PURE__*/Context.Service("@effect/sql-pg/PgClient");
56
27
  /**
57
- * Creates a scoped PostgreSQL client backed by a managed `pg` connection pool.
58
- *
59
- * @category constructors
60
- * @since 4.0.0
61
- */
62
- export const make = options => fromPool({
63
- ...options,
64
- acquire: Effect.gen(function* () {
65
- const pool = new Pg.Pool({
66
- connectionString: options.url ? Redacted.value(options.url) : undefined,
67
- user: options.username,
68
- host: options.host,
69
- database: options.database,
70
- password: options.password ? Redacted.value(options.password) : undefined,
71
- ssl: options.ssl,
72
- port: options.port,
73
- ...(options.stream ? {
74
- stream: options.stream
75
- } : {}),
76
- connectionTimeoutMillis: options.connectTimeout ? Duration.toMillis(Duration.fromInputUnsafe(options.connectTimeout)) : undefined,
77
- idleTimeoutMillis: options.idleTimeout ? Duration.toMillis(Duration.fromInputUnsafe(options.idleTimeout)) : undefined,
78
- max: options.maxConnections,
79
- min: options.minConnections,
80
- maxLifetimeSeconds: options.connectionTTL ? Duration.toSeconds(Duration.fromInputUnsafe(options.connectionTTL)) : undefined,
81
- application_name: options.applicationName ?? "@effect/sql-pg",
82
- types: options.types
83
- });
84
- pool.on("error", _err => {});
85
- yield* Effect.acquireRelease(Effect.tryPromise({
86
- try: () => pool.query("SELECT 1"),
87
- catch: cause => new SqlError({
88
- reason: classifyError(cause, "PgClient: Failed to connect", "connect")
89
- })
90
- }), () => Effect.promise(() => pool.end()).pipe(Effect.timeoutOption(1000)), {
91
- interruptible: true
92
- }).pipe(Effect.timeoutOrElse({
93
- duration: options.connectTimeout ?? Duration.seconds(5),
94
- orElse: () => Effect.fail(new SqlError({
95
- reason: new ConnectionError({
96
- cause: new Error("Connection timed out"),
97
- message: "PgClient: Connection timed out",
98
- operation: "connect"
99
- })
100
- }))
101
- }));
102
- return pool;
103
- })
104
- });
105
- /**
106
- * Creates a scoped PostgreSQL client backed by a managed single `pg` client, optionally acquiring a separate client for streaming and LISTEN operations.
107
- *
108
- * @category constructors
109
- * @since 4.0.0
110
- */
111
- export const makeClient = options => {
112
- function onError() {}
113
- return fromClient({
114
- ...options,
115
- acquire: Effect.acquireRelease(Effect.tryPromise({
116
- try: async () => {
117
- const client = new Pg.Client({
118
- connectionString: options.url ? Redacted.value(options.url) : undefined,
119
- user: options.username,
120
- host: options.host,
121
- database: options.database,
122
- password: options.password ? Redacted.value(options.password) : undefined,
123
- ssl: options.ssl,
124
- port: options.port,
125
- ...(options.stream ? {
126
- stream: options.stream
127
- } : {}),
128
- application_name: options.applicationName ?? "@effect/sql-pg",
129
- types: options.types
130
- });
131
- client.on("error", onError);
132
- await client.connect();
133
- return client;
134
- },
135
- catch: cause => new SqlError({
136
- reason: classifyError(cause, "PgClient: Failed to connect", "connect")
137
- })
138
- }), client => Effect.promise(() => {
139
- client.off("error", onError);
140
- return client.end();
141
- }).pipe(Effect.timeoutOption(1000)), {
142
- interruptible: true
143
- }).pipe(Effect.timeoutOrElse({
144
- duration: options.connectTimeout ?? Duration.seconds(5),
145
- orElse: () => Effect.fail(new SqlError({
146
- reason: new ConnectionError({
147
- cause: new Error("Connection timed out"),
148
- message: "PgClient: Connection timed out",
149
- operation: "connect"
150
- })
151
- }))
152
- })),
153
- acquireForStream: options.acquireForStream ?? false
154
- });
155
- };
156
- /**
157
- * Builds a PostgreSQL client from a scoped `pg` pool acquisition effect, deriving transaction, streaming, and LISTEN/NOTIFY support from that pool.
28
+ * Creates a scoped PostgreSQL client backed by a connection pool.
158
29
  *
159
30
  * @category constructors
160
31
  * @since 4.0.0
161
32
  */
162
- export const fromPool = /*#__PURE__*/Effect.fnUntraced(function* (options) {
163
- const pool = yield* options.acquire;
164
- const makeConection = client => new ConnectionImpl(function runWithClient(f) {
165
- if (client !== undefined) {
166
- return Effect.callback(resume => {
167
- f(client, resume);
168
- return makeCancel(pool, client);
169
- });
170
- }
171
- return Effect.callback(resume => {
172
- let done = false;
173
- let cancel = undefined;
174
- let client = undefined;
175
- function onError(cause) {
176
- cleanup(cause);
177
- resume(Effect.fail(new SqlError({
178
- reason: classifyError(cause, "Connection error", "acquireConnection")
179
- })));
180
- }
181
- function cleanup(cause) {
182
- if (!done) client?.release(cause);
183
- done = true;
184
- client?.off("error", onError);
185
- }
186
- pool.connect((cause, client_) => {
187
- if (cause) {
188
- return resume(Effect.fail(new SqlError({
189
- reason: classifyError(cause, "Failed to acquire connection", "acquireConnection")
190
- })));
191
- } else if (!client_) {
192
- return resume(Effect.fail(new SqlError({
193
- reason: new ConnectionError({
194
- message: "Failed to acquire connection",
195
- cause: new Error("No client returned"),
196
- operation: "acquireConnection"
197
- })
198
- })));
199
- } else if (done) {
200
- client_.release();
201
- return;
202
- }
203
- client = client_;
204
- client.once("error", onError);
205
- cancel = makeCancel(pool, client);
206
- f(client, eff => {
207
- cleanup();
208
- resume(eff);
209
- });
210
- });
211
- return Effect.suspend(() => {
212
- if (!cancel) {
213
- cleanup();
214
- return Effect.void;
215
- }
216
- return Effect.ensuring(cancel, Effect.sync(cleanup));
217
- });
218
- });
219
- }, client ? Effect.succeed(client) : reserveRaw);
220
- const reserveRaw = Effect.callback(resume => {
221
- const fiber = Fiber.getCurrent();
222
- const scope = Context.getUnsafe(fiber.context, Scope.Scope);
223
- let cause = undefined;
224
- function onError(cause_) {
225
- cause = cause_;
226
- }
227
- pool.connect((err, client, release) => {
228
- if (err) {
229
- return resume(Effect.fail(new SqlError({
230
- reason: classifyError(err, "Failed to acquire connection for transaction", "acquireConnection")
231
- })));
232
- } else if (!client) {
233
- return resume(Effect.fail(new SqlError({
234
- reason: new ConnectionError({
235
- message: "Failed to acquire connection for transaction",
236
- cause: new Error("No client returned"),
237
- operation: "acquireConnection"
238
- })
239
- })));
240
- }
241
- client.on("error", onError);
242
- resume(Effect.as(Scope.addFinalizer(scope, Effect.sync(() => {
243
- client.off("error", onError);
244
- release(cause);
245
- })), client));
246
- });
247
- });
248
- const reserve = Effect.map(reserveRaw, makeConection);
249
- const onListenClientError = _ => {};
250
- const listenAcquirer = yield* RcRef.make({
251
- acquire: Effect.acquireRelease(Effect.tryPromise({
252
- try: async () => {
253
- const client = new Pg.Client(pool.options);
254
- await client.connect();
255
- client.on("error", onListenClientError);
256
- return client;
257
- },
258
- catch: cause => new SqlError({
259
- reason: classifyError(cause, "Failed to acquire connection for listen", "acquireConnection")
260
- })
261
- }), client => Effect.promise(() => {
262
- client.off("error", onListenClientError);
263
- return client.end();
264
- }).pipe(Effect.timeoutOption(1000)), {
265
- interruptible: true
266
- })
267
- });
268
- let config = {
269
- url: pool.options.connectionString ? Redacted.make(pool.options.connectionString) : undefined,
270
- host: pool.options.host,
271
- port: pool.options.port,
272
- database: pool.options.database,
273
- username: pool.options.user,
274
- password: typeof pool.options.password === "string" ? Redacted.make(pool.options.password) : undefined,
275
- ssl: pool.options.ssl,
276
- applicationName: pool.options.application_name,
277
- types: pool.options.types
278
- };
279
- if (pool.options.connectionString) {
280
- // @effect-diagnostics-next-line tryCatchInEffectGen:off
281
- try {
282
- const parsed = PgConnString.parse(pool.options.connectionString);
283
- config = {
284
- ...config,
285
- host: config.host ?? parsed.host ?? undefined,
286
- port: config.port ?? (parsed.port ? Option.getOrUndefined(Number.parse(parsed.port)) : undefined),
287
- username: config.username ?? parsed.user ?? undefined,
288
- password: config.password ?? (parsed.password ? Redacted.make(parsed.password) : undefined),
289
- database: config.database ?? parsed.database ?? undefined
290
- };
291
- } catch {
292
- //
293
- }
294
- }
295
- return yield* makeWith({
296
- acquirer: Effect.succeed(makeConection()),
297
- transactionAcquirer: reserve,
298
- listenAcquirer: RcRef.get(listenAcquirer),
299
- config,
300
- spanAttributes: options.spanAttributes,
301
- transformResultNames: options.transformResultNames,
302
- transformQueryNames: options.transformQueryNames,
303
- transformJson: options.transformJson
304
- });
305
- });
33
+ export const make = options => Effect.flatMap(PgPool.make(options), pool => makeImpl({
34
+ acquirer: Effect.map(pool.get, makeConnection),
35
+ borrower: f => pool.use(connection => f(makeConnection(connection))),
36
+ transactionAcquirer: Effect.map(pool.reserve, makeConnection),
37
+ listenAcquirer: pool.reserve,
38
+ config: options
39
+ }));
306
40
  /**
307
- * Builds a PostgreSQL client from a scoped `pg` client acquisition effect, serializing access when sharing the client and optionally using separate clients for streams and LISTEN.
41
+ * Creates a scoped PostgreSQL client backed by one connection.
308
42
  *
309
43
  * @category constructors
310
44
  * @since 4.0.0
311
45
  */
312
- export const fromClient = /*#__PURE__*/Effect.fnUntraced(function* (options) {
313
- function onError() {}
314
- const acquireWithErrorHandler = options.acquire.pipe(Effect.tap(client => {
315
- client.on("error", onError);
316
- return Effect.addFinalizer(() => {
317
- client.off("error", onError);
318
- return Effect.void;
319
- });
320
- }));
321
- const client = yield* acquireWithErrorHandler;
322
- const semaphore = Semaphore.makeUnsafe(1);
323
- let streamClient = options.acquireForStream ? acquireWithErrorHandler : Effect.acquireRelease(Effect.as(semaphore.take(1), client), () => semaphore.release(1));
324
- const makeConection = client => new ConnectionImpl(function runWithClient(f) {
325
- return Effect.callback(resume => {
326
- f(client, resume);
327
- });
328
- }, streamClient);
329
- const connection = makeConection(client);
330
- const acquirer = semaphore.withPermit(Effect.succeed(connection));
331
- const transactionAcquirer = Effect.uninterruptibleMask(restore => {
332
- const fiber = Fiber.getCurrent();
333
- const scope = Context.getUnsafe(fiber.context, Scope.Scope);
334
- return Effect.as(Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))), connection);
335
- });
336
- const config = {
337
- ...options,
338
- host: client.host,
339
- port: client.port,
340
- database: client.database,
341
- username: client.user,
342
- password: typeof client.password === "string" ? Redacted.make(client.password) : undefined,
343
- ssl: client.ssl
344
- };
345
- return yield* makeWith({
346
- acquirer,
347
- transactionAcquirer,
348
- listenAcquirer: streamClient,
349
- config,
350
- spanAttributes: options.spanAttributes,
351
- transformResultNames: options.transformResultNames,
352
- transformQueryNames: options.transformQueryNames,
353
- transformJson: options.transformJson
354
- });
355
- });
46
+ export const makeClient = options => Effect.flatMap(PgConnection.make(options), connection => makeImpl({
47
+ acquirer: Effect.succeed(makeConnection(connection, options.acquireForStream ? PgConnection.make(options) : undefined)),
48
+ transactionAcquirer: Effect.map(connection.pin, makeConnection),
49
+ listenAcquirer: options.acquireForStream ? PgConnection.make(options) : connection.pin,
50
+ config: options
51
+ }));
356
52
  /**
357
- * Creates a `PgClient` from SQL connection acquirers, a LISTEN acquirer, client configuration, and transformation options.
358
- *
359
- * **When to use**
360
- *
361
- * Use to build a PostgreSQL client from custom connection acquisition logic
362
- * instead of the built-in pool or single-client constructors.
363
- *
364
- * @category constructors
365
- * @since 4.0.0
53
+ * Builds the shared SQL facade around native connection acquirers.
366
54
  */
367
- export const makeWith = /*#__PURE__*/Effect.fnUntraced(function* (options) {
368
- const compiler = makeCompiler(options.transformQueryNames, options.transformJson);
369
- const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames, options.transformJson).array : undefined;
55
+ const makeImpl = /*#__PURE__*/Effect.fnUntraced(function* (options) {
370
56
  const config = options.config;
57
+ const compiler = makeCompiler(config.transformQueryNames, config.transformJson);
58
+ const transformRows = config.transformResultNames ? Statement.defaultTransforms(config.transformResultNames, config.transformJson).array : undefined;
59
+ const listen = channel => Effect.flatMap(options.listenAcquirer, connection => connection.listen(channel));
371
60
  return Object.assign(yield* Client.make({
372
61
  acquirer: options.acquirer,
62
+ borrower: options.borrower,
63
+ // Postgres prepares transaction control like anything else, and a client
64
+ // with preparation turned off falls back to the unnamed path anyway.
65
+ prepareTransactionControls: true,
373
66
  transactionAcquirer: options.transactionAcquirer,
374
67
  compiler,
375
- 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]],
68
+ spanAttributes: [...(config.spanAttributes ? Object.entries(config.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]],
376
69
  transformRows
377
70
  }), {
378
71
  [TypeId]: TypeId,
379
- config: options.config,
72
+ config,
380
73
  json: _ => Statement.fragment([PgJson(_)]),
381
- listen: channel => Stream.callback(Effect.fnUntraced(function* (queue) {
382
- const client = yield* options.listenAcquirer;
383
- function onNotification(msg) {
384
- if (msg.channel === channel && msg.payload) {
385
- Queue.offerUnsafe(queue, msg.payload);
386
- }
387
- }
388
- yield* Effect.addFinalizer(() => Effect.promise(() => {
389
- client.off("notification", onNotification);
390
- return client.query(`UNLISTEN ${Pg.escapeIdentifier(channel)}`);
391
- }));
392
- yield* Effect.tryPromise({
393
- try: () => client.query(`LISTEN ${Pg.escapeIdentifier(channel)}`),
394
- catch: cause => new SqlError({
395
- reason: classifyError(cause, "Failed to listen", "listen")
396
- })
397
- });
398
- client.on("notification", onNotification);
399
- })),
400
- notify: (channel, payload) => Effect.asVoid(Effect.scoped(Effect.flatMap(options.acquirer, conn => conn.executeRaw(`SELECT pg_notify($1, $2)`, [channel, payload]))))
74
+ listen,
75
+ notify: (channel, payload) => {
76
+ const channelError = validateChannelName(channel, "notify");
77
+ return channelError !== undefined ? Effect.fail(channelError) : Effect.asVoid(Effect.scoped(Effect.flatMap(options.acquirer, conn => conn.executeRaw(`SELECT pg_notify($1, $2)`, [channel, payload]))));
78
+ }
401
79
  });
402
80
  });
403
81
  class ConnectionImpl {
404
- constructor(runWithClient, reserve) {
405
- this.runWithClient = runWithClient;
406
- this.reserve = reserve;
82
+ connection;
83
+ streamAcquirer;
84
+ constructor(connection, streamAcquirer) {
85
+ this.connection = connection;
86
+ this.streamAcquirer = streamAcquirer;
407
87
  }
408
- runWithClient;
409
- reserve;
410
- run(query, params) {
411
- return this.runWithClient((client, resume) => {
412
- client.query(query, params, (err, result) => {
413
- if (err) {
414
- resume(Effect.fail(new SqlError({
415
- reason: classifyError(err, "Failed to execute statement", "execute")
416
- })));
417
- } else {
418
- // Multi-statement queries return an array of results
419
- resume(Effect.succeed(Array.isArray(result) ? result.map(r => r.rows ?? []) : result.rows ?? []));
420
- }
421
- });
422
- });
88
+ run(query, params, prepare = true) {
89
+ return Effect.map(this.connection.query(query, params, prepare), result => result.rows);
423
90
  }
424
91
  execute(sql, params, transformRows) {
425
92
  return transformRows ? Effect.map(this.run(sql, params), transformRows) : this.run(sql, params);
426
93
  }
427
94
  executeRaw(sql, params) {
428
- return this.runWithClient((client, resume) => {
429
- client.query(sql, params, (err, result) => {
430
- if (err) {
431
- resume(Effect.fail(new SqlError({
432
- reason: classifyError(err, "Failed to execute statement", "execute")
433
- })));
434
- } else {
435
- resume(Effect.succeed(result));
436
- }
437
- });
438
- });
95
+ return this.connection.query(sql, params);
439
96
  }
440
97
  executeWithoutTransform(sql, params) {
441
98
  return this.run(sql, params);
442
99
  }
443
100
  executeValues(sql, params) {
444
- return this.runWithClient((client, resume) => {
445
- client.query({
446
- text: sql,
447
- rowMode: "array",
448
- values: params
449
- }, (err, result) => {
450
- if (err) {
451
- resume(Effect.fail(new SqlError({
452
- reason: classifyError(err, "Failed to execute statement", "execute")
453
- })));
454
- } else {
455
- resume(Effect.succeed(result.rows));
456
- }
457
- });
458
- });
101
+ return this.connection.queryValues(sql, params);
459
102
  }
460
103
  executeValuesUnprepared(sql, params) {
461
- return this.executeValues(sql, params);
104
+ return this.connection.queryValues(sql, params, false);
462
105
  }
463
106
  executeUnprepared(sql, params, transformRows) {
464
- return this.execute(sql, params, transformRows);
107
+ const operation = this.run(sql, params, false);
108
+ return transformRows ? Effect.map(operation, transformRows) : operation;
465
109
  }
466
110
  executeStream(sql, params, transformRows) {
467
- // oxlint-disable-next-line @typescript-eslint/no-this-alias
468
- const self = this;
469
- return Stream.fromChannel(Channel.fromTransform(Effect.fnUntraced(function* (_, scope) {
470
- const client = yield* Scope.provide(self.reserve, scope);
471
- yield* Scope.addFinalizer(scope, Effect.promise(() => cursor.close()));
472
- const cursor = client.query(new Cursor(sql, params));
473
- // @effect-diagnostics-next-line returnEffectInGen:off
474
- return Effect.callback(resume => {
475
- cursor.read(128, (err, rows) => {
476
- if (err) {
477
- resume(Effect.fail(new SqlError({
478
- reason: classifyError(err, "Failed to execute statement", "stream")
479
- })));
480
- } else if (Arr.isArrayNonEmpty(rows)) {
481
- resume(Effect.succeed(transformRows ? transformRows(rows) : rows));
482
- } else {
483
- resume(Cause.done());
484
- }
485
- });
486
- });
487
- })));
111
+ const stream = this.streamAcquirer === undefined ? this.connection.stream(sql, params) : Stream.unwrap(Effect.map(this.streamAcquirer, connection => connection.stream(sql, params)));
112
+ return transformRows ? Stream.mapArray(stream, rows => transformRows(rows)) : stream;
488
113
  }
489
114
  }
490
- const cancelEffects = /*#__PURE__*/new WeakMap();
491
- const makeCancel = (pool, client) => {
492
- if (cancelEffects.has(client)) {
493
- return cancelEffects.get(client);
494
- }
495
- const processId = client.processID;
496
- const eff = processId !== undefined
497
- // query cancelation is best-effort, so we don't fail if it doesn't work
498
- ? Effect.callback(resume => {
499
- if (pool.ending) return resume(Effect.void);
500
- pool.query(`SELECT pg_cancel_backend(${processId})`, () => {
501
- resume(Effect.void);
502
- });
503
- }).pipe(Effect.interruptible, Effect.timeoutOption(5000)) : undefined;
504
- cancelEffects.set(client, eff);
505
- return eff;
506
- };
115
+ const makeConnection = (connection, streamAcquirer) => new ConnectionImpl(connection, streamAcquirer);
507
116
  /**
508
- * Creates a layer from an effect that acquires a `PgClient`, providing both `PgClient` and `SqlClient`.
117
+ * Provides both `PgClient` and `SqlClient` from an acquisition effect.
509
118
  *
510
119
  * @category layers
511
120
  * @since 4.0.0
512
121
  */
513
122
  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));
514
123
  /**
515
- * Creates a layer from a `Config`-wrapped PostgreSQL pool configuration, providing both `PgClient` and `SqlClient`.
124
+ * Creates a client layer from wrapped pool configuration.
516
125
  *
517
126
  * @category layers
518
127
  * @since 4.0.0
519
128
  */
520
129
  export const layerConfig = config => layerFrom(Effect.flatMap(Config.unwrap(config), make));
521
130
  /**
522
- * Creates a layer from a concrete PostgreSQL pool configuration, providing both `PgClient` and `SqlClient`.
131
+ * Creates a client layer from pool configuration.
523
132
  *
524
133
  * @category layers
525
134
  * @since 4.0.0
526
135
  */
527
136
  export const layer = config => layerFrom(make(config));
528
137
  /**
529
- * Creates the PostgreSQL statement compiler, using `$1` placeholders, double-quoted identifiers, PostgreSQL returning clauses, and optional JSON value transformation.
138
+ * Creates the PostgreSQL statement compiler.
530
139
  *
531
140
  * @category constructors
532
141
  * @since 4.0.0
@@ -548,7 +157,8 @@ export const makeCompiler = (transform, transformJson = true) => {
548
157
  switch (type.kind) {
549
158
  case "PgJson":
550
159
  {
551
- return [placeholder(undefined), [withoutTransform || transformValue === undefined ? type.paramA : transformValue(type.paramA)]];
160
+ const value = withoutTransform || transformValue === undefined ? type.paramA : transformValue(type.paramA);
161
+ return [placeholder(undefined), [PgTypes.jsonb(value)]];
552
162
  }
553
163
  }
554
164
  }
@@ -564,66 +174,4 @@ const ATTR_DB_SYSTEM_NAME = "db.system.name";
564
174
  const ATTR_DB_NAMESPACE = "db.namespace";
565
175
  const ATTR_SERVER_ADDRESS = "server.address";
566
176
  const ATTR_SERVER_PORT = "server.port";
567
- const pgCodeFromCause = cause => {
568
- if (typeof cause !== "object" || cause === null || !("code" in cause)) {
569
- return undefined;
570
- }
571
- const code = cause.code;
572
- return typeof code === "string" ? code : undefined;
573
- };
574
- const pgConstraintFromCause = cause => {
575
- if (typeof cause !== "object" || cause === null || !("constraint" in cause)) {
576
- return "unknown";
577
- }
578
- const constraint = cause.constraint;
579
- if (typeof constraint !== "string") {
580
- return "unknown";
581
- }
582
- const normalized = constraint.trim();
583
- return normalized.length === 0 ? "unknown" : normalized;
584
- };
585
- const classifyError = (cause, message, operation) => {
586
- const props = {
587
- cause,
588
- message,
589
- operation
590
- };
591
- const code = pgCodeFromCause(cause);
592
- if (code !== undefined) {
593
- if (code.startsWith("08")) {
594
- return new ConnectionError(props);
595
- }
596
- if (code.startsWith("28")) {
597
- return new AuthenticationError(props);
598
- }
599
- if (code === "42501") {
600
- return new AuthorizationError(props);
601
- }
602
- if (code.startsWith("42")) {
603
- return new SqlSyntaxError(props);
604
- }
605
- if (code === "23505") {
606
- return new UniqueViolation({
607
- ...props,
608
- constraint: pgConstraintFromCause(cause)
609
- });
610
- }
611
- if (code.startsWith("23")) {
612
- return new ConstraintError(props);
613
- }
614
- if (code === "40P01") {
615
- return new DeadlockError(props);
616
- }
617
- if (code === "40001") {
618
- return new SerializationError(props);
619
- }
620
- if (code === "55P03") {
621
- return new LockTimeoutError(props);
622
- }
623
- if (code === "57014") {
624
- return new StatementTimeoutError(props);
625
- }
626
- }
627
- return new UnknownError(props);
628
- };
629
177
  //# sourceMappingURL=PgClient.js.map