@vibeorm/adapter-bun 1.1.8 → 1.3.0

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.ts +298 -77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeorm/adapter-bun",
3
- "version": "1.1.8",
3
+ "version": "1.3.0",
4
4
  "description": "Bun-native database adapter for VibeORM using bun:sql",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -33,7 +33,7 @@
33
33
  "bun": ">=1.1.0"
34
34
  },
35
35
  "dependencies": {
36
- "@vibeorm/runtime": "1.1.8"
36
+ "@vibeorm/runtime": "1.3.0"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { DatabaseAdapter, QueryResult, TransactionOptions } from "@vibeorm/runtime";
10
+ import { PgArray } from "@vibeorm/runtime";
10
11
 
11
12
  export type BunAdapterOptions = {
12
13
  /** PostgreSQL connection URL. Falls back to DATABASE_URL env var if not provided. */
@@ -30,13 +31,21 @@ export type BunAdapterOptions = {
30
31
  */
31
32
  statementTimeout?: number;
32
33
  /**
33
- * Maximum time in milliseconds to wait for a connection from the pool (default: none).
34
+ * Maximum time in milliseconds to wait when establishing a new connection (default: none).
34
35
  *
35
- * When set, connection acquisition that exceeds this duration will throw.
36
- * Prevents indefinite blocking when the pool is exhausted under load.
36
+ * Maps directly to bun:sql's `connectionTimeout` constructor option (which expects
37
+ * seconds — we convert ms → seconds for you). If a connection cannot be established
38
+ * within this duration, bun:sql throws `ERR_POSTGRES_CONNECTION_TIMEOUT`.
37
39
  *
38
- * Injected as `connect_timeout` in the PostgreSQL connection URL. Note that
39
- * bun:sql uses this for initial TCP connection establishment, not pool checkout.
40
+ * Note: this is for the initial TCP/handshake establishment, NOT for waiting on
41
+ * pool checkout once the pool is saturated.
42
+ *
43
+ * Historically this was injected as a `connect_timeout=…` URL parameter, but
44
+ * bun:sql forwards unrecognised URL parameters to PostgreSQL as runtime
45
+ * configuration settings, and `connect_timeout` is a libpq *client-side* option
46
+ * (not a server GUC), so PostgreSQL would error with
47
+ * `unrecognized configuration parameter "connect_timeout"`. The constructor
48
+ * option is the canonical way to set it.
40
49
  */
41
50
  connectionTimeout?: number;
42
51
  /**
@@ -97,20 +106,60 @@ export type BunAdapterOptions = {
97
106
 
98
107
  // ─── Internal bun:sql types ───────────────────────────────────────
99
108
 
109
+ /**
110
+ * A bun:sql "reserved" connection (single physical connection checked out
111
+ * from the pool). Used by the manual `BEGIN … COMMIT` transaction path.
112
+ *
113
+ * Note: a reserved connection has NO `.begin()` or `.reserve()` — any nested
114
+ * transaction must be implemented via SAVEPOINTs on the same connection.
115
+ */
100
116
  type SqlReserved = {
101
117
  (strings: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
102
118
  unsafe(query: string, values?: unknown[]): Promise<unknown[]>;
103
119
  release(): void;
104
120
  };
105
121
 
122
+ /**
123
+ * The transactional handle passed to the `sql.begin(callback)` callback.
124
+ * Modern bun:sql exposes `.savepoint(fn)` which creates a savepoint-scoped
125
+ * sub-transaction (the correct API for nested transactions). Calling
126
+ * `.begin()` here throws "cannot call begin inside a transaction use
127
+ * savepoint() instead" — so we always prefer `savepoint` when present.
128
+ *
129
+ * Older / minimal bun:sql versions may lack `.savepoint`, in which case we
130
+ * fall back to explicit `SAVEPOINT <name>` SQL via `.unsafe()`.
131
+ */
132
+ type SqlTransaction = {
133
+ (strings: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
134
+ unsafe(query: string, values?: unknown[]): Promise<unknown[]>;
135
+ savepoint?: <T>(fn: (tx: SqlTransaction) => Promise<T>) => Promise<T>;
136
+ };
137
+
106
138
  type SqlInstance = {
107
139
  (strings: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
108
140
  unsafe(query: string, values?: unknown[]): Promise<unknown[]>;
109
- begin<T>(fn: (tx: SqlInstance) => Promise<T>): Promise<T>;
141
+ begin<T>(fn: (tx: SqlTransaction) => Promise<T>): Promise<T>;
110
142
  reserve(): Promise<SqlReserved>;
111
143
  close(): Promise<void>;
112
144
  };
113
145
 
146
+ /**
147
+ * Anything that can execute SQL through bun:sql — pool, reserved connection,
148
+ * or in-transaction handle. Used by the unified adapter created at every
149
+ * nesting level.
150
+ */
151
+ type SqlSource = SqlInstance | SqlReserved | SqlTransaction;
152
+
153
+ /**
154
+ * Whether the SQL source is the outermost pool (can issue real BEGIN/COMMIT
155
+ * or use `sql.begin()`) or is already inside a transaction (must use
156
+ * SAVEPOINTs for nesting).
157
+ */
158
+ type ParentMode = "pool" | "tx";
159
+
160
+ /** Shared monotonic counter for savepoint naming across one top-level tx. */
161
+ type SavepointCounter = { n: number };
162
+
114
163
  /**
115
164
  * Create a VibeORM database adapter using Bun's built-in SQL driver.
116
165
  *
@@ -152,16 +201,19 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
152
201
  * Resolve the connection URL, injecting startup parameters as needed.
153
202
  * Falls back to DATABASE_URL env var when no explicit URL is provided.
154
203
  *
155
- * Injects via the PostgreSQL `options` startup parameter:
204
+ * Injects via the PostgreSQL `options` startup parameter (server-side runtime
205
+ * configuration; valid because `options` is a real Postgres startup-protocol
206
+ * parameter that the server parses for `-c key=value` settings):
156
207
  * - plan_cache_mode (unless "auto")
157
208
  * - statement_timeout (if configured)
158
209
  *
159
- * Injects as a URL parameter:
160
- * - connect_timeout (if configured, converted to seconds for PG)
210
+ * `connect_timeout` is NOT injected here — it's a libpq client-side option,
211
+ * not a server GUC, and bun:sql would forward it to the server as an
212
+ * unrecognised configuration parameter. We pass it via the bun:sql
213
+ * `connectionTimeout` constructor option instead (see getSql()).
161
214
  */
162
215
  function resolveConnectionUrl(): string | undefined {
163
216
  const STATEMENT_TIMEOUT = options?.statementTimeout;
164
- const CONNECTION_TIMEOUT = options?.connectionTimeout;
165
217
 
166
218
  // Build startup options string (for -c parameters)
167
219
  const startupParts: string[] = [];
@@ -172,29 +224,16 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
172
224
  startupParts.push(`-c statement_timeout=${Number(STATEMENT_TIMEOUT)}`);
173
225
  }
174
226
 
175
- const needsUrlMutation = startupParts.length > 0 || CONNECTION_TIMEOUT !== undefined;
227
+ const needsUrlMutation = startupParts.length > 0;
176
228
  if (!needsUrlMutation) return options?.url;
177
229
 
178
230
  const baseUrl = options?.url ?? process.env.DATABASE_URL;
179
231
  if (!baseUrl) return undefined;
180
232
 
181
- let url = baseUrl;
182
-
183
- // Inject startup options (-c params)
184
- if (startupParts.length > 0) {
185
- url = appendStartupOption({
186
- url,
187
- option: startupParts.join(" "),
188
- });
189
- }
190
-
191
- // Inject connect_timeout as a URL parameter (PG uses seconds)
192
- if (CONNECTION_TIMEOUT !== undefined) {
193
- const separator = url.includes("?") ? "&" : "?";
194
- url = `${url}${separator}connect_timeout=${Math.ceil(CONNECTION_TIMEOUT / 1000)}`;
195
- }
196
-
197
- return url;
233
+ return appendStartupOption({
234
+ url: baseUrl,
235
+ option: startupParts.join(" "),
236
+ });
198
237
  }
199
238
 
200
239
  function getSql(): SqlInstance {
@@ -215,6 +254,13 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
215
254
  // See: https://github.com/oven-sh/bun/issues/20294
216
255
  };
217
256
 
257
+ // Connection establishment timeout — bun:sql expects seconds.
258
+ // We accept milliseconds in our public API to stay consistent with pgAdapter
259
+ // and Node convention, then convert here.
260
+ if (options?.connectionTimeout !== undefined) {
261
+ sqlOptions.connectionTimeout = Math.max(1, Math.ceil(options.connectionTimeout / 1000));
262
+ }
263
+
218
264
  const connectionUrl = resolveConnectionUrl();
219
265
  if (connectionUrl) {
220
266
  sqlInstance = new SQL(connectionUrl, sqlOptions) as unknown as SqlInstance;
@@ -260,29 +306,107 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
260
306
  * Convert a JS array to a PostgreSQL array literal string `{val1,val2,...}`.
261
307
  * bun:sql's extended query protocol sends values as strings, so PostgreSQL
262
308
  * needs array parameters in its native array literal format.
309
+ *
310
+ * Element handling:
311
+ * - `null` / `undefined` → `NULL`
312
+ * - `number` / `bigint` / `boolean` → unquoted primitive
313
+ * - `Date` → ISO-8601 string (quoted+escaped) so PG can parse it as
314
+ * `timestamp[]` / `timestamptz[]`. Using the default `String(d)` would
315
+ * yield a non-ISO format like `Mon May 24 2026 …` that PG cannot parse.
316
+ * - `Buffer` / `Uint8Array` → not supported inside array literals; throw
317
+ * a clear error instead of silently producing `[object Object]`.
318
+ * - `string` → quoted with `"`/`\` escaping
319
+ * - other `object` → `JSON.stringify` then quoted+escaped (covers users
320
+ * putting plain objects into a `Json[]` / `Jsonb[]` scalar list).
263
321
  */
264
322
  function toPgArrayLiteral(arr: unknown[]): string {
323
+ const escape = (str: string): string =>
324
+ `"${str.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
325
+
265
326
  const escaped = arr.map((v) => {
266
327
  if (v === null || v === undefined) return "NULL";
267
- if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);
268
- const str = String(v);
269
- return `"${str.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
328
+ if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") {
329
+ return String(v);
330
+ }
331
+ if (v instanceof Date) {
332
+ return escape(v.toISOString());
333
+ }
334
+ if (Buffer.isBuffer(v) || v instanceof Uint8Array) {
335
+ throw new Error(
336
+ "toPgArrayLiteral: Buffer/Uint8Array array elements are not supported"
337
+ );
338
+ }
339
+ if (typeof v === "string") {
340
+ return escape(v);
341
+ }
342
+ if (typeof v === "object") {
343
+ return escape(JSON.stringify(v));
344
+ }
345
+ return escape(String(v));
270
346
  });
271
347
  return `{${escaped.join(",")}}`;
272
348
  }
273
349
 
350
+ /**
351
+ * Normalise raw-path parameter values for bun:sql.
352
+ *
353
+ * bun:sql's extended query protocol sends params as strings, so a plain JS
354
+ * array binding cannot be auto-converted to a PG array literal by the driver.
355
+ * The ORM hot path wraps array params in `PgArray` and converts them via
356
+ * `formatArrayParam` before they reach the adapter — but raw queries
357
+ * (`$queryRawUnsafe`, `$queryRaw` template-literal, `$executeRaw*`) hand the
358
+ * user's array straight through.
359
+ *
360
+ * To make `db.$queryRawUnsafe('… WHERE "id" = ANY($1)', [1,2,3])` actually
361
+ * match rows, we convert any plain JS array element in the param list to a
362
+ * PG array literal here. Non-array values pass through unchanged so that
363
+ * bun:sql's native handling (numbers, strings, Dates, Buffers, plain objects
364
+ * for json columns when bun supports it) remains in effect.
365
+ */
366
+ function normalizeRawValues(values: unknown[] | undefined): unknown[] | undefined {
367
+ if (!values || values.length === 0) return values;
368
+ let mutated: unknown[] | null = null;
369
+ for (let i = 0; i < values.length; i++) {
370
+ const v = values[i];
371
+ if (Array.isArray(v)) {
372
+ if (!mutated) mutated = values.slice();
373
+ mutated[i] = toPgArrayLiteral(v);
374
+ }
375
+ }
376
+ return mutated ?? values;
377
+ }
378
+
274
379
  /**
275
380
  * Execute a query using the configured strategy.
276
381
  * - preparedStatements=true: uses synthetic tagged templates (named prepared stmts)
277
382
  * - preparedStatements=false: uses sql.unsafe() (replanned each time)
383
+ *
384
+ * Note: this is the ORM hot path. Scalar-list / `= ANY()` array values are
385
+ * already wrapped in `PgArray` by the query builder and converted to a PG
386
+ * array literal string via `formatArrayParam` before they reach here, so we
387
+ * intentionally do NOT touch raw JS arrays here (that would corrupt JSON
388
+ * column writes that genuinely pass a JS array as a JSON value).
278
389
  */
279
390
  async function executeQuery(sql: SqlInstance, params: { text: string; values: unknown[] }): Promise<Record<string, unknown>[]> {
391
+ // Defence-in-depth: if any value is a runtime PgArray that slipped through
392
+ // (e.g. a caller bypassed `client.ts`'s formatArrayParam unwrap), convert
393
+ // it to the PG array literal string that bun:sql actually needs. The
394
+ // standard ORM path has already done this conversion via `formatArrayParam`,
395
+ // so the loop is a near-zero-cost guard for the normal case.
396
+ // Bug 3 — see .ai/bug-report-2026-05-24.md.
397
+ let values: unknown[] = params.values;
398
+ for (let i = 0; i < params.values.length; i++) {
399
+ if (params.values[i] instanceof PgArray) {
400
+ if (values === params.values) values = params.values.slice();
401
+ values[i] = toPgArrayLiteral((params.values[i] as PgArray).values);
402
+ }
403
+ }
280
404
  if (USE_PREPARED) {
281
405
  const strings = getOrCreateTemplate({ text: params.text });
282
- const result = await sql(strings, ...params.values);
406
+ const result = await sql(strings, ...values);
283
407
  return result as Record<string, unknown>[];
284
408
  }
285
- const result = await sql.unsafe(params.text, params.values);
409
+ const result = await sql.unsafe(params.text, values);
286
410
  return result as Record<string, unknown>[];
287
411
  }
288
412
 
@@ -294,14 +418,118 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
294
418
  }
295
419
  }
296
420
 
297
- function createAdapter(sql: SqlInstance): DatabaseAdapter {
421
+ /**
422
+ * Build the typed adapter facade over any bun:sql source — the pool, a
423
+ * reserved connection, or an in-transaction handle. The `parentMode`
424
+ * discriminator tells `.transaction()` whether a nested call should issue
425
+ * a real BEGIN/COMMIT (or `sql.begin()`) at the pool level, or open a
426
+ * SAVEPOINT on the current connection.
427
+ *
428
+ * `savepointCounter` is shared by reference across every adapter created
429
+ * for one top-level transaction, so sibling and deeply-nested transactions
430
+ * always get distinct savepoint names like `vibeorm_sp_0`, `vibeorm_sp_1`,
431
+ * `vibeorm_sp_2`, …
432
+ */
433
+ function createAdapter(params: {
434
+ sql: SqlSource;
435
+ parentMode: ParentMode;
436
+ savepointCounter: SavepointCounter;
437
+ }): DatabaseAdapter {
438
+ const { sql, parentMode, savepointCounter } = params;
439
+
440
+ async function txViaSavepoint<T>(innerFn: (txAdapter: DatabaseAdapter) => Promise<T>): Promise<T> {
441
+ // We're inside a transaction already — open a SAVEPOINT on this
442
+ // connection. Prefer the driver's native `tx.savepoint()` if exposed
443
+ // (modern bun:sql); else fall back to explicit `SAVEPOINT` SQL on
444
+ // either the in-tx handle (which lacks `.savepoint`) or a reserved
445
+ // connection holding a manual `BEGIN`.
446
+ const txSql = sql as SqlTransaction;
447
+ if (typeof txSql.savepoint === "function") {
448
+ return txSql.savepoint(async (sp) => {
449
+ const nestedAdapter = createAdapter({
450
+ sql: sp,
451
+ parentMode: "tx",
452
+ savepointCounter,
453
+ });
454
+ return innerFn(nestedAdapter);
455
+ });
456
+ }
457
+
458
+ // Fallback: explicit SAVEPOINT via unsafe(). The counter is shared by
459
+ // reference, so sibling/nested SAVEPOINTs never collide.
460
+ const spName = `vibeorm_sp_${savepointCounter.n++}`;
461
+ await txSql.unsafe(`SAVEPOINT ${spName}`);
462
+ try {
463
+ const nestedAdapter = createAdapter({
464
+ sql: txSql,
465
+ parentMode: "tx",
466
+ savepointCounter,
467
+ });
468
+ const result = await innerFn(nestedAdapter);
469
+ await txSql.unsafe(`RELEASE SAVEPOINT ${spName}`);
470
+ return result;
471
+ } catch (err) {
472
+ // Best-effort rollback — mirror pg adapter behaviour so we never
473
+ // mask the original error if the ROLLBACK itself fails.
474
+ try { await txSql.unsafe(`ROLLBACK TO SAVEPOINT ${spName}`); } catch { /* rollback best-effort */ }
475
+ throw err;
476
+ }
477
+ }
478
+
479
+ async function txViaPool<T>(
480
+ innerFn: (txAdapter: DatabaseAdapter) => Promise<T>,
481
+ options?: TransactionOptions
482
+ ): Promise<T> {
483
+ const pool = sql as SqlInstance;
484
+
485
+ // Fast path: no custom options — use native sql.begin() for best perf.
486
+ if (!options?.isolationLevel && !options?.timeout) {
487
+ return pool.begin(async (txSql: SqlTransaction) => {
488
+ const txAdapter = createAdapter({
489
+ sql: txSql,
490
+ parentMode: "tx",
491
+ savepointCounter,
492
+ });
493
+ return innerFn(txAdapter);
494
+ });
495
+ }
496
+
497
+ // Manual path: reserve a single connection for custom BEGIN options.
498
+ const reserved = await pool.reserve();
499
+ try {
500
+ const isolation = options.isolationLevel
501
+ ? ` ISOLATION LEVEL ${isolationLevelToSql({ level: options.isolationLevel })}`
502
+ : "";
503
+ await reserved.unsafe(`BEGIN${isolation}`);
504
+
505
+ if (options.timeout) {
506
+ await reserved.unsafe(`SET LOCAL statement_timeout = ${Number(options.timeout)}`);
507
+ }
508
+
509
+ const txAdapter = createAdapter({
510
+ sql: reserved,
511
+ parentMode: "tx",
512
+ savepointCounter,
513
+ });
514
+ const result = await innerFn(txAdapter);
515
+ await reserved.unsafe("COMMIT");
516
+ return result;
517
+ } catch (err) {
518
+ try { await reserved.unsafe("ROLLBACK"); } catch { /* rollback best-effort */ }
519
+ throw err;
520
+ } finally {
521
+ reserved.release();
522
+ }
523
+ }
524
+
298
525
  return {
299
- async execute(params) {
300
- return executeQuery(sql, params);
526
+ async execute(execParams) {
527
+ return executeQuery(sql as SqlInstance, execParams);
301
528
  },
302
529
 
303
- async executeUnsafe(params) {
304
- const result = await sql.unsafe(params.text, params.values);
530
+ async executeUnsafe(execParams) {
531
+ const values = normalizeRawValues(execParams.values);
532
+ const result = await sql.unsafe(execParams.text, values);
305
533
  const resultAny = result as unknown as Record<string, unknown>;
306
534
  let affectedRows: number;
307
535
  if (typeof resultAny.count === "number") {
@@ -318,46 +546,27 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
318
546
  },
319
547
 
320
548
  async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T> {
321
- // Fast path: no custom options — use native sql.begin() for best performance
322
- if (!options?.isolationLevel && !options?.timeout) {
323
- return sql.begin(async (txSql: SqlInstance) => {
324
- const txAdapter = createAdapter(txSql);
325
- return fn(txAdapter);
326
- });
327
- }
328
-
329
- // Manual path: reserve a connection for custom BEGIN options
330
- const reserved = await sql.reserve();
331
- try {
332
- const isolation = options?.isolationLevel
333
- ? ` ISOLATION LEVEL ${isolationLevelToSql({ level: options.isolationLevel })}`
334
- : "";
335
- await reserved.unsafe(`BEGIN${isolation}`);
336
-
337
- if (options?.timeout) {
338
- await reserved.unsafe(`SET LOCAL statement_timeout = ${Number(options.timeout)}`);
339
- }
340
-
341
- const txAdapter = createAdapter(reserved as unknown as SqlInstance);
342
- const result = await fn(txAdapter);
343
- await reserved.unsafe("COMMIT");
344
- return result;
345
- } catch (err) {
346
- try { await reserved.unsafe("ROLLBACK"); } catch { /* rollback best-effort */ }
347
- throw err;
348
- } finally {
349
- reserved.release();
350
- }
549
+ return parentMode === "pool"
550
+ ? txViaPool(fn, options)
551
+ : txViaSavepoint(fn);
351
552
  },
352
553
 
353
554
  async connect() {
354
- const reserved = await sql.reserve();
355
- reserved.release();
555
+ // Only the pool-level adapter can reserve a fresh connection.
556
+ // Adapters scoped to a reserved/tx connection are already connected.
557
+ if (parentMode === "pool") {
558
+ const reserved = await (sql as SqlInstance).reserve();
559
+ reserved.release();
560
+ }
356
561
  },
357
562
 
358
563
  async disconnect() {
359
- await sql.close();
360
- sqlInstance = null;
564
+ if (parentMode === "pool") {
565
+ await (sql as SqlInstance).close();
566
+ sqlInstance = null;
567
+ }
568
+ // Inside a transaction `.disconnect()` is a no-op — the pool owns
569
+ // the connection lifecycle.
361
570
  },
362
571
 
363
572
  formatArrayParam(values: unknown[]): unknown {
@@ -366,22 +575,34 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
366
575
  };
367
576
  }
368
577
 
369
- // Create a lazy adapter that initializes the SQL connection on first use
578
+ // Create a lazy adapter that initializes the SQL connection on first use.
579
+ // Each top-level call lazily resolves the underlying `SqlInstance`. The
580
+ // `savepointCounter` for every top-level transaction is freshly created
581
+ // inside that transaction's own `txViaPool` / `txViaSavepoint` — at the
582
+ // pool level there's no shared counter to manage.
583
+ function poolAdapter(): DatabaseAdapter {
584
+ return createAdapter({
585
+ sql: getSql(),
586
+ parentMode: "pool",
587
+ savepointCounter: { n: 0 },
588
+ });
589
+ }
590
+
370
591
  const adapter: DatabaseAdapter = {
371
592
  async execute(params) {
372
- return createAdapter(getSql()).execute(params);
593
+ return poolAdapter().execute(params);
373
594
  },
374
595
 
375
596
  async executeUnsafe(params) {
376
- return createAdapter(getSql()).executeUnsafe(params);
597
+ return poolAdapter().executeUnsafe(params);
377
598
  },
378
599
 
379
600
  async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T> {
380
- return createAdapter(getSql()).transaction(fn, options);
601
+ return poolAdapter().transaction(fn, options);
381
602
  },
382
603
 
383
604
  async connect() {
384
- return createAdapter(getSql()).connect();
605
+ return poolAdapter().connect();
385
606
  },
386
607
 
387
608
  async disconnect() {