@prisma-next/driver-postgres 0.4.0-dev.9 → 0.4.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/postgres-driver.ts","../src/exports/runtime.ts"],"sourcesContent":[],"mappings":";;;;;KAsBY,sBAAsB,iBAAiB,kBAAkB,cAAc;KAEvE,eAAA;;EAFA,SAAA,GAAA,EAAW,MAAA;CAAW,GAAA;EAAiB,SAAA,IAAA,EAAA,QAAA;EAAgC,SAAA,IAAA,EAIrC,IAJqC;CAAd,GAAA;EAAa,SAAA,IAAA,EAAA,UAAA;EAEtE,SAAA,MAAA,EAGsC,MAHvB;AAK3B,CAAA;AAKU,UALO,qBAAA,CAKc;EACD,SAAA,SAAA,CAAA,EAAA,MAAA;EAAmB,SAAA,QAAA,CAAA,EAAA,OAAA;;UADvC,qBAAA,CAE+B;EAG7B,SAAA,OAAA,EAAA;YAJkB;;UAAmB;ECjBrC,CAAA;EAAwB,SAAA,MAAA,CAAA,EDkBhB,qBClBgB,GAAA,SAAA;;AAClC,KDoBU,2BAAA,GAA8B,ICpBxC,CDoB6C,qBCpB7C,EAAA,SAAA,CAAA;;;KADU,qBAAA,GAAwB,2CAClC,UAAU;cA6HN,iCAAiC,2CAGrC,6BACA"}
1
+ {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/postgres-driver.ts","../src/exports/runtime.ts"],"sourcesContent":[],"mappings":";;;;;KAsBY,sBAAsB,iBAAiB,kBAAkB,cAAc;KAEvE,eAAA;;EAFA,SAAA,GAAA,EAAW,MAAA;CAAW,GAAA;EAAiB,SAAA,IAAA,EAAA,QAAA;EAAgC,SAAA,IAAA,EAIrC,IAJqC;CAAd,GAAA;EAAa,SAAA,IAAA,EAAA,UAAA;EAEtE,SAAA,MAAA,EAGsC,MAHvB;AAK3B,CAAA;AAKU,UALO,qBAAA,CAKc;EACD,SAAA,SAAA,CAAA,EAAA,MAAA;EAAmB,SAAA,QAAA,CAAA,EAAA,OAAA;;UADvC,qBAAA,CAE+B;EAG7B,SAAA,OAAA,EAAA;YAJkB;;UAAmB;ECjBrC,CAAA;EAAwB,SAAA,MAAA,CAAA,EDkBhB,qBClBgB,GAAA,SAAA;;AAClC,KDoBU,2BAAA,GAA8B,ICpBxC,CDoB6C,qBCpB7C,EAAA,SAAA,CAAA;;;KADU,qBAAA,GAAwB,2CAClC,UAAU;cAsKN,iCAAiC,2CAGrC,6BACA"}
package/dist/runtime.mjs CHANGED
@@ -92,10 +92,12 @@ var PostgresQueryable = class {
92
92
  var PostgresConnectionImpl = class extends PostgresQueryable {
93
93
  #connection;
94
94
  #onRelease;
95
- constructor(connection, options, onRelease) {
95
+ #onDestroy;
96
+ constructor(connection, options, onRelease, onDestroy) {
96
97
  super(options);
97
98
  this.#connection = connection;
98
99
  this.#onRelease = onRelease;
100
+ this.#onDestroy = onDestroy;
99
101
  }
100
102
  acquireClient() {
101
103
  return Promise.resolve(this.#connection);
@@ -108,9 +110,25 @@ var PostgresConnectionImpl = class extends PostgresQueryable {
108
110
  return new PostgresTransactionImpl(this.#connection, this.options);
109
111
  }
110
112
  async release() {
111
- if ("release" in this.#connection) this.#connection.release();
112
- this.#onRelease?.();
113
+ const conn = this.#connection;
114
+ if ("release" in conn) conn.release();
115
+ const onRelease = this.#onRelease;
113
116
  this.#onRelease = void 0;
117
+ this.#onDestroy = void 0;
118
+ onRelease?.();
119
+ }
120
+ async destroy(reason) {
121
+ const onDestroy = this.#onDestroy;
122
+ const onRelease = this.#onRelease;
123
+ this.#onDestroy = void 0;
124
+ this.#onRelease = void 0;
125
+ const conn = this.#connection;
126
+ if ("release" in conn) {
127
+ const releaseArg = reason instanceof Error ? reason : /* @__PURE__ */ new Error("Connection destroyed");
128
+ conn.release(releaseArg);
129
+ }
130
+ if (onDestroy) await onDestroy(reason);
131
+ else onRelease?.();
114
132
  }
115
133
  };
116
134
  var PostgresTransactionImpl = class extends PostgresQueryable {
@@ -181,7 +199,13 @@ var PostgresDirectDriverImpl = class extends PostgresQueryable {
181
199
  async acquireConnection() {
182
200
  const releaseLease = await this.#connectionMutex.lock();
183
201
  try {
184
- return new PostgresConnectionImpl(await this.acquireClient(), this.options, releaseLease);
202
+ return new PostgresConnectionImpl(await this.acquireClient(), this.options, releaseLease, async () => {
203
+ try {
204
+ await this.#closeWhileHoldingLease();
205
+ } finally {
206
+ releaseLease();
207
+ }
208
+ });
185
209
  } catch (error) {
186
210
  releaseLease();
187
211
  throw error;
@@ -190,14 +214,17 @@ var PostgresDirectDriverImpl = class extends PostgresQueryable {
190
214
  async close() {
191
215
  const releaseLease = await this.#connectionMutex.lock();
192
216
  try {
193
- if (this.#closed) return;
194
- this.#closed = true;
195
- await this.directClient.end();
196
- this.#connected = false;
217
+ await this.#closeWhileHoldingLease();
197
218
  } finally {
198
219
  releaseLease();
199
220
  }
200
221
  }
222
+ async #closeWhileHoldingLease() {
223
+ if (this.#closed) return;
224
+ this.#closed = true;
225
+ await this.directClient.end();
226
+ this.#connected = false;
227
+ }
201
228
  async acquireClient() {
202
229
  if (this.#connected) return this.directClient;
203
230
  if (this.#connectPromise !== void 0) {
@@ -301,7 +328,46 @@ var PostgresUnboundDriverImpl = class {
301
328
  this.#closed = false;
302
329
  }
303
330
  async acquireConnection() {
304
- return this.#requireDelegate().acquireConnection();
331
+ const delegate = this.#requireDelegate();
332
+ const connection = await delegate.acquireConnection();
333
+ return this.#wrapConnection(connection, delegate);
334
+ }
335
+ /**
336
+ * Wraps an acquired connection so that teardown paths which close the
337
+ * underlying delegate (notably `destroy()` on a pgClient binding, where
338
+ * the single socket means a destroyed connection invalidates the driver)
339
+ * also reset our own `#delegate` reference. Without this, a failed
340
+ * transaction rollback would leave the outer unbound wrapper reporting
341
+ * `connected` while routing subsequent work to an already-ended delegate.
342
+ */
343
+ #wrapConnection(connection, delegate) {
344
+ const syncDelegateState = () => {
345
+ if (this.#delegate === delegate && delegate.state === "closed") {
346
+ this.#delegate = null;
347
+ this.#closed = true;
348
+ }
349
+ };
350
+ const wrapped = {
351
+ beginTransaction: connection.beginTransaction.bind(connection),
352
+ execute: connection.execute.bind(connection),
353
+ query: connection.query.bind(connection),
354
+ release: async () => {
355
+ try {
356
+ await connection.release();
357
+ } finally {
358
+ syncDelegateState();
359
+ }
360
+ },
361
+ destroy: async (reason) => {
362
+ try {
363
+ await connection.destroy(reason);
364
+ } finally {
365
+ syncDelegateState();
366
+ }
367
+ }
368
+ };
369
+ if (connection.explain) wrapped.explain = connection.explain.bind(connection);
370
+ return wrapped;
305
371
  }
306
372
  async close() {
307
373
  const delegate = this.#delegate;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.mjs","names":["#queue","releaseLock: (() => void) | undefined","#connection","#onRelease","#closed","#connectionMutex","#connected","#connectPromise","error: unknown","#cursorOpts","#delegate","#closed","#requireDelegate","postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n>"],"sources":["../src/callback-to-promise.ts","../src/postgres-driver.ts","../src/exports/runtime.ts"],"sourcesContent":["/**\n * Wraps a Node-style callback function into a Promise.\n * Supports both (err) => void and (err, result) => void patterns.\n */\nexport function callbackToPromise(\n fn: (callback: (err: Error | null | undefined) => void) => void,\n): Promise<void>;\nexport function callbackToPromise<T>(\n fn: (callback: (err: Error | null | undefined, result: T) => void) => void,\n): Promise<T>;\nexport function callbackToPromise<T = void>(\n fn: (callback: (err: Error | null | undefined, result?: T) => void) => void,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n fn((err, result) => {\n if (err) {\n reject(err);\n return;\n }\n resolve(result as T);\n });\n });\n}\n","import type {\n SqlConnection,\n SqlDriver,\n SqlDriverState,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryable,\n SqlQueryResult,\n SqlTransaction,\n} from '@prisma-next/sql-relational-core/ast';\nimport type {\n Client,\n QueryResult as PgQueryResult,\n PoolClient,\n Pool as PoolType,\n QueryResultRow,\n} from 'pg';\nimport { Pool } from 'pg';\nimport Cursor from 'pg-cursor';\nimport { callbackToPromise } from './callback-to-promise';\nimport { isAlreadyConnectedError, isPostgresError, normalizePgError } from './normalize-error';\n\nexport type QueryResult<T extends QueryResultRow = QueryResultRow> = PgQueryResult<T>;\n\nexport type PostgresBinding =\n | { readonly kind: 'url'; readonly url: string }\n | { readonly kind: 'pgPool'; readonly pool: PoolType }\n | { readonly kind: 'pgClient'; readonly client: Client };\n\nexport interface PostgresCursorOptions {\n readonly batchSize?: number;\n readonly disabled?: boolean;\n}\n\ninterface PostgresDriverOptions {\n readonly connect: { client: Client } | { pool: PoolType };\n readonly cursor?: PostgresCursorOptions | undefined;\n}\n\nexport type PostgresDriverCreateOptions = Omit<PostgresDriverOptions, 'connect'>;\n\nconst DEFAULT_BATCH_SIZE = 100;\n\ntype ConnectionOptions =\n | { readonly cursorDisabled: true }\n | { readonly cursorBatchSize: number; readonly cursorDisabled: false };\n\nclass AsyncMutex {\n #queue = Promise.resolve();\n\n async lock(): Promise<() => void> {\n const previous = this.#queue;\n let releaseLock: (() => void) | undefined;\n this.#queue = new Promise<void>((resolve) => {\n releaseLock = resolve;\n });\n await previous;\n return () => {\n releaseLock?.();\n releaseLock = undefined;\n };\n }\n}\n\nabstract class PostgresQueryable<C extends PoolClient | Client = PoolClient | Client>\n implements SqlQueryable\n{\n abstract acquireClient(): Promise<C>;\n abstract releaseClient(client: C): Promise<void>;\n\n protected readonly options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.options = options;\n }\n\n async *execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const client = await this.acquireClient();\n try {\n if (!this.options.cursorDisabled) {\n try {\n for await (const row of this.executeWithCursor(\n client,\n request.sql,\n request.params,\n this.options.cursorBatchSize,\n )) {\n yield row as Row;\n }\n return;\n } catch (cursorError) {\n if (!(cursorError instanceof Error)) {\n throw cursorError;\n }\n // Check if this is a pg error - if so, normalize and throw\n // Otherwise, fall back to buffered mode for cursor-specific errors\n if (isPostgresError(cursorError)) {\n throw normalizePgError(cursorError);\n }\n // Not a pg error - cursor-specific error, fall back to buffered mode\n }\n }\n\n for await (const row of this.executeBuffered(client, request.sql, request.params)) {\n yield row as Row;\n }\n } catch (error) {\n throw normalizePgError(error);\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n // SQL is generated by Prisma Next planners (or validated raw paths), so\n // EXPLAIN prefixing preserves the same statement semantics.\n const text = `EXPLAIN (FORMAT JSON) ${request.sql}`;\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(text, request.params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return { rows: result.rows as ReadonlyArray<Record<string, unknown>> };\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(sql, params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return result as unknown as SqlQueryResult<Row>;\n } finally {\n await this.releaseClient(client);\n }\n }\n\n private async *executeWithCursor(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n cursorBatchSize: number,\n ): AsyncIterable<Record<string, unknown>> {\n const cursor = client.query(new Cursor(sql, params as unknown[] | undefined));\n\n try {\n while (true) {\n const rows = await readCursor(cursor, cursorBatchSize);\n if (rows.length === 0) {\n break;\n }\n\n for (const row of rows) {\n yield row;\n }\n }\n } finally {\n await closeCursor(cursor);\n }\n }\n\n private async *executeBuffered(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n ): AsyncIterable<Record<string, unknown>> {\n const result = await client.query(sql, params as unknown[] | undefined);\n for (const row of result.rows as Record<string, unknown>[]) {\n yield row;\n }\n }\n}\n\nclass PostgresConnectionImpl extends PostgresQueryable implements SqlConnection {\n #connection: PoolClient | Client;\n #onRelease: (() => void) | undefined;\n\n constructor(connection: PoolClient | Client, options: ConnectionOptions, onRelease?: () => void) {\n super(options);\n this.#connection = connection;\n this.#onRelease = onRelease;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async beginTransaction(): Promise<SqlTransaction> {\n await this.#connection.query('BEGIN').catch(rethrowNormalizedError);\n return new PostgresTransactionImpl(this.#connection, this.options);\n }\n\n async release(): Promise<void> {\n if ('release' in this.#connection) {\n this.#connection.release();\n }\n this.#onRelease?.();\n this.#onRelease = undefined;\n }\n}\n\nclass PostgresTransactionImpl extends PostgresQueryable implements SqlTransaction {\n #connection: PoolClient | Client;\n\n constructor(connection: PoolClient | Client, options: ConnectionOptions) {\n super(options);\n this.#connection = connection;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async commit(): Promise<void> {\n await this.#connection.query('COMMIT').catch(rethrowNormalizedError);\n }\n\n async rollback(): Promise<void> {\n await this.#connection.query('ROLLBACK').catch(rethrowNormalizedError);\n }\n}\n\nclass PostgresPoolDriverImpl\n extends PostgresQueryable<PoolClient>\n implements SqlDriver<PostgresBinding>\n{\n private readonly pool: PoolType;\n #closed = false;\n\n constructor(options: PostgresDriverOptions & { connect: { pool: PoolType } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.pool = options.connect.pool;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with pool already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(client, this.options);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.pool.end();\n }\n\n async acquireClient(): Promise<PoolClient> {\n return this.pool.connect();\n }\n\n async releaseClient(client: PoolClient): Promise<void> {\n client.release();\n }\n}\n\nclass PostgresDirectDriverImpl\n extends PostgresQueryable<Client>\n implements SqlDriver<PostgresBinding>\n{\n private readonly directClient: Client;\n readonly #connectionMutex = new AsyncMutex();\n #closed = false;\n #connected = false;\n #connectPromise: Promise<void> | undefined;\n\n constructor(options: PostgresDriverOptions & { connect: { client: Client } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.directClient = options.connect.client;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with client already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(client, this.options, releaseLease);\n } catch (error) {\n releaseLease();\n throw error;\n }\n }\n\n async close(): Promise<void> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.directClient.end();\n this.#connected = false;\n } finally {\n releaseLease();\n }\n }\n\n async acquireClient(): Promise<Client> {\n if (this.#connected) {\n return this.directClient;\n }\n if (this.#connectPromise !== undefined) {\n await this.#connectPromise;\n return this.directClient;\n }\n\n this.#connectPromise = (async () => {\n try {\n await this.directClient.connect();\n } catch (error: unknown) {\n if (!isAlreadyConnectedError(error)) {\n throw error;\n }\n } finally {\n this.#connectPromise = undefined;\n }\n this.#connected = true;\n })();\n await this.#connectPromise;\n\n return this.directClient;\n }\n\n async releaseClient(_client: Client): Promise<void> {}\n}\n\nexport function createBoundDriverFromBinding(\n binding: PostgresBinding,\n cursorOpts: PostgresDriverCreateOptions['cursor'],\n): SqlDriver<PostgresBinding> {\n switch (binding.kind) {\n case 'url': {\n const pool = new Pool({\n connectionString: binding.url,\n connectionTimeoutMillis: 20_000,\n idleTimeoutMillis: 30_000,\n });\n return new PostgresPoolDriverImpl({\n connect: { pool },\n cursor: cursorOpts,\n });\n }\n case 'pgPool':\n return new PostgresPoolDriverImpl({\n connect: { pool: binding.pool },\n cursor: cursorOpts,\n });\n case 'pgClient':\n return new PostgresDirectDriverImpl({\n connect: { client: binding.client },\n cursor: cursorOpts,\n });\n }\n}\n\nfunction readCursor<Row>(cursor: Cursor<Row>, size: number): Promise<Row[]> {\n return callbackToPromise<Row[]>((cb) => {\n cursor.read(size, (err, rows) => cb(err, rows));\n });\n}\n\nfunction closeCursor(cursor: Cursor<unknown>): Promise<void> {\n return callbackToPromise((cb) => cursor.close(cb));\n}\n\nfunction rethrowNormalizedError(error: unknown): never {\n throw normalizePgError(error);\n}\n","import type {\n RuntimeDriverDescriptor,\n RuntimeDriverInstance,\n} from '@prisma-next/framework-components/execution';\nimport type {\n SqlConnection,\n SqlDriver,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryResult,\n} from '@prisma-next/sql-relational-core/ast';\nimport { postgresDriverDescriptorMeta } from '../core/descriptor-meta';\nimport {\n createBoundDriverFromBinding,\n type PostgresBinding,\n type PostgresDriverCreateOptions,\n} from '../postgres-driver';\n\nexport type PostgresRuntimeDriver = RuntimeDriverInstance<'sql', 'postgres'> &\n SqlDriver<PostgresBinding>;\n\nconst USE_BEFORE_CONNECT_MESSAGE =\n 'Postgres driver not connected. Call connect(binding) before acquireConnection or execute.';\nconst ALREADY_CONNECTED_MESSAGE =\n 'Postgres driver already connected. Call close() before reconnecting with a new binding.';\n\ninterface DriverRuntimeError extends Error {\n readonly code: 'DRIVER.NOT_CONNECTED' | 'DRIVER.ALREADY_CONNECTED';\n readonly category: 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nfunction driverError(\n code: DriverRuntimeError['code'],\n message: string,\n details?: Record<string, unknown>,\n): DriverRuntimeError {\n const error = new Error(message) as DriverRuntimeError;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n return Object.assign(error, {\n code,\n category: 'RUNTIME' as const,\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction unboundExecute<Row>(): AsyncIterable<Row> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n },\n };\n },\n };\n}\n\nclass PostgresUnboundDriverImpl implements PostgresRuntimeDriver {\n readonly familyId = 'sql' as const;\n readonly targetId = 'postgres' as const;\n\n #delegate: SqlDriver<PostgresBinding> | null = null;\n #closed = false;\n #cursorOpts: PostgresDriverCreateOptions['cursor'];\n\n constructor(cursorOpts?: PostgresDriverCreateOptions['cursor']) {\n this.#cursorOpts = cursorOpts;\n }\n\n get state(): 'unbound' | 'connected' | 'closed' {\n if (this.#delegate !== null) {\n return 'connected';\n }\n if (this.#closed) {\n return 'closed';\n }\n return 'unbound';\n }\n\n #requireDelegate(): SqlDriver<PostgresBinding> {\n const delegate = this.#delegate;\n if (delegate === null) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return delegate;\n }\n\n async connect(binding: PostgresBinding): Promise<void> {\n if (this.#delegate !== null) {\n throw driverError('DRIVER.ALREADY_CONNECTED', ALREADY_CONNECTED_MESSAGE, {\n bindingKind: binding.kind,\n });\n }\n this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts);\n this.#closed = false;\n }\n\n async acquireConnection(): Promise<SqlConnection> {\n const delegate = this.#requireDelegate();\n return delegate.acquireConnection();\n }\n\n async close(): Promise<void> {\n const delegate = this.#delegate;\n if (delegate !== null) {\n this.#delegate = null;\n await delegate.close();\n }\n this.#closed = true;\n }\n\n execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const delegate = this.#delegate;\n if (delegate === null) {\n return unboundExecute<Row>();\n }\n return delegate.execute<Row>(request);\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n const delegate = this.#requireDelegate();\n const explain = delegate.explain;\n if (explain === undefined) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return explain.call(delegate, request);\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const delegate = this.#requireDelegate();\n return delegate.query<Row>(sql, params);\n }\n}\n\nconst postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n> = {\n ...postgresDriverDescriptorMeta,\n create(options?: PostgresDriverCreateOptions): PostgresRuntimeDriver {\n return new PostgresUnboundDriverImpl(options?.cursor);\n },\n};\n\nexport default postgresRuntimeDriverDescriptor;\nexport type {\n PostgresBinding,\n PostgresDriverCreateOptions,\n QueryResult,\n} from '../postgres-driver';\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.mjs","names":["#queue","releaseLock: (() => void) | undefined","#connection","#onRelease","#onDestroy","releaseArg: Error","#closed","#connectionMutex","#closeWhileHoldingLease","#connected","#connectPromise","error: unknown","#cursorOpts","#delegate","#closed","#requireDelegate","#wrapConnection","wrapped: SqlConnection","postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n>"],"sources":["../src/callback-to-promise.ts","../src/postgres-driver.ts","../src/exports/runtime.ts"],"sourcesContent":["/**\n * Wraps a Node-style callback function into a Promise.\n * Supports both (err) => void and (err, result) => void patterns.\n */\nexport function callbackToPromise(\n fn: (callback: (err: Error | null | undefined) => void) => void,\n): Promise<void>;\nexport function callbackToPromise<T>(\n fn: (callback: (err: Error | null | undefined, result: T) => void) => void,\n): Promise<T>;\nexport function callbackToPromise<T = void>(\n fn: (callback: (err: Error | null | undefined, result?: T) => void) => void,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n fn((err, result) => {\n if (err) {\n reject(err);\n return;\n }\n resolve(result as T);\n });\n });\n}\n","import type {\n SqlConnection,\n SqlDriver,\n SqlDriverState,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryable,\n SqlQueryResult,\n SqlTransaction,\n} from '@prisma-next/sql-relational-core/ast';\nimport type {\n Client,\n QueryResult as PgQueryResult,\n PoolClient,\n Pool as PoolType,\n QueryResultRow,\n} from 'pg';\nimport { Pool } from 'pg';\nimport Cursor from 'pg-cursor';\nimport { callbackToPromise } from './callback-to-promise';\nimport { isAlreadyConnectedError, isPostgresError, normalizePgError } from './normalize-error';\n\nexport type QueryResult<T extends QueryResultRow = QueryResultRow> = PgQueryResult<T>;\n\nexport type PostgresBinding =\n | { readonly kind: 'url'; readonly url: string }\n | { readonly kind: 'pgPool'; readonly pool: PoolType }\n | { readonly kind: 'pgClient'; readonly client: Client };\n\nexport interface PostgresCursorOptions {\n readonly batchSize?: number;\n readonly disabled?: boolean;\n}\n\ninterface PostgresDriverOptions {\n readonly connect: { client: Client } | { pool: PoolType };\n readonly cursor?: PostgresCursorOptions | undefined;\n}\n\nexport type PostgresDriverCreateOptions = Omit<PostgresDriverOptions, 'connect'>;\n\nconst DEFAULT_BATCH_SIZE = 100;\n\ntype ConnectionOptions =\n | { readonly cursorDisabled: true }\n | { readonly cursorBatchSize: number; readonly cursorDisabled: false };\n\nclass AsyncMutex {\n #queue = Promise.resolve();\n\n async lock(): Promise<() => void> {\n const previous = this.#queue;\n let releaseLock: (() => void) | undefined;\n this.#queue = new Promise<void>((resolve) => {\n releaseLock = resolve;\n });\n await previous;\n return () => {\n releaseLock?.();\n releaseLock = undefined;\n };\n }\n}\n\nabstract class PostgresQueryable<C extends PoolClient | Client = PoolClient | Client>\n implements SqlQueryable\n{\n abstract acquireClient(): Promise<C>;\n abstract releaseClient(client: C): Promise<void>;\n\n protected readonly options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.options = options;\n }\n\n async *execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const client = await this.acquireClient();\n try {\n if (!this.options.cursorDisabled) {\n try {\n for await (const row of this.executeWithCursor(\n client,\n request.sql,\n request.params,\n this.options.cursorBatchSize,\n )) {\n yield row as Row;\n }\n return;\n } catch (cursorError) {\n if (!(cursorError instanceof Error)) {\n throw cursorError;\n }\n // Check if this is a pg error - if so, normalize and throw\n // Otherwise, fall back to buffered mode for cursor-specific errors\n if (isPostgresError(cursorError)) {\n throw normalizePgError(cursorError);\n }\n // Not a pg error - cursor-specific error, fall back to buffered mode\n }\n }\n\n for await (const row of this.executeBuffered(client, request.sql, request.params)) {\n yield row as Row;\n }\n } catch (error) {\n throw normalizePgError(error);\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n // SQL is generated by Prisma Next planners (or validated raw paths), so\n // EXPLAIN prefixing preserves the same statement semantics.\n const text = `EXPLAIN (FORMAT JSON) ${request.sql}`;\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(text, request.params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return { rows: result.rows as ReadonlyArray<Record<string, unknown>> };\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(sql, params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return result as unknown as SqlQueryResult<Row>;\n } finally {\n await this.releaseClient(client);\n }\n }\n\n private async *executeWithCursor(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n cursorBatchSize: number,\n ): AsyncIterable<Record<string, unknown>> {\n const cursor = client.query(new Cursor(sql, params as unknown[] | undefined));\n\n try {\n while (true) {\n const rows = await readCursor(cursor, cursorBatchSize);\n if (rows.length === 0) {\n break;\n }\n\n for (const row of rows) {\n yield row;\n }\n }\n } finally {\n await closeCursor(cursor);\n }\n }\n\n private async *executeBuffered(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n ): AsyncIterable<Record<string, unknown>> {\n const result = await client.query(sql, params as unknown[] | undefined);\n for (const row of result.rows as Record<string, unknown>[]) {\n yield row;\n }\n }\n}\n\nclass PostgresConnectionImpl extends PostgresQueryable implements SqlConnection {\n #connection: PoolClient | Client;\n #onRelease: (() => void) | undefined;\n #onDestroy: ((reason: unknown) => Promise<void> | void) | undefined;\n\n constructor(\n connection: PoolClient | Client,\n options: ConnectionOptions,\n onRelease?: () => void,\n onDestroy?: (reason: unknown) => Promise<void> | void,\n ) {\n super(options);\n this.#connection = connection;\n this.#onRelease = onRelease;\n this.#onDestroy = onDestroy;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async beginTransaction(): Promise<SqlTransaction> {\n await this.#connection.query('BEGIN').catch(rethrowNormalizedError);\n return new PostgresTransactionImpl(this.#connection, this.options);\n }\n\n async release(): Promise<void> {\n const conn = this.#connection;\n if ('release' in conn) {\n conn.release();\n }\n const onRelease = this.#onRelease;\n this.#onRelease = undefined;\n this.#onDestroy = undefined;\n onRelease?.();\n }\n\n async destroy(reason?: unknown): Promise<void> {\n const onDestroy = this.#onDestroy;\n const onRelease = this.#onRelease;\n this.#onDestroy = undefined;\n this.#onRelease = undefined;\n\n const conn = this.#connection;\n if ('release' in conn) {\n // Pass a truthy Error to pg's PoolClient.release so the pool evicts the\n // client instead of returning it for reuse. A connection that reaches\n // destroy() is in an indeterminate state (failed rollback/commit, etc.)\n // and must not be handed back to another caller. The Error value is\n // surfaced on pg-pool's 'release' event as advisory context; pg-pool\n // itself only uses its truthiness for the eviction decision.\n const releaseArg: Error =\n reason instanceof Error ? reason : new Error('Connection destroyed');\n conn.release(releaseArg);\n }\n\n if (onDestroy) {\n await onDestroy(reason);\n } else {\n onRelease?.();\n }\n }\n}\n\nclass PostgresTransactionImpl extends PostgresQueryable implements SqlTransaction {\n #connection: PoolClient | Client;\n\n constructor(connection: PoolClient | Client, options: ConnectionOptions) {\n super(options);\n this.#connection = connection;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async commit(): Promise<void> {\n await this.#connection.query('COMMIT').catch(rethrowNormalizedError);\n }\n\n async rollback(): Promise<void> {\n await this.#connection.query('ROLLBACK').catch(rethrowNormalizedError);\n }\n}\n\nclass PostgresPoolDriverImpl\n extends PostgresQueryable<PoolClient>\n implements SqlDriver<PostgresBinding>\n{\n private readonly pool: PoolType;\n #closed = false;\n\n constructor(options: PostgresDriverOptions & { connect: { pool: PoolType } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.pool = options.connect.pool;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with pool already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(client, this.options);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.pool.end();\n }\n\n async acquireClient(): Promise<PoolClient> {\n return this.pool.connect();\n }\n\n async releaseClient(client: PoolClient): Promise<void> {\n client.release();\n }\n}\n\nclass PostgresDirectDriverImpl\n extends PostgresQueryable<Client>\n implements SqlDriver<PostgresBinding>\n{\n private readonly directClient: Client;\n readonly #connectionMutex = new AsyncMutex();\n #closed = false;\n #connected = false;\n #connectPromise: Promise<void> | undefined;\n\n constructor(options: PostgresDriverOptions & { connect: { client: Client } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.directClient = options.connect.client;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with client already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(\n client,\n this.options,\n releaseLease,\n // A direct driver has a single underlying socket, so a destroyed\n // connection means the driver itself is no longer usable. Tear down\n // the driver while still holding the lease so that any\n // acquireConnection() queued on #connectionMutex only observes the\n // driver after .end() has completed and #closed is set — preventing\n // a concurrent caller from reusing a socket that is already being\n // closed. The lease is released in a finally so a failing .end()\n // cannot leave the mutex permanently held.\n async () => {\n try {\n await this.#closeWhileHoldingLease();\n } finally {\n releaseLease();\n }\n },\n );\n } catch (error) {\n releaseLease();\n throw error;\n }\n }\n\n async close(): Promise<void> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n await this.#closeWhileHoldingLease();\n } finally {\n releaseLease();\n }\n }\n\n async #closeWhileHoldingLease(): Promise<void> {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.directClient.end();\n this.#connected = false;\n }\n\n async acquireClient(): Promise<Client> {\n if (this.#connected) {\n return this.directClient;\n }\n if (this.#connectPromise !== undefined) {\n await this.#connectPromise;\n return this.directClient;\n }\n\n this.#connectPromise = (async () => {\n try {\n await this.directClient.connect();\n } catch (error: unknown) {\n if (!isAlreadyConnectedError(error)) {\n throw error;\n }\n } finally {\n this.#connectPromise = undefined;\n }\n this.#connected = true;\n })();\n await this.#connectPromise;\n\n return this.directClient;\n }\n\n async releaseClient(_client: Client): Promise<void> {}\n}\n\nexport function createBoundDriverFromBinding(\n binding: PostgresBinding,\n cursorOpts: PostgresDriverCreateOptions['cursor'],\n): SqlDriver<PostgresBinding> {\n switch (binding.kind) {\n case 'url': {\n const pool = new Pool({\n connectionString: binding.url,\n connectionTimeoutMillis: 20_000,\n idleTimeoutMillis: 30_000,\n });\n return new PostgresPoolDriverImpl({\n connect: { pool },\n cursor: cursorOpts,\n });\n }\n case 'pgPool':\n return new PostgresPoolDriverImpl({\n connect: { pool: binding.pool },\n cursor: cursorOpts,\n });\n case 'pgClient':\n return new PostgresDirectDriverImpl({\n connect: { client: binding.client },\n cursor: cursorOpts,\n });\n }\n}\n\nfunction readCursor<Row>(cursor: Cursor<Row>, size: number): Promise<Row[]> {\n return callbackToPromise<Row[]>((cb) => {\n cursor.read(size, (err, rows) => cb(err, rows));\n });\n}\n\nfunction closeCursor(cursor: Cursor<unknown>): Promise<void> {\n return callbackToPromise((cb) => cursor.close(cb));\n}\n\nfunction rethrowNormalizedError(error: unknown): never {\n throw normalizePgError(error);\n}\n","import type {\n RuntimeDriverDescriptor,\n RuntimeDriverInstance,\n} from '@prisma-next/framework-components/execution';\nimport type {\n SqlConnection,\n SqlDriver,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryResult,\n} from '@prisma-next/sql-relational-core/ast';\nimport { postgresDriverDescriptorMeta } from '../core/descriptor-meta';\nimport {\n createBoundDriverFromBinding,\n type PostgresBinding,\n type PostgresDriverCreateOptions,\n} from '../postgres-driver';\n\nexport type PostgresRuntimeDriver = RuntimeDriverInstance<'sql', 'postgres'> &\n SqlDriver<PostgresBinding>;\n\nconst USE_BEFORE_CONNECT_MESSAGE =\n 'Postgres driver not connected. Call connect(binding) before acquireConnection or execute.';\nconst ALREADY_CONNECTED_MESSAGE =\n 'Postgres driver already connected. Call close() before reconnecting with a new binding.';\n\ninterface DriverRuntimeError extends Error {\n readonly code: 'DRIVER.NOT_CONNECTED' | 'DRIVER.ALREADY_CONNECTED';\n readonly category: 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nfunction driverError(\n code: DriverRuntimeError['code'],\n message: string,\n details?: Record<string, unknown>,\n): DriverRuntimeError {\n const error = new Error(message) as DriverRuntimeError;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n return Object.assign(error, {\n code,\n category: 'RUNTIME' as const,\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction unboundExecute<Row>(): AsyncIterable<Row> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n },\n };\n },\n };\n}\n\nclass PostgresUnboundDriverImpl implements PostgresRuntimeDriver {\n readonly familyId = 'sql' as const;\n readonly targetId = 'postgres' as const;\n\n #delegate: SqlDriver<PostgresBinding> | null = null;\n #closed = false;\n #cursorOpts: PostgresDriverCreateOptions['cursor'];\n\n constructor(cursorOpts?: PostgresDriverCreateOptions['cursor']) {\n this.#cursorOpts = cursorOpts;\n }\n\n get state(): 'unbound' | 'connected' | 'closed' {\n if (this.#delegate !== null) {\n return 'connected';\n }\n if (this.#closed) {\n return 'closed';\n }\n return 'unbound';\n }\n\n #requireDelegate(): SqlDriver<PostgresBinding> {\n const delegate = this.#delegate;\n if (delegate === null) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return delegate;\n }\n\n async connect(binding: PostgresBinding): Promise<void> {\n if (this.#delegate !== null) {\n throw driverError('DRIVER.ALREADY_CONNECTED', ALREADY_CONNECTED_MESSAGE, {\n bindingKind: binding.kind,\n });\n }\n this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts);\n this.#closed = false;\n }\n\n async acquireConnection(): Promise<SqlConnection> {\n const delegate = this.#requireDelegate();\n const connection = await delegate.acquireConnection();\n return this.#wrapConnection(connection, delegate);\n }\n\n /**\n * Wraps an acquired connection so that teardown paths which close the\n * underlying delegate (notably `destroy()` on a pgClient binding, where\n * the single socket means a destroyed connection invalidates the driver)\n * also reset our own `#delegate` reference. Without this, a failed\n * transaction rollback would leave the outer unbound wrapper reporting\n * `connected` while routing subsequent work to an already-ended delegate.\n */\n #wrapConnection(connection: SqlConnection, delegate: SqlDriver<PostgresBinding>): SqlConnection {\n const syncDelegateState = (): void => {\n if (this.#delegate === delegate && delegate.state === 'closed') {\n this.#delegate = null;\n this.#closed = true;\n }\n };\n const wrapped: SqlConnection = {\n beginTransaction: connection.beginTransaction.bind(connection),\n execute: connection.execute.bind(connection),\n query: connection.query.bind(connection),\n release: async () => {\n try {\n await connection.release();\n } finally {\n syncDelegateState();\n }\n },\n destroy: async (reason?: unknown) => {\n try {\n await connection.destroy(reason);\n } finally {\n syncDelegateState();\n }\n },\n };\n if (connection.explain) {\n wrapped.explain = connection.explain.bind(connection);\n }\n return wrapped;\n }\n\n async close(): Promise<void> {\n const delegate = this.#delegate;\n if (delegate !== null) {\n this.#delegate = null;\n await delegate.close();\n }\n this.#closed = true;\n }\n\n execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const delegate = this.#delegate;\n if (delegate === null) {\n return unboundExecute<Row>();\n }\n return delegate.execute<Row>(request);\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n const delegate = this.#requireDelegate();\n const explain = delegate.explain;\n if (explain === undefined) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return explain.call(delegate, request);\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const delegate = this.#requireDelegate();\n return delegate.query<Row>(sql, params);\n }\n}\n\nconst postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n> = {\n ...postgresDriverDescriptorMeta,\n create(options?: PostgresDriverCreateOptions): PostgresRuntimeDriver {\n return new PostgresUnboundDriverImpl(options?.cursor);\n },\n};\n\nexport default postgresRuntimeDriverDescriptor;\nexport type {\n PostgresBinding,\n PostgresDriverCreateOptions,\n QueryResult,\n} from '../postgres-driver';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@prisma-next/driver-postgres",
3
- "version": "0.4.0-dev.9",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
7
7
  "arktype": "^2.0.0",
8
8
  "pg": "8.16.3",
9
9
  "pg-cursor": "^2.10.5",
10
- "@prisma-next/contract": "0.4.0-dev.9",
11
- "@prisma-next/errors": "0.4.0-dev.9",
12
- "@prisma-next/sql-contract": "0.4.0-dev.9",
13
- "@prisma-next/sql-errors": "0.4.0-dev.9",
14
- "@prisma-next/framework-components": "0.4.0-dev.9",
15
- "@prisma-next/sql-operations": "0.4.0-dev.9",
16
- "@prisma-next/sql-relational-core": "0.4.0-dev.9",
17
- "@prisma-next/utils": "0.4.0-dev.9"
10
+ "@prisma-next/errors": "0.4.2",
11
+ "@prisma-next/sql-contract": "0.4.2",
12
+ "@prisma-next/framework-components": "0.4.2",
13
+ "@prisma-next/contract": "0.4.2",
14
+ "@prisma-next/sql-errors": "0.4.2",
15
+ "@prisma-next/sql-operations": "0.4.2",
16
+ "@prisma-next/sql-relational-core": "0.4.2",
17
+ "@prisma-next/utils": "0.4.2"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/pg": "8.16.0",
@@ -104,7 +104,48 @@ class PostgresUnboundDriverImpl implements PostgresRuntimeDriver {
104
104
 
105
105
  async acquireConnection(): Promise<SqlConnection> {
106
106
  const delegate = this.#requireDelegate();
107
- return delegate.acquireConnection();
107
+ const connection = await delegate.acquireConnection();
108
+ return this.#wrapConnection(connection, delegate);
109
+ }
110
+
111
+ /**
112
+ * Wraps an acquired connection so that teardown paths which close the
113
+ * underlying delegate (notably `destroy()` on a pgClient binding, where
114
+ * the single socket means a destroyed connection invalidates the driver)
115
+ * also reset our own `#delegate` reference. Without this, a failed
116
+ * transaction rollback would leave the outer unbound wrapper reporting
117
+ * `connected` while routing subsequent work to an already-ended delegate.
118
+ */
119
+ #wrapConnection(connection: SqlConnection, delegate: SqlDriver<PostgresBinding>): SqlConnection {
120
+ const syncDelegateState = (): void => {
121
+ if (this.#delegate === delegate && delegate.state === 'closed') {
122
+ this.#delegate = null;
123
+ this.#closed = true;
124
+ }
125
+ };
126
+ const wrapped: SqlConnection = {
127
+ beginTransaction: connection.beginTransaction.bind(connection),
128
+ execute: connection.execute.bind(connection),
129
+ query: connection.query.bind(connection),
130
+ release: async () => {
131
+ try {
132
+ await connection.release();
133
+ } finally {
134
+ syncDelegateState();
135
+ }
136
+ },
137
+ destroy: async (reason?: unknown) => {
138
+ try {
139
+ await connection.destroy(reason);
140
+ } finally {
141
+ syncDelegateState();
142
+ }
143
+ },
144
+ };
145
+ if (connection.explain) {
146
+ wrapped.explain = connection.explain.bind(connection);
147
+ }
148
+ return wrapped;
108
149
  }
109
150
 
110
151
  async close(): Promise<void> {
@@ -180,11 +180,18 @@ abstract class PostgresQueryable<C extends PoolClient | Client = PoolClient | Cl
180
180
  class PostgresConnectionImpl extends PostgresQueryable implements SqlConnection {
181
181
  #connection: PoolClient | Client;
182
182
  #onRelease: (() => void) | undefined;
183
-
184
- constructor(connection: PoolClient | Client, options: ConnectionOptions, onRelease?: () => void) {
183
+ #onDestroy: ((reason: unknown) => Promise<void> | void) | undefined;
184
+
185
+ constructor(
186
+ connection: PoolClient | Client,
187
+ options: ConnectionOptions,
188
+ onRelease?: () => void,
189
+ onDestroy?: (reason: unknown) => Promise<void> | void,
190
+ ) {
185
191
  super(options);
186
192
  this.#connection = connection;
187
193
  this.#onRelease = onRelease;
194
+ this.#onDestroy = onDestroy;
188
195
  }
189
196
 
190
197
  override acquireClient(): Promise<PoolClient | Client> {
@@ -201,11 +208,40 @@ class PostgresConnectionImpl extends PostgresQueryable implements SqlConnection
201
208
  }
202
209
 
203
210
  async release(): Promise<void> {
204
- if ('release' in this.#connection) {
205
- this.#connection.release();
211
+ const conn = this.#connection;
212
+ if ('release' in conn) {
213
+ conn.release();
206
214
  }
207
- this.#onRelease?.();
215
+ const onRelease = this.#onRelease;
216
+ this.#onRelease = undefined;
217
+ this.#onDestroy = undefined;
218
+ onRelease?.();
219
+ }
220
+
221
+ async destroy(reason?: unknown): Promise<void> {
222
+ const onDestroy = this.#onDestroy;
223
+ const onRelease = this.#onRelease;
224
+ this.#onDestroy = undefined;
208
225
  this.#onRelease = undefined;
226
+
227
+ const conn = this.#connection;
228
+ if ('release' in conn) {
229
+ // Pass a truthy Error to pg's PoolClient.release so the pool evicts the
230
+ // client instead of returning it for reuse. A connection that reaches
231
+ // destroy() is in an indeterminate state (failed rollback/commit, etc.)
232
+ // and must not be handed back to another caller. The Error value is
233
+ // surfaced on pg-pool's 'release' event as advisory context; pg-pool
234
+ // itself only uses its truthiness for the eviction decision.
235
+ const releaseArg: Error =
236
+ reason instanceof Error ? reason : new Error('Connection destroyed');
237
+ conn.release(releaseArg);
238
+ }
239
+
240
+ if (onDestroy) {
241
+ await onDestroy(reason);
242
+ } else {
243
+ onRelease?.();
244
+ }
209
245
  }
210
246
  }
211
247
 
@@ -309,7 +345,26 @@ class PostgresDirectDriverImpl
309
345
  const releaseLease = await this.#connectionMutex.lock();
310
346
  try {
311
347
  const client = await this.acquireClient();
312
- return new PostgresConnectionImpl(client, this.options, releaseLease);
348
+ return new PostgresConnectionImpl(
349
+ client,
350
+ this.options,
351
+ releaseLease,
352
+ // A direct driver has a single underlying socket, so a destroyed
353
+ // connection means the driver itself is no longer usable. Tear down
354
+ // the driver while still holding the lease so that any
355
+ // acquireConnection() queued on #connectionMutex only observes the
356
+ // driver after .end() has completed and #closed is set — preventing
357
+ // a concurrent caller from reusing a socket that is already being
358
+ // closed. The lease is released in a finally so a failing .end()
359
+ // cannot leave the mutex permanently held.
360
+ async () => {
361
+ try {
362
+ await this.#closeWhileHoldingLease();
363
+ } finally {
364
+ releaseLease();
365
+ }
366
+ },
367
+ );
313
368
  } catch (error) {
314
369
  releaseLease();
315
370
  throw error;
@@ -319,17 +374,21 @@ class PostgresDirectDriverImpl
319
374
  async close(): Promise<void> {
320
375
  const releaseLease = await this.#connectionMutex.lock();
321
376
  try {
322
- if (this.#closed) {
323
- return;
324
- }
325
- this.#closed = true;
326
- await this.directClient.end();
327
- this.#connected = false;
377
+ await this.#closeWhileHoldingLease();
328
378
  } finally {
329
379
  releaseLease();
330
380
  }
331
381
  }
332
382
 
383
+ async #closeWhileHoldingLease(): Promise<void> {
384
+ if (this.#closed) {
385
+ return;
386
+ }
387
+ this.#closed = true;
388
+ await this.directClient.end();
389
+ this.#connected = false;
390
+ }
391
+
333
392
  async acquireClient(): Promise<Client> {
334
393
  if (this.#connected) {
335
394
  return this.directClient;