@vibeorm/adapter-pg 4.3.0 → 5.0.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.
package/dist/index.js CHANGED
@@ -27,10 +27,11 @@ import {
27
27
  scopeTurn,
28
28
  settleFailedTransaction,
29
29
  startTransactionBudget,
30
- transactionAbortedError,
31
- validateTransactionDeadline
30
+ transactionAbortedError as transactionAbortedError2,
31
+ validateTransactionDeadline,
32
+ parseExtendedDateTimeText
32
33
  } from "@vibeorm/runtime/adapter-kit";
33
- import { VibeError as VibeError5 } from "@vibeorm/schema";
34
+ import { VibeError as VibeError7 } from "@vibeorm/schema";
34
35
  import pg3 from "pg";
35
36
 
36
37
  // packages/adapter-pg/src/batch.ts
@@ -39,6 +40,9 @@ var prepareValue = (() => {
39
40
  const utils = pg.utils;
40
41
  return typeof utils?.prepareValue === "function" ? utils.prepareValue : undefined;
41
42
  })();
43
+ function batchDriverAvailable() {
44
+ return prepareValue !== undefined;
45
+ }
42
46
  function supportsBatch(params) {
43
47
  if (prepareValue === undefined)
44
48
  return false;
@@ -97,13 +101,20 @@ class PgBatch {
97
101
  }
98
102
  }
99
103
  handleRowDescription(message) {
104
+ this.rows = [];
105
+ if (this.steps[this.outcomes.length]?.discardRows === true) {
106
+ this.fields = [];
107
+ this.parsers = [];
108
+ return;
109
+ }
100
110
  this.fields = message.fields;
101
111
  this.parsers = message.fields.map((field) => this.types.getTypeParser(field.dataTypeID, field.format ?? "text"));
102
- this.rows = [];
103
112
  }
104
113
  handleDataRow(message) {
105
114
  if (this.settled || this.rowError !== undefined)
106
115
  return;
116
+ if (this.steps[this.outcomes.length]?.discardRows === true)
117
+ return;
107
118
  const row = {};
108
119
  try {
109
120
  for (let index = 0;index < this.fields.length; index++) {
@@ -186,12 +197,162 @@ function firstBatchFailure(params) {
186
197
  return;
187
198
  }
188
199
 
200
+ // packages/adapter-pg/src/connection-group.ts
201
+ import { VibeError } from "@vibeorm/schema";
202
+ import { transactionAbortedError } from "@vibeorm/runtime/adapter-kit";
203
+ function connectionGroupMisuse(params) {
204
+ return new VibeError({ code: "VIBE_INTERNAL", message: `pipeline lane group: ${params.message}`, meta: { reason: "pipeline-lane-group" } });
205
+ }
206
+ function openConnectionGroup(params) {
207
+ const { driver } = params;
208
+ const seats = [];
209
+ let sealed = false;
210
+ let sent = false;
211
+ const settle = (entries, outcome) => {
212
+ let failure;
213
+ entries.forEach((seat, index) => {
214
+ const issued = seat.issued;
215
+ if (issued === undefined)
216
+ return;
217
+ const step = outcome.steps[index];
218
+ if (step === undefined) {
219
+ const error = driver.mapError(outcome.transportError);
220
+ failure ??= error;
221
+ issued.reject(error);
222
+ } else if (step.status === "ok") {
223
+ issued.resolve(step.result.rows);
224
+ } else if (step.status === "failed") {
225
+ const error = driver.mapError(step.error);
226
+ failure ??= error;
227
+ issued.reject(error);
228
+ } else {
229
+ issued.reject(transactionAbortedError({ cause: failure }));
230
+ }
231
+ });
232
+ };
233
+ const flushIfReady = () => {
234
+ if (!sealed || sent)
235
+ return;
236
+ for (const seat of seats)
237
+ if (seat.open && seat.issued === undefined && !seat.done)
238
+ return;
239
+ sent = true;
240
+ const entries = seats.filter((seat) => seat.issued !== undefined);
241
+ if (entries.length === 0)
242
+ return;
243
+ const sending = new Promise((resolve) => {
244
+ resolve(driver.send(entries.map((seat) => seat.issued.entry)));
245
+ });
246
+ sending.then((outcome) => {
247
+ settle(entries, outcome);
248
+ }, (error) => {
249
+ const mapped = driver.mapError(error);
250
+ for (const seat of entries)
251
+ seat.issued?.reject(mapped);
252
+ });
253
+ };
254
+ const refused = (message) => () => Promise.reject(connectionGroupMisuse({ message }));
255
+ const subAdapter = (seat) => ({
256
+ ...driver.base,
257
+ execute: (call) => {
258
+ if (seat.issued !== undefined || seat.done)
259
+ return Promise.reject(connectionGroupMisuse({ message: "a sub-lane issues ONE statement" }));
260
+ if (!seat.open || sent)
261
+ return Promise.reject(connectionGroupMisuse({ message: "a closed sub-lane issues nothing" }));
262
+ if (call.execution !== undefined) {
263
+ seat.done = true;
264
+ flushIfReady();
265
+ return Promise.reject(connectionGroupMisuse({ message: "a sub-lane statement takes no read options" }));
266
+ }
267
+ const pending = new Promise((resolve, reject) => {
268
+ const step = driver.prepare(call);
269
+ seat.issued = { entry: { step, call }, resolve, reject };
270
+ });
271
+ if (seat.issued === undefined) {
272
+ seat.done = true;
273
+ flushIfReady();
274
+ return pending.then(undefined, (error) => {
275
+ throw driver.mapError(error);
276
+ });
277
+ }
278
+ flushIfReady();
279
+ return pending;
280
+ },
281
+ executeUnsafe: refused("a sub-lane runs no raw statement"),
282
+ transaction: refused("a sub-lane opens no transaction"),
283
+ connect: refused("a sub-lane does not connect"),
284
+ disconnect: refused("a sub-lane does not disconnect")
285
+ });
286
+ return {
287
+ lane: () => {
288
+ if (sealed)
289
+ throw connectionGroupMisuse({ message: "a sub-lane is opened only before the group is sealed" });
290
+ const seat = { index: seats.length, open: true, done: false };
291
+ seats.push(seat);
292
+ return {
293
+ adapter: subAdapter(seat),
294
+ close: () => {
295
+ if (!seat.open)
296
+ return;
297
+ seat.open = false;
298
+ flushIfReady();
299
+ }
300
+ };
301
+ },
302
+ seal: () => {
303
+ if (sealed)
304
+ return;
305
+ sealed = true;
306
+ flushIfReady();
307
+ }
308
+ };
309
+ }
310
+
311
+ // packages/adapter-pg/src/connection-string.ts
312
+ import { VibeError as VibeError2 } from "@vibeorm/schema";
313
+ function lastValue(params) {
314
+ const values = params.search.getAll(params.key);
315
+ return values.length === 0 ? undefined : values[values.length - 1];
316
+ }
317
+ function honourSystemRootCert(params) {
318
+ const { connectionString, option } = params;
319
+ const queryStart = connectionString.indexOf("?");
320
+ if (queryStart === -1)
321
+ return connectionString;
322
+ const hashStart = connectionString.indexOf("#", queryStart);
323
+ const queryEnd = hashStart === -1 ? connectionString.length : hashStart;
324
+ const search = new URLSearchParams(connectionString.slice(queryStart + 1, queryEnd));
325
+ if (lastValue({ search, key: "sslrootcert" }) !== "system")
326
+ return connectionString;
327
+ const sslmode = lastValue({ search, key: "sslmode" });
328
+ const ssl = lastValue({ search, key: "ssl" });
329
+ const weaker = sslmode !== undefined && sslmode !== "verify-full" ? { parameter: "sslmode", value: sslmode } : ssl !== undefined && ssl !== "true" && ssl !== "1" ? { parameter: "ssl", value: ssl } : undefined;
330
+ if (weaker !== undefined) {
331
+ throw new VibeError2({
332
+ code: "VIBE_CONFIG",
333
+ message: `pgAdapter \`${option}\` combines sslrootcert=system with ${weaker.parameter}=${JSON.stringify(weaker.value)}. ` + "libpq allows sslrootcert=system only with sslmode=verify-full (its default when sslmode is omitted): any certificate " + "the system trusts would otherwise vouch for a server that is not yours. Use sslmode=verify-full, or remove the setting.",
334
+ meta: { adapter: "pg", reason: "sslrootcert-system-weak-sslmode", option, parameter: weaker.parameter, value: weaker.value }
335
+ });
336
+ }
337
+ search.delete("sslrootcert");
338
+ search.delete("ssl");
339
+ search.set("sslmode", "verify-full");
340
+ const query = search.toString().replace(/\+/g, "%20");
341
+ return `${connectionString.slice(0, queryStart)}?${query}${connectionString.slice(queryEnd)}`;
342
+ }
343
+
189
344
  // packages/adapter-pg/src/client-turns.ts
190
345
  function createClientTurns() {
191
346
  let outstanding = 0;
347
+ let failure;
192
348
  let previous = Promise.resolve();
193
349
  const release = () => {
194
350
  outstanding -= 1;
351
+ failure = undefined;
352
+ };
353
+ const fail = (error) => {
354
+ outstanding -= 1;
355
+ failure = { error };
195
356
  };
196
357
  return {
197
358
  run: (send) => {
@@ -199,16 +360,17 @@ function createClientTurns() {
199
360
  resolve(send());
200
361
  }) : previous.then(send);
201
362
  outstanding += 1;
202
- previous = running.then(release, release);
363
+ previous = running.then(release, fail);
203
364
  return running;
204
365
  },
205
- outstanding: () => outstanding
366
+ outstanding: () => outstanding,
367
+ lastFailure: () => failure
206
368
  };
207
369
  }
208
370
 
209
371
  // packages/adapter-pg/src/read-cancellation.ts
210
372
  import pg2 from "pg";
211
- import { VibeError as VibeError2 } from "@vibeorm/schema";
373
+ import { VibeError as VibeError4 } from "@vibeorm/schema";
212
374
  import { assertReadExecution, readCancelledError } from "@vibeorm/runtime/adapter-kit";
213
375
 
214
376
  // packages/adapter-pg/src/config.ts
@@ -217,7 +379,7 @@ var PG_READ_CANCELLATION = Object.freeze({
217
379
  });
218
380
 
219
381
  // packages/adapter-pg/src/errors.ts
220
- import { VibeError } from "@vibeorm/schema";
382
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
221
383
  var PG_SQLSTATE_ERROR_CODES = Object.freeze({
222
384
  "23P01": "VIBE_EXCLUSION_VIOLATION",
223
385
  "23505": "VIBE_UNIQUE_VIOLATION",
@@ -264,7 +426,7 @@ function pgErrorSqlstate(params) {
264
426
  }
265
427
  function mapPgDriverError(params) {
266
428
  const { error } = params;
267
- if (error instanceof VibeError)
429
+ if (error instanceof VibeError3)
268
430
  return error;
269
431
  const source = error !== null && typeof error === "object" ? error : {};
270
432
  const sqlstate = pgErrorSqlstate({ error });
@@ -287,7 +449,7 @@ function mapPgDriverError(params) {
287
449
  const driverMessage = readStringField({ source, key: "message" }) ?? (typeof error === "string" ? error : "unknown postgres driver error");
288
450
  const hint = sqlstate === undefined ? undefined : PG_SQLSTATE_HINTS[sqlstate];
289
451
  const message = hint === undefined ? driverMessage : `${driverMessage}. ${hint}`;
290
- return new VibeError({ code, message, meta, cause: error });
452
+ return new VibeError3({ code, message, meta, cause: error });
291
453
  }
292
454
  async function runMapped(fn) {
293
455
  try {
@@ -310,14 +472,14 @@ function createPgReadCancellation(params) {
310
472
  if (query.execution?.cancellation !== "running")
311
473
  return query.send();
312
474
  if (params.target.pipeline === true || (params.target.connectionParameters?.query_timeout ?? 0) > 0) {
313
- throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Running cancellation requires a non-pipelined target without local query_timeout", meta: { capability: "readCancellation", reason: "target-lifetime" } });
475
+ throw new VibeError4({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Running cancellation requires a non-pipelined target without local query_timeout", meta: { capability: "readCancellation", reason: "target-lifetime" } });
314
476
  }
315
477
  const address = params.connectionString;
316
478
  if (address === undefined)
317
- throw new VibeError2({ code: "VIBE_INTERNAL", message: "Running cancellation requires a control address" });
479
+ throw new VibeError4({ code: "VIBE_INTERNAL", message: "Running cancellation requires a control address" });
318
480
  const token = `vibe_cancel_${crypto.randomUUID()}`;
319
481
  if (!URL.canParse(address))
320
- throw new VibeError2({ code: "VIBE_CONFIG", message: "The PostgreSQL cancellation control address must be a URL" });
482
+ throw new VibeError4({ code: "VIBE_CONFIG", message: "The PostgreSQL cancellation control address must be a URL" });
321
483
  const url = new URL(address);
322
484
  url.searchParams.delete("query_timeout");
323
485
  url.searchParams.set("connect_timeout", String(PG_READ_CANCELLATION.controlTimeoutMs / 1000));
@@ -364,7 +526,7 @@ function createPgReadCancellation(params) {
364
526
  if (preflightAborted)
365
527
  throw readCancelledError({ stage: "waiting-for-connection" });
366
528
  if (witness === undefined || typeof witness.pid !== "number" || typeof witness.backend_start !== "string" || typeof witness.datname !== "string" || typeof witness.usename !== "string" || witness.tag !== token) {
367
- throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Cannot prove the exact PostgreSQL cancellation target", meta: { capability: "readCancellation", reason: "target-witness" } });
529
+ throw new VibeError4({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Cannot prove the exact PostgreSQL cancellation target", meta: { capability: "readCancellation", reason: "target-witness" } });
368
530
  }
369
531
  assertReadExecution({ execution: query.execution, effect: "read", support, provider: "postgresql", stage: "waiting-for-connection" });
370
532
  listener = () => {
@@ -381,7 +543,7 @@ function createPgReadCancellation(params) {
381
543
  throw error;
382
544
  };
383
545
  settlementTimer = setTimeout(() => {
384
- controlFailure = new VibeError2({ code: "VIBE_ADAPTER", message: "PostgreSQL read did not settle after cancellation; its exact session was discarded", meta: { reason: "read-cancellation-unsettled", databaseCancelled: false } });
546
+ controlFailure = new VibeError4({ code: "VIBE_ADAPTER", message: "PostgreSQL read did not settle after cancellation; its exact session was discarded", meta: { reason: "read-cancellation-unsettled", databaseCancelled: false } });
385
547
  if (!terminated) {
386
548
  terminated = true;
387
549
  params.terminate();
@@ -393,7 +555,7 @@ function createPgReadCancellation(params) {
393
555
  })).then((result) => {
394
556
  signalSent = rowsOf(result)[0]?.signalled === true;
395
557
  if (!signalSent)
396
- failControl(new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "The control endpoint did not signal the exact PostgreSQL target", meta: { capability: "readCancellation", reason: "target-not-signalled", databaseCancelled: false } }));
558
+ failControl(new VibeError4({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "The control endpoint did not signal the exact PostgreSQL target", meta: { capability: "readCancellation", reason: "target-not-signalled", databaseCancelled: false } }));
397
559
  }, failControl);
398
560
  cancel.then(() => {
399
561
  return;
@@ -424,12 +586,12 @@ function createPgReadCancellation(params) {
424
586
  }, () => {
425
587
  return;
426
588
  });
427
- const sqlstate = error instanceof VibeError2 ? error.meta.sqlstate : pgErrorSqlstate({ error });
589
+ const sqlstate = error instanceof VibeError4 ? error.meta.sqlstate : pgErrorSqlstate({ error });
428
590
  if (terminated && controlFailure !== undefined && sqlstate === undefined)
429
591
  throw controlFailure;
430
592
  if (attempted && signalSent && sqlstate === "57014") {
431
593
  const cancelled = readCancelledError({ stage: "running", databaseCancelled: true, cause: error });
432
- throw new VibeError2({ code: cancelled.code, message: cancelled.message, meta: { ...cancelled.meta, sqlstate: "57014" }, cause: error });
594
+ throw new VibeError4({ code: cancelled.code, message: cancelled.message, meta: { ...cancelled.meta, sqlstate: "57014" }, cause: error });
433
595
  }
434
596
  throw error;
435
597
  });
@@ -511,7 +673,7 @@ function serializeParamsRaw(params) {
511
673
  }
512
674
 
513
675
  // packages/adapter-pg/src/pool-config.ts
514
- import { VibeError as VibeError3 } from "@vibeorm/schema";
676
+ import { VibeError as VibeError5 } from "@vibeorm/schema";
515
677
  var PLAN_CACHE_MODES = ["auto", "force_custom_plan", "force_generic_plan"];
516
678
  function isPlanCacheMode(value) {
517
679
  return typeof value === "string" && PLAN_CACHE_MODES.includes(value);
@@ -520,7 +682,7 @@ function assertPlanCacheMode(params) {
520
682
  const { value } = params;
521
683
  if (isPlanCacheMode(value))
522
684
  return value;
523
- throw new VibeError3({
685
+ throw new VibeError5({
524
686
  code: "VIBE_CONFIG",
525
687
  message: `planCacheMode must be one of ${PLAN_CACHE_MODES.map((mode) => `"${mode}"`).join(", ")}, received ${JSON.stringify(value)}`,
526
688
  meta: { planCacheMode: value, allowed: PLAN_CACHE_MODES }
@@ -550,19 +712,31 @@ function assertIdleTimeout(params) {
550
712
  const { value } = params;
551
713
  if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 2147483647)
552
714
  return value;
553
- throw new VibeError3({
715
+ throw new VibeError5({
554
716
  code: "VIBE_CONFIG",
555
717
  message: "pgAdapter `idleTimeout` must be integer milliseconds from 0 through 2,147,483,647 (0 keeps idle connections open).",
556
718
  meta: { adapter: "pg", reason: "invalid-idle-timeout", option: "idleTimeout" }
557
719
  });
558
720
  }
721
+ function assertPoolMin(params) {
722
+ const { value, max } = params;
723
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= max)
724
+ return value;
725
+ throw new VibeError5({
726
+ code: "VIBE_CONFIG",
727
+ message: `pgAdapter \`min\` must be an integer from 0 through \`max\` (${max}).`,
728
+ meta: { adapter: "pg", reason: "invalid-pool-min", option: "min", max }
729
+ });
730
+ }
559
731
  function buildPoolConfig(params) {
560
- const { max, statementTimeout, connectionTimeout, idleTimeout } = params;
732
+ const { max, min, statementTimeout, connectionTimeout, idleTimeout } = params;
561
733
  const placement = placePlanCacheMode({
562
734
  connectionString: params.connectionString,
563
735
  planCacheMode: params.planCacheMode ?? "auto"
564
736
  });
565
737
  const config = { connectionString: placement.connectionString, max };
738
+ if (min !== undefined)
739
+ config.min = min;
566
740
  if (statementTimeout !== undefined)
567
741
  config.statement_timeout = statementTimeout;
568
742
  if (connectionTimeout !== undefined)
@@ -719,7 +893,7 @@ function parsePlainTimestampText(params) {
719
893
  }
720
894
 
721
895
  // packages/adapter-pg/src/transaction-sql.ts
722
- import { VibeError as VibeError4 } from "@vibeorm/schema";
896
+ import { VibeError as VibeError6 } from "@vibeorm/schema";
723
897
  import { refuseNestedTransactionOptions } from "@vibeorm/runtime/adapter-kit";
724
898
  var ISOLATION_LEVEL_SQL = Object.freeze({
725
899
  ReadCommitted: "READ COMMITTED",
@@ -742,7 +916,7 @@ function openTransactionStatement(params) {
742
916
  function statementTimeoutStatement(params) {
743
917
  const { timeout } = params;
744
918
  if (!Number.isInteger(timeout) || timeout < 0) {
745
- throw new VibeError4({
919
+ throw new VibeError6({
746
920
  code: "VIBE_VALIDATION",
747
921
  message: `transaction timeout must be a non-negative integer of milliseconds, received ${String(timeout)}`,
748
922
  meta: { timeout }
@@ -778,19 +952,42 @@ import {
778
952
  transactionOutcomeUnknownError,
779
953
  validateTransactionDeadline as validateTransactionDeadline2
780
954
  } from "@vibeorm/runtime/adapter-kit";
955
+ function laneGroupMisuse(params) {
956
+ return new VibeError7({ code: "VIBE_INTERNAL", message: `pipeline lane group: ${params.message}`, meta: { reason: "pipeline-lane-group" } });
957
+ }
781
958
  var MAX_STATEMENT_TIMEOUT_MS = 2147483647;
782
959
  var ignore = () => {
783
960
  return;
784
961
  };
785
962
  function predecessorClosedError() {
786
- return new VibeError5({
963
+ return new VibeError7({
787
964
  code: "VIBE_TRANSACTION",
788
965
  message: "pipeline(): an earlier lane of this ordered scope closed without issuing its statement, so this statement was not sent " + "(the operation before it failed before it reached the database)",
789
966
  meta: { reason: "pipeline-predecessor-closed" }
790
967
  });
791
968
  }
792
969
  function orderedLaneMisuse(params) {
793
- return new VibeError5({ code: "VIBE_INTERNAL", message: `pipeline(): ${params.message}`, meta: { reason: "pipeline-ordered-lane" } });
970
+ return new VibeError7({ code: "VIBE_INTERNAL", message: `pipeline(): ${params.message}`, meta: { reason: "pipeline-ordered-lane" } });
971
+ }
972
+ var COMMIT_STEP = Object.freeze({ text: "COMMIT" });
973
+ function commitSentRefusal() {
974
+ return new VibeError7({
975
+ code: "VIBE_TRANSACTION",
976
+ message: "this transaction already sent its COMMIT together with its last statement, so it accepts no further statement",
977
+ meta: { provider: PROVIDER, reason: "commit-sent" }
978
+ });
979
+ }
980
+ function finalStatementMisuse() {
981
+ return new VibeError7({
982
+ code: "VIBE_INTERNAL",
983
+ message: "withCommit(): a view carries ONE statement, the transaction's last",
984
+ meta: { reason: "with-commit-second-statement" }
985
+ });
986
+ }
987
+ function beforeCommit(params) {
988
+ const { outcome, commitIndex } = params;
989
+ const steps = outcome.steps.slice(0, commitIndex);
990
+ return steps.length < commitIndex && outcome.transportError !== undefined ? { steps, transportError: outcome.transportError } : { steps };
794
991
  }
795
992
  function finalResult(params) {
796
993
  return Array.isArray(params.result) ? params.result.at(-1) ?? { rows: [], rowCount: 0 } : params.result;
@@ -798,13 +995,13 @@ function finalResult(params) {
798
995
  var POOL_STATEMENT_NAMES = new WeakMap;
799
996
  function isConnectionLoss(params) {
800
997
  const { error } = params;
801
- if (!(error instanceof VibeError5))
998
+ if (!(error instanceof VibeError7))
802
999
  return true;
803
1000
  const sqlstate = error.meta.sqlstate;
804
1001
  return typeof sqlstate !== "string" || sqlstate.startsWith("08") || ["57P01", "57P02", "57P03"].includes(sqlstate);
805
1002
  }
806
1003
  function commitOutcomeUnknownError(params) {
807
- return new VibeError5({
1004
+ return new VibeError7({
808
1005
  code: "VIBE_TRANSACTION",
809
1006
  message: "the connection was lost while COMMIT was in flight, so the transaction's outcome is unknown: the write may have committed. The connection was discarded; inspect the data before retrying.",
810
1007
  meta: { provider: PROVIDER, reason: "transaction-outcome-unknown", outcome: "unknown", stage: "commit" },
@@ -833,7 +1030,7 @@ function observeReadyForQuery(params) {
833
1030
  client.on("end", onEnd);
834
1031
  return {
835
1032
  reusableAfter: async ({ error, named }) => {
836
- if (!(error instanceof VibeError5))
1033
+ if (!(error instanceof VibeError7))
837
1034
  return false;
838
1035
  const sqlstate = error.meta.sqlstate;
839
1036
  if (typeof sqlstate !== "string" || NEVER_REUSE_SQLSTATE_CLASSES.has(sqlstate.slice(0, 2)))
@@ -870,7 +1067,7 @@ function guardBackgroundErrors(params) {
870
1067
  }
871
1068
  var DIALECT = "postgres";
872
1069
  var PROVIDER = "postgresql";
873
- var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
1070
+ var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native", bytes: "buffer" });
874
1071
  var ROUND_TRIPS = "network";
875
1072
  var READ_SNAPSHOT = Object.freeze({ isolationLevel: "RepeatableRead", accessMode: "readOnly" });
876
1073
  var PIPELINING = "batched";
@@ -900,7 +1097,7 @@ function parseZoneLessAsUtc(value) {
900
1097
  if (!Number.isNaN(parsed.getTime()))
901
1098
  return parsed;
902
1099
  }
903
- return driverTypeParser(TIMESTAMP_OID, "text")(value);
1100
+ return parseExtendedDateTimeText({ text: value }) ?? driverTypeParser(TIMESTAMP_OID, "text")(value);
904
1101
  }
905
1102
  function parseZoneLessArrayAsUtc(value) {
906
1103
  const mapElements = (element) => {
@@ -960,24 +1157,52 @@ function takeAddress(params) {
960
1157
  if (options?.pool !== undefined && options.pool !== null)
961
1158
  return { pool: options.pool };
962
1159
  const connectionString = options?.connectionString;
963
- if (typeof connectionString === "string" && connectionString.trim() !== "")
964
- return { connectionString };
965
- throw new VibeError5({
1160
+ if (typeof connectionString === "string" && connectionString.trim() !== "") {
1161
+ return { connectionString: honourSystemRootCert({ connectionString, option: "connectionString" }) };
1162
+ }
1163
+ throw new VibeError7({
966
1164
  code: "VIBE_CONFIG",
967
1165
  message: "pgAdapter needs an explicit address: pass `connectionString` (a PostgreSQL URL) or an existing `pool`. " + "The adapter does not read DATABASE_URL or the PG* environment variables.",
968
1166
  meta: { adapter: "pg", reason: "missing-address" }
969
1167
  });
970
1168
  }
1169
+ function transactionsTakeOneRoundTrip(params) {
1170
+ const { address } = params;
1171
+ if (!batchDriverAvailable())
1172
+ return false;
1173
+ const config = "pool" in address ? address.pool.options ?? {} : { connectionString: address.connectionString };
1174
+ if (config.pipeline === true)
1175
+ return false;
1176
+ let timeout = config.query_timeout;
1177
+ const connectionString = config.connectionString;
1178
+ if (typeof connectionString === "string") {
1179
+ if (!URL.canParse(connectionString))
1180
+ return false;
1181
+ const fromUrl = new URL(connectionString).searchParams.get("query_timeout");
1182
+ if (fromUrl !== null)
1183
+ timeout = fromUrl;
1184
+ }
1185
+ timeout ||= pg3.defaults.query_timeout;
1186
+ return !(Number(timeout ?? 0) > 0);
1187
+ }
971
1188
  function pgAdapter(options) {
972
1189
  const address = takeAddress({ options });
973
- const cancellationAddress = options.cancellationConnectionString ?? ("connectionString" in address ? address.connectionString : undefined);
1190
+ const cancellationAddress = options.cancellationConnectionString !== undefined ? honourSystemRootCert({ connectionString: options.cancellationConnectionString, option: "cancellationConnectionString" }) : ("connectionString" in address) ? address.connectionString : undefined;
974
1191
  const readCancellation = cancellationAddress === undefined ? "before-dispatch" : "running";
975
1192
  const usePrepared = options?.preparedStatements ?? false;
976
1193
  const stmtCacheMax = options?.stmtCacheMax ?? 1000;
977
1194
  const planCacheMode = options?.planCacheMode === undefined ? "auto" : assertPlanCacheMode({ value: options.planCacheMode });
978
1195
  const idleTimeout = "pool" in address || options?.idleTimeout === undefined ? undefined : assertIdleTimeout({ value: options.idleTimeout });
1196
+ if (options?.min !== undefined && "pool" in address) {
1197
+ throw new VibeError7({
1198
+ code: "VIBE_CONFIG",
1199
+ message: "pgAdapter `min` configures the pool the adapter builds \u2014 it cannot be applied to a caller-supplied `pool`; set `min` (and `idleTimeoutMillis`) on that pool instead",
1200
+ meta: { adapter: "pg", reason: "pool-min-with-injected-pool", option: "min" }
1201
+ });
1202
+ }
1203
+ const poolMin = options?.min === undefined ? undefined : assertPoolMin({ value: options.min, max: options.max ?? 10 });
979
1204
  if (planCacheMode !== "auto" && "pool" in address) {
980
- throw new VibeError5({
1205
+ throw new VibeError7({
981
1206
  code: "VIBE_CONFIG",
982
1207
  message: "planCacheMode configures the pool the adapter builds \u2014 it cannot be applied to a caller-supplied `pool`; " + 'set `options: "-c plan_cache_mode=\u2026"` on that pool instead',
983
1208
  meta: { planCacheMode }
@@ -999,6 +1224,7 @@ function pgAdapter(options) {
999
1224
  const config = buildPoolConfig({
1000
1225
  connectionString: address.connectionString,
1001
1226
  max: options?.max ?? 10,
1227
+ ...poolMin === undefined ? {} : { min: poolMin },
1002
1228
  ...options?.statementTimeout === undefined ? {} : { statementTimeout: options.statementTimeout },
1003
1229
  ...options?.connectionTimeout === undefined ? {} : { connectionTimeout: options.connectionTimeout },
1004
1230
  ...idleTimeout === undefined ? {} : { idleTimeout },
@@ -1048,14 +1274,22 @@ function pgAdapter(options) {
1048
1274
  return { rows: final.rows, affectedRows: final.rowCount ?? 0 };
1049
1275
  }
1050
1276
  function createClientAdapter(params) {
1051
- const { dispatch, scope, budget, deferred, cancellation, pipelines, lane } = params;
1277
+ const { dispatch, scope, budget, deferred, cancellation, pipelines, lane, connection, final } = params;
1052
1278
  const laneOf = lane === undefined ? {} : { lane };
1279
+ const finalOf = () => {
1280
+ if (final === undefined)
1281
+ return {};
1282
+ if (final.used)
1283
+ throw finalStatementMisuse();
1284
+ final.used = true;
1285
+ return { commit: true, ...final.discardRows === true ? { discardRows: true } : {} };
1286
+ };
1053
1287
  const waitAsLane = (wait) => lane === undefined || pipelines === null ? wait : pipelines.waitAs({ lane, wait });
1054
1288
  const rawClient = {
1055
- query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 || isSingleStatementText({ text: config.text }), ...laneOf })
1289
+ query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 || isSingleStatementText({ text: config.text }), ...laneOf, ...finalOf() })
1056
1290
  };
1057
1291
  const savepointControl = (statement, cleanup) => {
1058
- if (deferred !== null && deferred.routeSavepoint({ text: statement }) !== "send")
1292
+ if (deferred !== null && deferred.routeSavepoint({ text: statement, cleanup }) !== "send")
1059
1293
  return;
1060
1294
  return runMapped(() => dispatch({
1061
1295
  config: { text: statement },
@@ -1074,22 +1308,46 @@ function pgAdapter(options) {
1074
1308
  roundTrips: ROUND_TRIPS,
1075
1309
  readSnapshot: READ_SNAPSHOT,
1076
1310
  pipelining: pipelines === null ? "ordered" : PIPELINING,
1311
+ connectionIdentity: connection,
1312
+ ...params.topLevel !== true || deferred === null ? {} : {
1313
+ deferStatement: (statement) => {
1314
+ budget.assertUsable({ stage: "statement" });
1315
+ deferred.defer({ text: statement.text, values: prepareBatchValues({ values: statement.values }) });
1316
+ }
1317
+ },
1318
+ ...params.commits !== true || deferred === null || scope.depth !== 0 || final !== undefined ? {} : {
1319
+ withCommit: (options) => createClientAdapter({
1320
+ dispatch,
1321
+ scope,
1322
+ budget,
1323
+ deferred,
1324
+ cancellation,
1325
+ pipelines,
1326
+ ...laneOf,
1327
+ connection,
1328
+ final: { used: false, ...options?.rows === "discard" ? { discardRows: true } : {} }
1329
+ })
1330
+ },
1077
1331
  ...pipelines === null ? {} : {
1078
1332
  ...params.ordered === true ? { orderedPipeline: true } : {},
1079
1333
  pipeline: ({ lanes: count, ordered }) => {
1080
1334
  budget.assertUsable({ stage: "statement" });
1335
+ if (lane?.seat !== undefined)
1336
+ throw laneGroupMisuse({ message: "a sub-lane opens no pipeline scope" });
1337
+ if (lane?.group !== undefined)
1338
+ throw laneGroupMisuse({ message: "a lane opens no pipeline scope while its group is open" });
1081
1339
  if (!Number.isSafeInteger(count) || count < 1) {
1082
- throw new VibeError5({ code: "VIBE_VALIDATION", message: "pipeline() needs a positive whole number of lanes", meta: { lanes: count } });
1340
+ throw new VibeError7({ code: "VIBE_VALIDATION", message: "pipeline() needs a positive whole number of lanes", meta: { lanes: count } });
1083
1341
  }
1084
1342
  if (ordered === true && params.ordered !== true) {
1085
- throw new VibeError5({
1343
+ throw new VibeError7({
1086
1344
  code: "VIBE_VALIDATION",
1087
- message: "pipeline({ ordered: true }) is offered only on a handle that declares orderedPipeline (the top-level or a savepoint handle of a transaction without a deadline or a timeout, outside a pipeline lane)",
1345
+ message: "pipeline({ ordered: true }) is offered only on a handle that declares orderedPipeline (the top-level or a savepoint handle of a transaction without a deadline, outside a pipeline lane)",
1088
1346
  meta: { ordered: true, reason: "ordered-pipeline-undeclared" }
1089
1347
  });
1090
1348
  }
1091
1349
  const laneHandle = (opened) => ({
1092
- adapter: createClientAdapter({ dispatch, scope, budget, deferred, cancellation, pipelines, lane: opened }),
1350
+ adapter: createClientAdapter({ dispatch, scope, budget, deferred, cancellation, pipelines, lane: opened, connection, ...params.commits === true && opened.seat === undefined ? { commits: true } : {} }),
1093
1351
  close: () => {
1094
1352
  pipelines.close(opened);
1095
1353
  },
@@ -1098,6 +1356,19 @@ function pgAdapter(options) {
1098
1356
  budget.assertUsable({ stage: "statement" });
1099
1357
  return laneHandle(pipelines.follow(opened));
1100
1358
  }
1359
+ },
1360
+ ...opened.order !== undefined || opened.seat !== undefined || lane !== undefined ? {} : {
1361
+ group: (options) => {
1362
+ const opening = pipelines.group(opened, options?.final === true && params.commits === true);
1363
+ if (opening === undefined)
1364
+ return;
1365
+ return {
1366
+ lane: () => laneHandle(pipelines.seat(opening)),
1367
+ seal: () => {
1368
+ pipelines.seal(opening);
1369
+ }
1370
+ };
1371
+ }
1101
1372
  }
1102
1373
  });
1103
1374
  return (ordered === true ? pipelines.openOrdered(count) : pipelines.open(count)).map(laneHandle);
@@ -1114,7 +1385,7 @@ function pgAdapter(options) {
1114
1385
  await waitAsLane(turn);
1115
1386
  budget.assertUsable({ stage: "statement" });
1116
1387
  assertReadExecution2({ ...execParams, support: cancellation.support, provider: PROVIDER, stage: "waiting-for-connection" });
1117
- const queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== "running", execution: execParams.execution, onReadControl: execParams.onReadControl, ...laneOf }) };
1388
+ const queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== "running", execution: execParams.execution, onReadControl: execParams.onReadControl, ...laneOf, ...finalOf() }) };
1118
1389
  return runExecute({ queryable, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
1119
1390
  } });
1120
1391
  },
@@ -1128,6 +1399,10 @@ function pgAdapter(options) {
1128
1399
  async transaction(fn, nestedOptions) {
1129
1400
  refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
1130
1401
  budget.assertUsable({ stage: "nested" });
1402
+ if (lane?.group !== undefined)
1403
+ throw laneGroupMisuse({ message: "a lane opens no savepoint while its group is open" });
1404
+ if (lane?.seat !== undefined)
1405
+ throw laneGroupMisuse({ message: "a sub-lane opens no savepoint" });
1131
1406
  pipelines?.flush();
1132
1407
  const leave = lane === undefined || pipelines === null ? ignore : pipelines.enter(lane);
1133
1408
  return runNestedSavepoint({
@@ -1137,7 +1412,7 @@ function pgAdapter(options) {
1137
1412
  body: (child) => {
1138
1413
  leave();
1139
1414
  const ordered = lane === undefined && params.ordered === true ? { ordered: true } : {};
1140
- return fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation, pipelines, ...laneOf, ...ordered }));
1415
+ return fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation, pipelines, connection, ...laneOf, ...ordered }));
1141
1416
  }
1142
1417
  }).finally(leave);
1143
1418
  },
@@ -1287,8 +1562,14 @@ function pgAdapter(options) {
1287
1562
  const settingsStatement = settings.length === 0 ? undefined : renderTransactionSettings({ settings });
1288
1563
  const client = await runMapped(() => getPool().connect());
1289
1564
  const turns = createClientTurns();
1565
+ let gathering = null;
1566
+ let gatheringsPending = 0;
1567
+ const runTurn = (send) => {
1568
+ gathering = null;
1569
+ return turns.run(send);
1570
+ };
1290
1571
  const onClient = {
1291
- query: (config) => turns.run(() => client.query(config)),
1572
+ query: (config) => runTurn(() => client.query(config)),
1292
1573
  get pipeline() {
1293
1574
  return client.pipeline;
1294
1575
  },
@@ -1307,11 +1588,55 @@ function pgAdapter(options) {
1307
1588
  const scope = createSavepointScope();
1308
1589
  const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });
1309
1590
  const capable = supportsBatch({ client });
1310
- const deferOpening = settingsStatement === undefined && capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);
1591
+ const deferOpening = (settingsStatement === undefined || transactionOptions?.deferSettings === true) && capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);
1311
1592
  const opening = deferOpening ? [beginStatement(openingOptions), ...openingOptions.timeout === undefined ? [] : [statementTimeoutStatement({ timeout: openingOptions.timeout })]] : [];
1312
- const deferred = createDeferredControl({ opening });
1593
+ const deferred = createDeferredControl({ opening, ...budget.totalMs === null ? { deferReleases: true } : {} });
1594
+ const settingsDeferred = deferOpening && settingsStatement !== undefined;
1595
+ if (settingsDeferred)
1596
+ deferred.defer({ text: settingsStatement.text, values: prepareBatchValues({ values: settingsStatement.values }) });
1597
+ const openingLength = opening.length + (settingsDeferred ? 1 : 0);
1598
+ const openingStepError = (index, error) => {
1599
+ const mapped = mapPgDriverError({ error });
1600
+ if (!settingsDeferred || index !== opening.length || typeof mapped.meta.sqlstate !== "string")
1601
+ return mapped;
1602
+ return transactionSettingRefusedError({ provider: PROVIDER, settings, setting: findRefusedSetting({ settings, cause: mapped }), cause: mapped, deferred: true });
1603
+ };
1313
1604
  let openingFailure;
1314
1605
  let inFlight = null;
1606
+ const commits = capable && budget.totalMs === null && !((client.connectionParameters?.query_timeout ?? 0) > 0);
1607
+ let ridden;
1608
+ let ordinaryOpen = 0;
1609
+ const ridesNow = () => commits && ridden === undefined && !cancellationQueue && !cancellationAttempted && connectionFailure === undefined && openingFailure === undefined && scope.state.open === null && scope.state.aborted === null && scope.state.poisoned === null;
1610
+ const settleRidden = (entry, outcome, commitIndex) => {
1611
+ const step = outcome.steps[commitIndex];
1612
+ const answeredError = step?.status === "failed" ? mapPgDriverError({ error: step.error }) : undefined;
1613
+ if (step === undefined || answeredError !== undefined && isConnectionLoss({ error: answeredError })) {
1614
+ commitProgress = "unanswered";
1615
+ discard = true;
1616
+ budget.close({ outcome: "unknown" });
1617
+ entry.state = "lost";
1618
+ entry.error = commitOutcomeUnknownError({ cause: answeredError ?? outcome.transportError ?? firstBatchFailure({ outcome })?.error });
1619
+ } else if (step.status === "skipped") {
1620
+ commitProgress = "not-sent";
1621
+ entry.state = "skipped";
1622
+ } else if (step.status === "failed" || step.result.command === "ROLLBACK") {
1623
+ commitProgress = "answered";
1624
+ entry.state = "failed";
1625
+ entry.error = answeredError ?? transactionAbortedError2({ cause: scope.state.aborted?.cause });
1626
+ } else {
1627
+ entry.state = "committed";
1628
+ }
1629
+ };
1630
+ const trackRidden = (batch, commitIndex) => {
1631
+ commitProgress = "sent";
1632
+ const entry = { settled: Promise.resolve(), state: "in-flight" };
1633
+ entry.settled = batch.then((outcome) => {
1634
+ settleRidden(entry, outcome, commitIndex);
1635
+ }, (error) => {
1636
+ settleRidden(entry, { steps: [], transportError: error }, commitIndex);
1637
+ });
1638
+ ridden = entry;
1639
+ };
1315
1640
  const sendWithControl = (dispatchParams) => {
1316
1641
  const { config, cleanup } = dispatchParams;
1317
1642
  const handOver = () => {
@@ -1320,9 +1645,11 @@ function pgAdapter(options) {
1320
1645
  return cancellation.run({ config, execution: dispatchParams.execution, onReadControl: dispatchParams.onReadControl, send: () => onClient.query(config) });
1321
1646
  };
1322
1647
  if (cleanup !== true && cancellationAttempted)
1323
- throw transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) });
1648
+ throw transactionAbortedError2({ cause: readCancelledError2({ stage: "running" }) });
1324
1649
  if (cleanup !== true && openingFailure !== undefined)
1325
- throw transactionAbortedError({ cause: openingFailure });
1650
+ throw transactionAbortedError2({ cause: openingFailure });
1651
+ if (cleanup !== true && ridden !== undefined)
1652
+ throw commitSentRefusal();
1326
1653
  let bound;
1327
1654
  if (cleanup !== true && budget.totalMs !== null) {
1328
1655
  if (scope.state.poisoned !== null)
@@ -1342,24 +1669,39 @@ function pgAdapter(options) {
1342
1669
  });
1343
1670
  }
1344
1671
  const pendingControl = cleanup !== true && deferred.hasPending();
1345
- if (!pendingControl && bound === undefined)
1672
+ const rides = dispatchParams.commit === true && cleanup !== true && dispatchParams.batchable === true && bound === undefined && ordinaryOpen === 1 && ridesNow();
1673
+ if (!pendingControl && bound === undefined && !rides) {
1674
+ if ((gathering?.phase === "turn" || turns.outstanding() > 0) && gathers(dispatchParams) && deferred.opened())
1675
+ return gatherInTurn(dispatchParams);
1346
1676
  return handOver();
1347
- const statement = dispatchParams.batchable === true ? [{ text: config.text, values: prepareBatchValues({ values: config.values ?? [] }) }] : [];
1677
+ }
1678
+ const statement = dispatchParams.batchable === true ? [{ text: config.text, values: prepareBatchValues({ values: config.values ?? [] }), ...rides && dispatchParams.discardRows === true ? { discardRows: true } : {} }] : [];
1679
+ assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1348
1680
  const carriesOpening = !deferred.opened();
1349
- const control = pendingControl ? [...deferred.take()] : [];
1681
+ const control = pendingControl ? [...deferred.takeSteps()] : [];
1350
1682
  if (bound !== undefined)
1351
- control.push(bound);
1352
- const openingSteps = carriesOpening ? opening.length : 0;
1353
- assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1683
+ control.push({ text: bound });
1684
+ const openingSteps = carriesOpening ? openingLength : 0;
1354
1685
  if (statement.length > 0)
1355
1686
  dispatchParams.sent?.();
1356
- const burst = turns.run(() => runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES })).then((outcome) => {
1687
+ const commitIndex = control.length + statement.length;
1688
+ const batch = runTurn(() => runBatch({ client, steps: [...control, ...statement, ...rides ? [COMMIT_STEP] : []], types: EXACT_TYPES }));
1689
+ if (rides)
1690
+ trackRidden(batch, commitIndex);
1691
+ const burst = batch.then((received) => {
1692
+ const outcome = rides ? beforeCommit({ outcome: received, commitIndex }) : received;
1357
1693
  const failure = firstBatchFailure({ outcome });
1358
1694
  if (failure !== undefined) {
1359
- if (failure.index < openingSteps || outcome.transportError !== undefined && openingSteps > 0) {
1360
- openingFailure = mapPgDriverError({ error: failure.error });
1695
+ const openingFailed = failure.index < openingSteps || outcome.transportError !== undefined && openingSteps > 0;
1696
+ if (openingFailed) {
1697
+ openingFailure = openingStepError(failure.index, failure.error);
1698
+ }
1699
+ if (gatheringsPending > 0) {
1700
+ const mapped = openingFailed && openingFailure !== undefined ? openingFailure : mapPgDriverError({ error: failure.error });
1701
+ if (typeof mapped.meta.sqlstate === "string")
1702
+ markTransactionAborted({ scope, cause: mapped });
1361
1703
  }
1362
- throw failure.error;
1704
+ throw openingFailed && settingsDeferred && failure.index === opening.length && openingFailure !== undefined ? openingFailure : failure.error;
1363
1705
  }
1364
1706
  if (statement.length === 0)
1365
1707
  return handOver();
@@ -1410,14 +1752,14 @@ function pgAdapter(options) {
1410
1752
  });
1411
1753
  return;
1412
1754
  }
1413
- const mapped = mapPgDriverError({ error: failure.error });
1755
+ const mapped = failure.index < openingSteps ? openingStepError(failure.index, failure.error) : mapPgDriverError({ error: failure.error });
1414
1756
  if (failure.index < openingSteps || outcome.transportError !== undefined && openingSteps > 0)
1415
1757
  openingFailure = mapped;
1416
1758
  if (typeof mapped.meta.sqlstate === "string")
1417
1759
  markTransactionAborted({ scope, cause: mapped });
1418
1760
  const answered = outcome.transportError === undefined;
1419
1761
  if (failure.index < controlSteps) {
1420
- const later = answered || openingFailure !== undefined ? transactionAbortedError({ cause: openingFailure ?? mapped }) : mapped;
1762
+ const later = answered || openingFailure !== undefined ? transactionAbortedError2({ cause: openingFailure ?? mapped }) : mapped;
1421
1763
  live.forEach((entry, index) => {
1422
1764
  settleQueued(entry, { error: index === 0 ? mapped : later });
1423
1765
  });
@@ -1428,7 +1770,7 @@ function pgAdapter(options) {
1428
1770
  const position = controlSteps + index;
1429
1771
  const step = outcome.steps[position];
1430
1772
  if (step === undefined) {
1431
- settleQueued(entry, { error: position === failure.index || openingFailure === undefined ? mapped : transactionAbortedError({ cause: openingFailure }) });
1773
+ settleQueued(entry, { error: position === failure.index || openingFailure === undefined ? mapped : transactionAbortedError2({ cause: openingFailure }) });
1432
1774
  } else if (step.status === "ok") {
1433
1775
  settleQueued(entry, { result: step.result });
1434
1776
  } else if (step.status === "failed") {
@@ -1437,12 +1779,36 @@ function pgAdapter(options) {
1437
1779
  abortCause = error;
1438
1780
  settleQueued(entry, { error });
1439
1781
  } else {
1440
- settleQueued(entry, { error: transactionAbortedError({ cause: abortCause }) });
1782
+ settleQueued(entry, { error: transactionAbortedError2({ cause: abortCause }) });
1441
1783
  }
1442
1784
  });
1443
1785
  };
1444
- const sendRefusal = () => cancellationAttempted ? transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) }) : openingFailure !== undefined ? transactionAbortedError({ cause: openingFailure }) : scope.state.poisoned ?? (scope.state.aborted === null ? undefined : transactionAbortedError({ cause: scope.state.aborted.cause }));
1445
- const sendQueued = (entries) => {
1786
+ const sendRefusal = () => ridden !== undefined ? commitSentRefusal() : cancellationAttempted ? transactionAbortedError2({ cause: readCancelledError2({ stage: "running" }) }) : openingFailure !== undefined ? transactionAbortedError2({ cause: openingFailure }) : scope.state.poisoned ?? (scope.state.aborted === null ? undefined : transactionAbortedError2({ cause: scope.state.aborted.cause }));
1787
+ const ridesQueued = (live) => {
1788
+ const last = live.at(-1);
1789
+ if (last === undefined || last.commit !== true && last.lane.seat?.group.final !== true || queued.length > 0 || inFlight !== null || ordinaryOpen > 0 || !ridesNow())
1790
+ return false;
1791
+ const counts = new Map;
1792
+ for (const entry of live) {
1793
+ if (entry.commit !== true && entry.lane.order?.followed !== true && entry.lane.seat?.group.final !== true)
1794
+ return false;
1795
+ counts.set(entry.lane, (counts.get(entry.lane) ?? 0) + 1);
1796
+ }
1797
+ for (const open of lanes) {
1798
+ const count = counts.get(open) ?? 0;
1799
+ if (count > 0 && open.waiting === count)
1800
+ continue;
1801
+ const group = open.group;
1802
+ if (count === 0 && group?.final === true && group.sealed && open.waiting === 1 && live.some((entry) => entry.lane.seat?.group === group))
1803
+ continue;
1804
+ if (count === 0 && open.final === true && open.waiting === 0)
1805
+ continue;
1806
+ return false;
1807
+ }
1808
+ return true;
1809
+ };
1810
+ const sendQueued = (entries, options) => {
1811
+ const inTurn = options?.inTurn === true;
1446
1812
  const refusal = sendRefusal();
1447
1813
  const live = [];
1448
1814
  for (const entry of entries) {
@@ -1455,11 +1821,19 @@ function pgAdapter(options) {
1455
1821
  }
1456
1822
  if (live.length === 0)
1457
1823
  return Promise.resolve();
1458
- const carriesOpening = !deferred.opened();
1459
- const control = deferred.take();
1460
- const openingSteps = carriesOpening ? opening.length : 0;
1461
- const burst = turns.run(() => runBatch({ client, steps: [...control.map((text) => ({ text })), ...live.map((entry) => ({ text: entry.text, values: entry.values }))], types: EXACT_TYPES })).then((outcome) => {
1462
- distributeBurst({ outcome, live, controlSteps: control.length, openingSteps });
1824
+ const carriesOpening = !inTurn && !deferred.opened();
1825
+ const rides = !inTurn && live.length === entries.length && ridesQueued(live);
1826
+ const control = inTurn ? [] : deferred.takeSteps();
1827
+ const openingSteps = carriesOpening ? openingLength : 0;
1828
+ const commitIndex = control.length + live.length;
1829
+ const sendBatch = () => runBatch({ client, steps: [...control, ...live.map((entry) => ({ text: entry.text, values: entry.values })), ...rides ? [COMMIT_STEP] : []], types: EXACT_TYPES });
1830
+ const batch = inTurn ? new Promise((resolve) => {
1831
+ resolve(sendBatch());
1832
+ }) : runTurn(sendBatch);
1833
+ if (rides)
1834
+ trackRidden(batch, commitIndex);
1835
+ const burst = batch.then((received) => {
1836
+ distributeBurst({ outcome: rides ? beforeCommit({ outcome: received, commitIndex }) : received, live, controlSteps: control.length, openingSteps });
1463
1837
  }).then(undefined, (error) => {
1464
1838
  if (openingSteps > 0)
1465
1839
  openingFailure ??= mapPgDriverError({ error });
@@ -1475,6 +1849,81 @@ function pgAdapter(options) {
1475
1849
  }
1476
1850
  return burst;
1477
1851
  };
1852
+ const gathers = (dispatchParams) => dispatchParams.commit !== true && coalescable(dispatchParams);
1853
+ const closeGathering = () => {
1854
+ gathering = null;
1855
+ };
1856
+ const gatheredLane = { open: false, waiting: 0 };
1857
+ const joinGathering = (group, dispatchParams, values) => new Promise((resolve, reject) => {
1858
+ group.members.push({ params: dispatchParams, values, resolve, reject });
1859
+ });
1860
+ const sendGathered = (members, options) => sendQueued(members.map((member) => {
1861
+ gatheredLane.waiting += 1;
1862
+ return {
1863
+ text: member.params.config.text,
1864
+ values: member.values,
1865
+ ...member.params.execution === undefined || options?.inTurn === true ? {} : { execution: member.params.execution },
1866
+ lane: gatheredLane,
1867
+ resolve: member.resolve,
1868
+ reject: member.reject,
1869
+ settled: false
1870
+ };
1871
+ }), options);
1872
+ const gatherBehind = (dispatchParams, behind) => {
1873
+ const values = prepareBatchValues({ values: dispatchParams.config.values ?? [] });
1874
+ const open = gathering;
1875
+ if (open !== null && open.phase === "opening" && open.behind === behind)
1876
+ return joinGathering(open, dispatchParams, values);
1877
+ const group = { phase: "opening", behind, members: [] };
1878
+ gathering = group;
1879
+ gatheringsPending += 1;
1880
+ const joined = joinGathering(group, dispatchParams, values);
1881
+ behind.then(() => {
1882
+ if (gathering === group)
1883
+ gathering = null;
1884
+ gatheringsPending -= 1;
1885
+ const [only, ...more] = group.members;
1886
+ if (only !== undefined && more.length === 0) {
1887
+ send(only.params).then(only.resolve, only.reject);
1888
+ return;
1889
+ }
1890
+ sendGathered(group.members);
1891
+ });
1892
+ return joined;
1893
+ };
1894
+ const gatherInTurn = (dispatchParams) => {
1895
+ assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1896
+ const values = prepareBatchValues({ values: dispatchParams.config.values ?? [] });
1897
+ const open = gathering;
1898
+ if (open !== null && open.phase === "turn")
1899
+ return joinGathering(open, dispatchParams, values);
1900
+ const group = { phase: "turn", members: [] };
1901
+ gatheringsPending += 1;
1902
+ const joined = joinGathering(group, dispatchParams, values);
1903
+ turns.run(() => sendTurnGroup(group));
1904
+ gathering = group;
1905
+ return joined;
1906
+ };
1907
+ const sendTurnGroup = (group) => {
1908
+ if (gathering === group)
1909
+ gathering = null;
1910
+ gatheringsPending -= 1;
1911
+ const [only, ...more] = group.members;
1912
+ if (only !== undefined && more.length === 0) {
1913
+ const query = new Promise((resolve) => {
1914
+ resolve(client.query(only.params.config));
1915
+ });
1916
+ query.then(only.resolve, only.reject);
1917
+ return query;
1918
+ }
1919
+ const prior = turns.lastFailure();
1920
+ if (prior !== undefined) {
1921
+ const mapped = mapPgDriverError({ error: prior.error });
1922
+ if (typeof mapped.meta.sqlstate === "string")
1923
+ markTransactionAborted({ scope, cause: mapped });
1924
+ }
1925
+ return sendGathered(group.members, { inTurn: true });
1926
+ };
1478
1927
  const sendAlone = (entry, alone) => {
1479
1928
  const refusal = sendRefusal();
1480
1929
  if (refusal !== undefined) {
@@ -1524,7 +1973,7 @@ function pgAdapter(options) {
1524
1973
  if (failed === undefined)
1525
1974
  live.push(entry);
1526
1975
  else
1527
- settleQueued(entry, { error: sendRefusal() ?? transactionAbortedError({ cause: failed.cause }) });
1976
+ settleQueued(entry, { error: sendRefusal() ?? transactionAbortedError2({ cause: failed.cause }) });
1528
1977
  }
1529
1978
  if (live.length === 0)
1530
1979
  return Promise.resolve();
@@ -1577,11 +2026,12 @@ function pgAdapter(options) {
1577
2026
  const flushQueue = () => {
1578
2027
  if (queued.length === 0)
1579
2028
  return;
2029
+ closeGathering();
1580
2030
  if (queued.some((entry) => entry.lane.order !== undefined)) {
1581
2031
  flushOrdered();
1582
2032
  return;
1583
2033
  }
1584
- const entries = queued;
2034
+ const entries = queued.some((entry) => entry.lane.seat !== undefined) ? seatOrder(queued) : queued;
1585
2035
  queued = [];
1586
2036
  if (inFlight === null)
1587
2037
  sendQueued(entries);
@@ -1598,6 +2048,26 @@ function pgAdapter(options) {
1598
2048
  return;
1599
2049
  flushQueue();
1600
2050
  };
2051
+ const seatOrder = (entries) => [...entries].sort((first, second) => (first.lane.seat?.index ?? -1) - (second.lane.seat?.index ?? -1));
2052
+ const endGroupIfOver = (opening) => {
2053
+ if (!opening.sealed || opening.live > 0 || opening.parent.group !== opening)
2054
+ return;
2055
+ opening.parent.group = undefined;
2056
+ opening.parent.waiting -= 1;
2057
+ };
2058
+ const closeLane = (lane) => {
2059
+ if (!lane.open)
2060
+ return;
2061
+ lane.open = false;
2062
+ lanes.delete(lane);
2063
+ if (lane.order !== undefined)
2064
+ refuseOrphaned();
2065
+ if (lane.seat !== undefined) {
2066
+ lane.seat.group.live -= 1;
2067
+ endGroupIfOver(lane.seat.group);
2068
+ }
2069
+ flushIfReady();
2070
+ };
1601
2071
  const pipelines = {
1602
2072
  open: (count) => Array.from({ length: count }, () => {
1603
2073
  const opened = { open: true, waiting: 0 };
@@ -1629,15 +2099,7 @@ function pgAdapter(options) {
1629
2099
  lanes.add(next);
1630
2100
  return next;
1631
2101
  },
1632
- close: (lane) => {
1633
- if (!lane.open)
1634
- return;
1635
- lane.open = false;
1636
- lanes.delete(lane);
1637
- if (lane.order !== undefined)
1638
- refuseOrphaned();
1639
- flushIfReady();
1640
- },
2102
+ close: closeLane,
1641
2103
  waitAs: ({ lane, wait }) => {
1642
2104
  if (!lane.open)
1643
2105
  return wait;
@@ -1664,7 +2126,43 @@ function pgAdapter(options) {
1664
2126
  lane.waiting -= 1;
1665
2127
  };
1666
2128
  },
1667
- flush: flushQueue
2129
+ flush: flushQueue,
2130
+ group: (lane, final) => {
2131
+ if (!lane.open || lane.order !== undefined || lane.seat !== undefined || lane.gate === true || lane.group !== undefined || lane.final === true)
2132
+ return;
2133
+ if (!capable || budget.totalMs !== null || cancellationQueue || sendRefusal() !== undefined)
2134
+ return;
2135
+ if ((client.connectionParameters?.query_timeout ?? 0) > 0)
2136
+ return;
2137
+ for (const other of lanes) {
2138
+ if (other !== lane && (other.final !== true || other.waiting > 0))
2139
+ return;
2140
+ }
2141
+ const gate = { open: true, waiting: 0, gate: true };
2142
+ const opening = { parent: lane, gate, opened: 0, live: 0, sealed: false, final };
2143
+ lanes.add(gate);
2144
+ lane.group = opening;
2145
+ lane.waiting += 1;
2146
+ return opening;
2147
+ },
2148
+ seat: (opening) => {
2149
+ if (opening.sealed)
2150
+ throw laneGroupMisuse({ message: "a sub-lane is opened only before the group is sealed" });
2151
+ if (!opening.parent.open)
2152
+ throw laneGroupMisuse({ message: "a sub-lane of a closed lane" });
2153
+ const seat = { open: true, waiting: 0, seat: { group: opening, index: opening.opened, issued: false } };
2154
+ opening.opened += 1;
2155
+ opening.live += 1;
2156
+ lanes.add(seat);
2157
+ return seat;
2158
+ },
2159
+ seal: (opening) => {
2160
+ if (opening.sealed)
2161
+ return;
2162
+ opening.sealed = true;
2163
+ endGroupIfOver(opening);
2164
+ closeLane(opening.gate);
2165
+ }
1668
2166
  };
1669
2167
  const endPipeline = () => {
1670
2168
  for (const open of lanes)
@@ -1674,9 +2172,11 @@ function pgAdapter(options) {
1674
2172
  };
1675
2173
  const enqueue = (dispatchParams, lane, alone) => {
1676
2174
  if (cancellationAttempted)
1677
- throw transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) });
2175
+ throw transactionAbortedError2({ cause: readCancelledError2({ stage: "running" }) });
1678
2176
  if (openingFailure !== undefined)
1679
- throw transactionAbortedError({ cause: openingFailure });
2177
+ throw transactionAbortedError2({ cause: openingFailure });
2178
+ if (ridden !== undefined)
2179
+ throw commitSentRefusal();
1680
2180
  assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1681
2181
  const values = alone === true ? [] : prepareBatchValues({ values: dispatchParams.config.values ?? [] });
1682
2182
  const pending = new Promise((resolve, reject) => {
@@ -1686,6 +2186,7 @@ function pgAdapter(options) {
1686
2186
  values,
1687
2187
  ...dispatchParams.execution === undefined ? {} : { execution: dispatchParams.execution },
1688
2188
  ...alone === true ? { alone: dispatchParams } : {},
2189
+ ...dispatchParams.commit === true ? { commit: true } : {},
1689
2190
  lane,
1690
2191
  resolve,
1691
2192
  reject,
@@ -1699,9 +2200,23 @@ function pgAdapter(options) {
1699
2200
  flushIfReady();
1700
2201
  return pending;
1701
2202
  };
1702
- const coalescable = (dispatchParams) => capable && budget.totalMs === null && !cancellationQueue && dispatchParams.batchable === true && dispatchParams.cleanup !== true && dispatchParams.stage === undefined && dispatchParams.sent === undefined && dispatchParams.execution?.cancellation !== "running";
2203
+ const coalescable = (dispatchParams) => capable && budget.totalMs === null && !cancellationQueue && !((client.connectionParameters?.query_timeout ?? 0) > 0) && dispatchParams.batchable === true && dispatchParams.cleanup !== true && dispatchParams.stage === undefined && dispatchParams.sent === undefined && dispatchParams.execution?.cancellation !== "running";
1703
2204
  const dispatch = (dispatchParams) => {
1704
2205
  const lane = dispatchParams.lane?.open === true ? dispatchParams.lane : undefined;
2206
+ if (lane !== undefined && dispatchParams.cleanup !== true) {
2207
+ if (lane.group !== undefined)
2208
+ return Promise.reject(laneGroupMisuse({ message: "a lane issues nothing while its group is open" }));
2209
+ const seat = lane.seat;
2210
+ if (seat !== undefined) {
2211
+ if (seat.issued)
2212
+ return Promise.reject(laneGroupMisuse({ message: "a sub-lane issues ONE statement" }));
2213
+ if (!coalescable(dispatchParams))
2214
+ return Promise.reject(laneGroupMisuse({ message: "a sub-lane statement must be able to join a batch" }));
2215
+ seat.issued = true;
2216
+ }
2217
+ if (dispatchParams.commit === true)
2218
+ lane.final = true;
2219
+ }
1705
2220
  const order = lane?.order;
1706
2221
  if (lane !== undefined && order !== undefined && dispatchParams.cleanup !== true) {
1707
2222
  return guarded(() => {
@@ -1728,16 +2243,32 @@ function pgAdapter(options) {
1728
2243
  if (dispatchParams.execution?.cancellation === "running" && !cancellationQueue) {
1729
2244
  flushQueue();
1730
2245
  cancellationQueue = true;
2246
+ closeGathering();
1731
2247
  tail = Promise.allSettled([...activeDispatches]);
1732
2248
  }
1733
2249
  if (budget.totalMs === null && !cancellationQueue) {
1734
- const pending = inFlight === null ? send(dispatchParams) : inFlight.then(() => send(dispatchParams));
2250
+ ordinaryOpen += 1;
2251
+ const behind = gathering?.phase === "opening" ? gathering.behind ?? null : inFlight;
2252
+ let pending;
2253
+ if (behind === null)
2254
+ pending = send(dispatchParams);
2255
+ else if (gathers(dispatchParams))
2256
+ pending = guarded(() => gatherBehind(dispatchParams, behind));
2257
+ else {
2258
+ closeGathering();
2259
+ pending = behind.then(() => send(dispatchParams));
2260
+ }
1735
2261
  activeDispatches.add(pending);
1736
- pending.then(() => activeDispatches.delete(pending), () => activeDispatches.delete(pending));
2262
+ const settle = () => {
2263
+ activeDispatches.delete(pending);
2264
+ ordinaryOpen -= 1;
2265
+ };
2266
+ pending.then(settle, settle);
1737
2267
  return pending;
1738
2268
  }
1739
2269
  const pending = tail.then(() => send(dispatchParams));
1740
2270
  tail = pending.then(ignore, ignore);
2271
+ closeGathering();
1741
2272
  return pending;
1742
2273
  };
1743
2274
  let discard = false;
@@ -1788,7 +2319,7 @@ function pgAdapter(options) {
1788
2319
  throw transactionSettingRefusedError({ provider: PROVIDER, settings, setting, cause });
1789
2320
  };
1790
2321
  try {
1791
- if (settingsStatement !== undefined)
2322
+ if (settingsStatement !== undefined && !settingsDeferred)
1792
2323
  await openWithSettings(settingsStatement);
1793
2324
  else if (!deferOpening)
1794
2325
  await runMapped(() => onClient.query({ text: openText }));
@@ -1801,17 +2332,30 @@ function pgAdapter(options) {
1801
2332
  deferred: capable ? deferred : null,
1802
2333
  cancellation,
1803
2334
  pipelines: capable ? pipelines : null,
1804
- ordered: capable && budget.totalMs === null && openingOptions.timeout === undefined && !((client.connectionParameters?.query_timeout ?? 0) > 0)
2335
+ connection: client,
2336
+ topLevel: true,
2337
+ ordered: capable && budget.totalMs === null && !((client.connectionParameters?.query_timeout ?? 0) > 0),
2338
+ commits
1805
2339
  }))
1806
2340
  });
1807
2341
  endPipeline();
2342
+ const riding = ridden;
2343
+ if (riding !== undefined) {
2344
+ await riding.settled;
2345
+ if (riding.state === "committed") {
2346
+ budget.close({ outcome: "committed" });
2347
+ return result;
2348
+ }
2349
+ throw riding.state === "skipped" ? transactionAbortedError2({ cause: scope.state.aborted?.cause }) : riding.error;
2350
+ }
1808
2351
  budget.assertUsable({ stage: "commit" });
1809
2352
  if (!deferred.opened()) {
1810
2353
  budget.close({ outcome: "committed" });
1811
2354
  return result;
1812
2355
  }
1813
2356
  if (openingFailure !== undefined)
1814
- throw transactionAbortedError({ cause: openingFailure });
2357
+ throw transactionAbortedError2({ cause: openingFailure });
2358
+ deferred.dropReleases();
1815
2359
  const commit = await runMapped(() => dispatch({
1816
2360
  config: { text: "COMMIT" },
1817
2361
  stage: "commit",
@@ -1835,12 +2379,20 @@ function pgAdapter(options) {
1835
2379
  });
1836
2380
  if (finalResult({ result: commit }).command === "ROLLBACK") {
1837
2381
  commitProgress = "answered";
1838
- throw transactionAbortedError({ cause: scope.state.aborted?.cause });
2382
+ throw transactionAbortedError2({ cause: scope.state.aborted?.cause });
1839
2383
  }
1840
2384
  budget.close({ outcome: "committed" });
1841
2385
  return result;
1842
- } catch (error) {
2386
+ } catch (caught) {
1843
2387
  endPipeline();
2388
+ const riding = ridden;
2389
+ if (riding !== undefined)
2390
+ await riding.settled;
2391
+ if (riding?.state === "committed") {
2392
+ budget.close({ outcome: "committed" });
2393
+ throw caught;
2394
+ }
2395
+ const error = riding?.state === "lost" ? riding.error : caught;
1844
2396
  if (isSavepointRollbackFailedError(error) || scope.state.poisoned !== null) {
1845
2397
  discard = true;
1846
2398
  budget.close({ outcome: "unknown" });
@@ -1873,10 +2425,57 @@ function pgAdapter(options) {
1873
2425
  }
1874
2426
  },
1875
2427
  async connect() {
1876
- const client = await runMapped(() => getPool().connect());
1877
- client.release();
2428
+ const opened = await Promise.allSettled(Array.from({ length: Math.max(1, poolMin ?? 0) }, () => runMapped(() => getPool().connect())));
2429
+ for (const outcome of opened) {
2430
+ if (outcome.status === "fulfilled")
2431
+ outcome.value.release();
2432
+ }
2433
+ const failed = opened.find((outcome) => outcome.status === "rejected");
2434
+ if (failed !== undefined)
2435
+ throw failed.reason;
1878
2436
  }
1879
2437
  };
2438
+ const sendConnectionGroup = async (entries) => {
2439
+ const rootPool = getPool();
2440
+ const client = rootViaPoolQuery ? undefined : await runMapped(() => rootPool.connect());
2441
+ if (client === undefined || !hasReadyForQuerySeam({ client }) || !supportsBatch({ client }) || (client.connectionParameters?.query_timeout ?? 0) > 0) {
2442
+ client?.release();
2443
+ const steps = [];
2444
+ for (const entry of entries) {
2445
+ if (steps.some((step) => step.status === "failed")) {
2446
+ steps.push({ status: "skipped" });
2447
+ continue;
2448
+ }
2449
+ steps.push(await root.execute(entry.call).then((rows) => ({ status: "ok", result: { rows, rowCount: rows.length } }), (error) => ({ status: "failed", error })));
2450
+ }
2451
+ return { steps };
2452
+ }
2453
+ const seam = observeReadyForQuery({ client });
2454
+ let reuse = false;
2455
+ const outcome = await runBatch({ client, steps: entries.map((entry) => entry.step), types: EXACT_TYPES }).then((settled) => settled, (error) => ({ steps: [], transportError: error }));
2456
+ const failure = firstBatchFailure({ outcome });
2457
+ if (failure === undefined)
2458
+ reuse = true;
2459
+ else if (outcome.transportError === undefined)
2460
+ reuse = await seam.reusableAfter({ error: mapPgDriverError({ error: failure.error }), named: false });
2461
+ seam.detach();
2462
+ client.release(!reuse);
2463
+ return outcome;
2464
+ };
2465
+ const connectionGroupBase = {
2466
+ dialect: DIALECT,
2467
+ provider: PROVIDER,
2468
+ wire: WIRE,
2469
+ budgets: BUDGETS,
2470
+ roundTrips: ROUND_TRIPS,
2471
+ formatArrayParam: (values) => new PgArrayParam(values)
2472
+ };
2473
+ const connectionGroupDriver = {
2474
+ base: connectionGroupBase,
2475
+ prepare: (call) => ({ text: call.text, values: prepareBatchValues({ values: serializeParams({ values: call.values }) }) }),
2476
+ send: (entries) => lifecycle.admit({ run: () => sendConnectionGroup(entries) }),
2477
+ mapError: (error) => mapPgDriverError({ error })
2478
+ };
1880
2479
  return {
1881
2480
  dialect: DIALECT,
1882
2481
  provider: PROVIDER,
@@ -1888,6 +2487,15 @@ function pgAdapter(options) {
1888
2487
  pipelining: PIPELINING,
1889
2488
  transactionOutcome: "reported",
1890
2489
  transactionSettings: "transaction-local",
2490
+ ...transactionsTakeOneRoundTrip({ address }) ? {
2491
+ oneRoundTripTransactions: true,
2492
+ connectionGroup: () => {
2493
+ const closed = lifecycle.refusal();
2494
+ if (closed !== undefined)
2495
+ throw closed;
2496
+ return rootViaPoolQuery ? undefined : openConnectionGroup({ driver: connectionGroupDriver });
2497
+ }
2498
+ } : {},
1891
2499
  withSession({ run }) {
1892
2500
  return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });
1893
2501
  },
@@ -1940,4 +2548,4 @@ export {
1940
2548
  validateTransactionDeadline2 as validateTransactionDeadline
1941
2549
  };
1942
2550
 
1943
- //# debugId=FB5284F22EED8D0A64756E2164756E21
2551
+ //# debugId=96B1BE63991D190764756E2164756E21