@vibeorm/adapter-pg 4.1.0 → 4.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.
package/dist/index.js CHANGED
@@ -16,8 +16,12 @@ import {
16
16
  transactionLifecycleRefusal,
17
17
  createSavepointScope,
18
18
  engineStatementBudgetMs,
19
+ findRefusedSetting,
19
20
  isSavepointRollbackFailedError,
20
21
  readOnlyRequested,
22
+ readTransactionSettings,
23
+ renderTransactionSettings,
24
+ transactionSettingRefusedError,
21
25
  runNestedSavepoint,
22
26
  runRootScope,
23
27
  scopeTurn,
@@ -182,6 +186,26 @@ function firstBatchFailure(params) {
182
186
  return;
183
187
  }
184
188
 
189
+ // packages/adapter-pg/src/client-turns.ts
190
+ function createClientTurns() {
191
+ let outstanding = 0;
192
+ let previous = Promise.resolve();
193
+ const release = () => {
194
+ outstanding -= 1;
195
+ };
196
+ return {
197
+ run: (send) => {
198
+ const running = outstanding === 0 ? new Promise((resolve) => {
199
+ resolve(send());
200
+ }) : previous.then(send);
201
+ outstanding += 1;
202
+ previous = running.then(release, release);
203
+ return running;
204
+ },
205
+ outstanding: () => outstanding
206
+ };
207
+ }
208
+
185
209
  // packages/adapter-pg/src/read-cancellation.ts
186
210
  import pg2 from "pg";
187
211
  import { VibeError as VibeError2 } from "@vibeorm/schema";
@@ -221,6 +245,11 @@ var PG_SQLSTATE_REASONS = Object.freeze({
221
245
  "40001": "serialization-failure",
222
246
  "25006": "read-only-transaction"
223
247
  });
248
+ var NAMED_STATEMENT_POOLER_HINT = "Hint: with preparedStatements: true every query must reach the backend that prepared its name. " + "Use a direct connection, session pooling, or PgBouncer 1.21+ with max_prepared_statements > 0; " + "behind a transaction-mode pooler without prepared-statement support, turn preparedStatements off.";
249
+ var PG_SQLSTATE_HINTS = Object.freeze({
250
+ "26000": NAMED_STATEMENT_POOLER_HINT,
251
+ "42P05": NAMED_STATEMENT_POOLER_HINT
252
+ });
224
253
  var SQLSTATE_PATTERN = /^(?=[A-Z]*\d)[0-9A-Z]{5}$/;
225
254
  function readStringField(params) {
226
255
  const value = params.source[params.key];
@@ -255,7 +284,9 @@ function mapPgDriverError(params) {
255
284
  const reason = sqlstate === undefined ? undefined : PG_SQLSTATE_REASONS[sqlstate];
256
285
  if (reason !== undefined)
257
286
  meta.reason = reason;
258
- const message = readStringField({ source, key: "message" }) ?? (typeof error === "string" ? error : "unknown postgres driver error");
287
+ const driverMessage = readStringField({ source, key: "message" }) ?? (typeof error === "string" ? error : "unknown postgres driver error");
288
+ const hint = sqlstate === undefined ? undefined : PG_SQLSTATE_HINTS[sqlstate];
289
+ const message = hint === undefined ? driverMessage : `${driverMessage}. ${hint}`;
259
290
  return new VibeError({ code, message, meta, cause: error });
260
291
  }
261
292
  async function runMapped(fn) {
@@ -515,8 +546,18 @@ function placePlanCacheMode(params) {
515
546
  url.searchParams.set("options", merged);
516
547
  return { connectionString: url.toString(), options: merged };
517
548
  }
549
+ function assertIdleTimeout(params) {
550
+ const { value } = params;
551
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 2147483647)
552
+ return value;
553
+ throw new VibeError3({
554
+ code: "VIBE_CONFIG",
555
+ message: "pgAdapter `idleTimeout` must be integer milliseconds from 0 through 2,147,483,647 (0 keeps idle connections open).",
556
+ meta: { adapter: "pg", reason: "invalid-idle-timeout", option: "idleTimeout" }
557
+ });
558
+ }
518
559
  function buildPoolConfig(params) {
519
- const { max, statementTimeout, connectionTimeout } = params;
560
+ const { max, statementTimeout, connectionTimeout, idleTimeout } = params;
520
561
  const placement = placePlanCacheMode({
521
562
  connectionString: params.connectionString,
522
563
  planCacheMode: params.planCacheMode ?? "auto"
@@ -526,21 +567,43 @@ function buildPoolConfig(params) {
526
567
  config.statement_timeout = statementTimeout;
527
568
  if (connectionTimeout !== undefined)
528
569
  config.connectionTimeoutMillis = connectionTimeout;
570
+ if (idleTimeout !== undefined)
571
+ config.idleTimeoutMillis = idleTimeout;
529
572
  if (placement.options !== undefined)
530
573
  config.options = placement.options;
531
574
  return config;
532
575
  }
533
576
 
534
577
  // packages/adapter-pg/src/statement-cache.ts
578
+ import { createHash } from "crypto";
535
579
  var DEFAULT_PROMOTE_AFTER = 2;
536
580
  var SIGHTINGS_PER_NAME = 4;
581
+ var NAME_PREFIX = "vibeorm_";
582
+ var BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
583
+ function statementNameFor(params) {
584
+ const digest = createHash("sha256").update(params.text, "utf8").digest();
585
+ let name = NAME_PREFIX;
586
+ let buffer = 0;
587
+ let bits = 0;
588
+ for (const byte of digest) {
589
+ buffer = buffer << 8 | byte;
590
+ bits += 8;
591
+ while (bits >= 5) {
592
+ bits -= 5;
593
+ name += BASE32_ALPHABET[buffer >>> bits & 31];
594
+ }
595
+ buffer &= (1 << bits) - 1;
596
+ }
597
+ if (bits > 0)
598
+ name += BASE32_ALPHABET[buffer << 5 - bits & 31];
599
+ return name;
600
+ }
537
601
  function createStatementNameCache(params) {
538
602
  const max = Math.max(0, params.max);
539
603
  const promoteAfter = Math.max(1, params.promoteAfter ?? DEFAULT_PROMOTE_AFTER);
540
604
  const sightingsMax = max * SIGHTINGS_PER_NAME;
541
605
  const names = new Map;
542
606
  const sightings = new Map;
543
- let counter = 0;
544
607
  return {
545
608
  getName(nameParams) {
546
609
  const { text } = nameParams;
@@ -562,7 +625,7 @@ function createStatementNameCache(params) {
562
625
  return;
563
626
  }
564
627
  sightings.delete(text);
565
- const name = `vibeorm_${counter++}`;
628
+ const name = statementNameFor({ text });
566
629
  names.set(text, name);
567
630
  return name;
568
631
  },
@@ -575,6 +638,86 @@ function createStatementNameCache(params) {
575
638
  };
576
639
  }
577
640
 
641
+ // packages/adapter-pg/src/timestamp-text.ts
642
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
643
+ var DIGIT_POSITIONS = [0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15, 17, 18];
644
+ function parsePlainTimestampText(params) {
645
+ const { text, zoned } = params;
646
+ const length = text.length;
647
+ if (length < 19 || text.charCodeAt(4) !== 45 || text.charCodeAt(7) !== 45 || text.charCodeAt(10) !== 32 || text.charCodeAt(13) !== 58 || text.charCodeAt(16) !== 58) {
648
+ return;
649
+ }
650
+ for (let position = 0;position < DIGIT_POSITIONS.length; position++) {
651
+ const code = text.charCodeAt(DIGIT_POSITIONS[position]);
652
+ if (code < 48 || code > 57)
653
+ return;
654
+ }
655
+ const year = (text.charCodeAt(0) - 48) * 1000 + (text.charCodeAt(1) - 48) * 100 + (text.charCodeAt(2) - 48) * 10 + (text.charCodeAt(3) - 48);
656
+ const month = (text.charCodeAt(5) - 48) * 10 + (text.charCodeAt(6) - 48);
657
+ const day = (text.charCodeAt(8) - 48) * 10 + (text.charCodeAt(9) - 48);
658
+ const hour = (text.charCodeAt(11) - 48) * 10 + (text.charCodeAt(12) - 48);
659
+ const minute = (text.charCodeAt(14) - 48) * 10 + (text.charCodeAt(15) - 48);
660
+ const second = (text.charCodeAt(17) - 48) * 10 + (text.charCodeAt(18) - 48);
661
+ if (year < 1000 || month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 59)
662
+ return;
663
+ if (day > 28) {
664
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
665
+ const days = month === 2 ? leap ? 29 : 28 : DAYS_IN_MONTH[month - 1];
666
+ if (day > days)
667
+ return;
668
+ }
669
+ let index = 19;
670
+ let milliseconds = 0;
671
+ if (index < length && text.charCodeAt(index) === 46) {
672
+ index++;
673
+ const start = index;
674
+ while (index < length) {
675
+ const code = text.charCodeAt(index);
676
+ if (code < 48 || code > 57)
677
+ break;
678
+ if (index - start < 3)
679
+ milliseconds = milliseconds * 10 + (code - 48);
680
+ index++;
681
+ }
682
+ const digits = index - start;
683
+ if (digits === 0 || digits > 6)
684
+ return;
685
+ if (digits === 1)
686
+ milliseconds *= 100;
687
+ else if (digits === 2)
688
+ milliseconds *= 10;
689
+ }
690
+ let offsetMinutes = 0;
691
+ if (zoned) {
692
+ if (index + 3 > length)
693
+ return;
694
+ const sign = text.charCodeAt(index);
695
+ if (sign !== 43 && sign !== 45)
696
+ return;
697
+ const hours1 = text.charCodeAt(index + 1) - 48;
698
+ const hours2 = text.charCodeAt(index + 2) - 48;
699
+ if (hours1 < 0 || hours1 > 9 || hours2 < 0 || hours2 > 9)
700
+ return;
701
+ offsetMinutes = (hours1 * 10 + hours2) * 60;
702
+ index += 3;
703
+ if (index < length) {
704
+ if (index + 3 !== length || text.charCodeAt(index) !== 58)
705
+ return;
706
+ const minutes1 = text.charCodeAt(index + 1) - 48;
707
+ const minutes2 = text.charCodeAt(index + 2) - 48;
708
+ if (minutes1 < 0 || minutes1 > 9 || minutes2 < 0 || minutes2 > 9)
709
+ return;
710
+ offsetMinutes += minutes1 * 10 + minutes2;
711
+ index = length;
712
+ }
713
+ if (sign === 45)
714
+ offsetMinutes = -offsetMinutes;
715
+ }
716
+ if (index !== length)
717
+ return;
718
+ return new Date(Date.UTC(year, month - 1, day, hour, minute, second, milliseconds) - offsetMinutes * 60000);
719
+ }
720
+
578
721
  // packages/adapter-pg/src/transaction-sql.ts
579
722
  import { VibeError as VibeError4 } from "@vibeorm/schema";
580
723
  import { refuseNestedTransactionOptions } from "@vibeorm/runtime/adapter-kit";
@@ -616,6 +759,12 @@ function classifyRawTransactionControl(params) {
616
759
  return "close";
617
760
  return null;
618
761
  }
762
+ var NOT_PROVABLY_ONE_STATEMENT = /[;$\u0000]|--|\/\*/;
763
+ var OWN_TURN_VERBS = /^\s*(?:begin|start|commit|end|rollback|abort|savepoint|release|prepare|copy)\b/i;
764
+ function isSingleStatementText(params) {
765
+ const { text } = params;
766
+ return text.trim() !== "" && !NOT_PROVABLY_ONE_STATEMENT.test(text) && !OWN_TURN_VERBS.test(text);
767
+ }
619
768
 
620
769
  // packages/adapter-pg/src/index.ts
621
770
  import { createSavepointCounter, nextSavepointName } from "@vibeorm/runtime/adapter-kit";
@@ -633,6 +782,16 @@ var MAX_STATEMENT_TIMEOUT_MS = 2147483647;
633
782
  var ignore = () => {
634
783
  return;
635
784
  };
785
+ function predecessorClosedError() {
786
+ return new VibeError5({
787
+ code: "VIBE_TRANSACTION",
788
+ 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
+ meta: { reason: "pipeline-predecessor-closed" }
790
+ });
791
+ }
792
+ function orderedLaneMisuse(params) {
793
+ return new VibeError5({ code: "VIBE_INTERNAL", message: `pipeline(): ${params.message}`, meta: { reason: "pipeline-ordered-lane" } });
794
+ }
636
795
  function finalResult(params) {
637
796
  return Array.isArray(params.result) ? params.result.at(-1) ?? { rows: [], rowCount: 0 } : params.result;
638
797
  }
@@ -713,6 +872,8 @@ var DIALECT = "postgres";
713
872
  var PROVIDER = "postgresql";
714
873
  var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
715
874
  var ROUND_TRIPS = "network";
875
+ var READ_SNAPSHOT = Object.freeze({ isolationLevel: "RepeatableRead", accessMode: "readOnly" });
876
+ var PIPELINING = "batched";
716
877
  var BUDGETS = Object.freeze({
717
878
  acquireTimeout: true,
718
879
  statementTimeout: "all-statements",
@@ -725,9 +886,14 @@ var TIMESTAMP_OID = 1114;
725
886
  var TIMESTAMP_ARRAY_OID = 1115;
726
887
  var DATE_OID = 1082;
727
888
  var DATE_ARRAY_OID = 1182;
889
+ var TIMESTAMPTZ_OID = 1184;
890
+ var TIMESTAMPTZ_ARRAY_OID = 1185;
728
891
  var driverTypeParser = pg3.types.getTypeParser;
729
892
  var ZONE_LESS_TEXT = /^\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?)?$/;
730
893
  function parseZoneLessAsUtc(value) {
894
+ const plain = parsePlainTimestampText({ text: value, zoned: false });
895
+ if (plain !== undefined)
896
+ return plain;
731
897
  if (ZONE_LESS_TEXT.test(value)) {
732
898
  const iso = value.includes(" ") ? `${value.replace(" ", "T").replace(/(\.\d{3})\d+$/, "$1")}Z` : value;
733
899
  const parsed = new Date(iso);
@@ -746,6 +912,28 @@ function parseZoneLessArrayAsUtc(value) {
746
912
  };
747
913
  return mapElements(driverTypeParser(TEXT_ARRAY_OID, "text")(value));
748
914
  }
915
+ var pgTypesArrayParser = (() => {
916
+ const candidate = pg3.types.arrayParser;
917
+ return typeof candidate?.create === "function" ? candidate : undefined;
918
+ })();
919
+ function recogniseBuiltinParser(params) {
920
+ const parser = driverTypeParser(params.oid, "text");
921
+ return parser.name === params.name && params.peers.every((peer) => driverTypeParser(peer, "text") === parser) ? parser : undefined;
922
+ }
923
+ var BUILTIN_TIMESTAMPTZ_PARSER = recogniseBuiltinParser({
924
+ oid: TIMESTAMPTZ_OID,
925
+ peers: [TIMESTAMP_OID, DATE_OID],
926
+ name: "parseDate"
927
+ });
928
+ var BUILTIN_TIMESTAMPTZ_ARRAY_PARSER = pgTypesArrayParser === undefined || BUILTIN_TIMESTAMPTZ_PARSER === undefined ? undefined : recogniseBuiltinParser({ oid: TIMESTAMPTZ_ARRAY_OID, peers: [TIMESTAMP_ARRAY_OID, DATE_ARRAY_OID], name: "parseDateArray" });
929
+ function parseTimestamptzText(value) {
930
+ return parsePlainTimestampText({ text: value, zoned: true }) ?? BUILTIN_TIMESTAMPTZ_PARSER(value);
931
+ }
932
+ function parseTimestamptzArrayText(value) {
933
+ if (!value)
934
+ return null;
935
+ return pgTypesArrayParser.create(value, (entry) => entry !== null ? parseTimestamptzText(entry) : entry).parse();
936
+ }
749
937
  var EXACT_TYPES = {
750
938
  getTypeParser(oid, format) {
751
939
  if (format === undefined || format === "text") {
@@ -755,6 +943,14 @@ var EXACT_TYPES = {
755
943
  return parseZoneLessAsUtc;
756
944
  if (oid === TIMESTAMP_ARRAY_OID || oid === DATE_ARRAY_OID)
757
945
  return parseZoneLessArrayAsUtc;
946
+ if (oid === TIMESTAMPTZ_OID) {
947
+ const driver = driverTypeParser(oid, "text");
948
+ return driver === BUILTIN_TIMESTAMPTZ_PARSER ? parseTimestamptzText : driver;
949
+ }
950
+ if (oid === TIMESTAMPTZ_ARRAY_OID) {
951
+ const driver = driverTypeParser(oid, "text");
952
+ return driver === BUILTIN_TIMESTAMPTZ_ARRAY_PARSER ? parseTimestamptzArrayText : driver;
953
+ }
758
954
  }
759
955
  return driverTypeParser(oid, format);
760
956
  }
@@ -779,6 +975,7 @@ function pgAdapter(options) {
779
975
  const usePrepared = options?.preparedStatements ?? false;
780
976
  const stmtCacheMax = options?.stmtCacheMax ?? 1000;
781
977
  const planCacheMode = options?.planCacheMode === undefined ? "auto" : assertPlanCacheMode({ value: options.planCacheMode });
978
+ const idleTimeout = "pool" in address || options?.idleTimeout === undefined ? undefined : assertIdleTimeout({ value: options.idleTimeout });
782
979
  if (planCacheMode !== "auto" && "pool" in address) {
783
980
  throw new VibeError5({
784
981
  code: "VIBE_CONFIG",
@@ -804,6 +1001,7 @@ function pgAdapter(options) {
804
1001
  max: options?.max ?? 10,
805
1002
  ...options?.statementTimeout === undefined ? {} : { statementTimeout: options.statementTimeout },
806
1003
  ...options?.connectionTimeout === undefined ? {} : { connectionTimeout: options.connectionTimeout },
1004
+ ...idleTimeout === undefined ? {} : { idleTimeout },
807
1005
  planCacheMode
808
1006
  });
809
1007
  const PoolCtor = pg3.Pool;
@@ -850,8 +1048,12 @@ function pgAdapter(options) {
850
1048
  return { rows: final.rows, affectedRows: final.rowCount ?? 0 };
851
1049
  }
852
1050
  function createClientAdapter(params) {
853
- const { dispatch, scope, budget, deferred, cancellation } = params;
854
- const rawClient = { query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 }) };
1051
+ const { dispatch, scope, budget, deferred, cancellation, pipelines, lane } = params;
1052
+ const laneOf = lane === undefined ? {} : { lane };
1053
+ const waitAsLane = (wait) => lane === undefined || pipelines === null ? wait : pipelines.waitAs({ lane, wait });
1054
+ const rawClient = {
1055
+ query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 || isSingleStatementText({ text: config.text }), ...laneOf })
1056
+ };
855
1057
  const savepointControl = (statement, cleanup) => {
856
1058
  if (deferred !== null && deferred.routeSavepoint({ text: statement }) !== "send")
857
1059
  return;
@@ -859,7 +1061,8 @@ function pgAdapter(options) {
859
1061
  config: { text: statement },
860
1062
  stage: "nested",
861
1063
  batchable: true,
862
- ...cleanup ? { cleanup: true } : {}
1064
+ ...cleanup ? { cleanup: true } : {},
1065
+ ...laneOf
863
1066
  }));
864
1067
  };
865
1068
  return {
@@ -869,6 +1072,37 @@ function pgAdapter(options) {
869
1072
  budgets: BUDGETS,
870
1073
  readCancellation: cancellation.support,
871
1074
  roundTrips: ROUND_TRIPS,
1075
+ readSnapshot: READ_SNAPSHOT,
1076
+ pipelining: pipelines === null ? "ordered" : PIPELINING,
1077
+ ...pipelines === null ? {} : {
1078
+ ...params.ordered === true ? { orderedPipeline: true } : {},
1079
+ pipeline: ({ lanes: count, ordered }) => {
1080
+ budget.assertUsable({ stage: "statement" });
1081
+ 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 } });
1083
+ }
1084
+ if (ordered === true && params.ordered !== true) {
1085
+ throw new VibeError5({
1086
+ 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)",
1088
+ meta: { ordered: true, reason: "ordered-pipeline-undeclared" }
1089
+ });
1090
+ }
1091
+ const laneHandle = (opened) => ({
1092
+ adapter: createClientAdapter({ dispatch, scope, budget, deferred, cancellation, pipelines, lane: opened }),
1093
+ close: () => {
1094
+ pipelines.close(opened);
1095
+ },
1096
+ ...opened.order === undefined ? {} : {
1097
+ follow: () => {
1098
+ budget.assertUsable({ stage: "statement" });
1099
+ return laneHandle(pipelines.follow(opened));
1100
+ }
1101
+ }
1102
+ });
1103
+ return (ordered === true ? pipelines.openOrdered(count) : pipelines.open(count)).map(laneHandle);
1104
+ }
1105
+ },
872
1106
  rowChanges: () => {
873
1107
  budget.assertUsable({ stage: "statement" });
874
1108
  return readRowChanges({ scope });
@@ -877,29 +1111,35 @@ function pgAdapter(options) {
877
1111
  return runReadExecution({ ...execParams, support: cancellation.support, provider: PROVIDER, run: async () => {
878
1112
  const turn = scopeTurn({ scope });
879
1113
  if (turn !== undefined)
880
- await turn;
1114
+ await waitAsLane(turn);
881
1115
  budget.assertUsable({ stage: "statement" });
882
1116
  assertReadExecution2({ ...execParams, support: cancellation.support, provider: PROVIDER, stage: "waiting-for-connection" });
883
- const queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== "running", execution: execParams.execution, onReadControl: execParams.onReadControl }) };
1117
+ const queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== "running", execution: execParams.execution, onReadControl: execParams.onReadControl, ...laneOf }) };
884
1118
  return runExecute({ queryable, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
885
1119
  } });
886
1120
  },
887
1121
  async executeUnsafe(execParams) {
888
1122
  const turn = scopeTurn({ scope });
889
1123
  if (turn !== undefined)
890
- await turn;
1124
+ await waitAsLane(turn);
891
1125
  budget.assertUsable({ stage: "statement" });
892
1126
  return runExecuteUnsafe({ queryable: rawClient, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
893
1127
  },
894
1128
  async transaction(fn, nestedOptions) {
895
1129
  refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
896
1130
  budget.assertUsable({ stage: "nested" });
1131
+ pipelines?.flush();
1132
+ const leave = lane === undefined || pipelines === null ? ignore : pipelines.enter(lane);
897
1133
  return runNestedSavepoint({
898
1134
  scope,
899
1135
  execute: (statement) => savepointControl(statement, false),
900
1136
  cleanup: (statement) => savepointControl(statement, true),
901
- body: (child) => fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation }))
902
- });
1137
+ body: (child) => {
1138
+ leave();
1139
+ const ordered = lane === undefined && params.ordered === true ? { ordered: true } : {};
1140
+ return fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation, pipelines, ...laneOf, ...ordered }));
1141
+ }
1142
+ }).finally(leave);
903
1143
  },
904
1144
  async connect() {
905
1145
  budget.assertUsable({ stage: "statement" });
@@ -1034,7 +1274,8 @@ function pgAdapter(options) {
1034
1274
  validateTransactionDeadline({
1035
1275
  options: transactionOptions,
1036
1276
  support: BUDGETS.transactionDeadline,
1037
- provider: PROVIDER
1277
+ provider: PROVIDER,
1278
+ settings: "transaction-local"
1038
1279
  });
1039
1280
  const openingOptions = {
1040
1281
  isolationLevel: transactionOptions?.isolationLevel,
@@ -1042,7 +1283,19 @@ function pgAdapter(options) {
1042
1283
  timeout: transactionOptions?.timeout
1043
1284
  };
1044
1285
  const openText = openTransactionStatement(openingOptions);
1286
+ const settings = readTransactionSettings({ settings: transactionOptions?.settings, provider: PROVIDER });
1287
+ const settingsStatement = settings.length === 0 ? undefined : renderTransactionSettings({ settings });
1045
1288
  const client = await runMapped(() => getPool().connect());
1289
+ const turns = createClientTurns();
1290
+ const onClient = {
1291
+ query: (config) => turns.run(() => client.query(config)),
1292
+ get pipeline() {
1293
+ return client.pipeline;
1294
+ },
1295
+ get connectionParameters() {
1296
+ return client.connectionParameters;
1297
+ }
1298
+ };
1046
1299
  let connectionFailure;
1047
1300
  const unguard = guardBackgroundErrors({
1048
1301
  client,
@@ -1054,7 +1307,7 @@ function pgAdapter(options) {
1054
1307
  const scope = createSavepointScope();
1055
1308
  const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });
1056
1309
  const capable = supportsBatch({ client });
1057
- const deferOpening = capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);
1310
+ const deferOpening = settingsStatement === undefined && capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);
1058
1311
  const opening = deferOpening ? [beginStatement(openingOptions), ...openingOptions.timeout === undefined ? [] : [statementTimeoutStatement({ timeout: openingOptions.timeout })]] : [];
1059
1312
  const deferred = createDeferredControl({ opening });
1060
1313
  let openingFailure;
@@ -1064,7 +1317,7 @@ function pgAdapter(options) {
1064
1317
  const handOver = () => {
1065
1318
  assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1066
1319
  dispatchParams.sent?.();
1067
- return cancellation.run({ config, execution: dispatchParams.execution, onReadControl: dispatchParams.onReadControl, send: () => client.query(config) });
1320
+ return cancellation.run({ config, execution: dispatchParams.execution, onReadControl: dispatchParams.onReadControl, send: () => onClient.query(config) });
1068
1321
  };
1069
1322
  if (cleanup !== true && cancellationAttempted)
1070
1323
  throw transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) });
@@ -1083,7 +1336,7 @@ function pgAdapter(options) {
1083
1336
  if (bound === undefined)
1084
1337
  return handOver();
1085
1338
  const stage = dispatchParams.stage ?? "statement";
1086
- return runMapped(() => client.query({ text: bound })).then(() => {
1339
+ return runMapped(() => onClient.query({ text: bound })).then(() => {
1087
1340
  budget.assertUsable({ stage });
1088
1341
  return handOver();
1089
1342
  });
@@ -1100,7 +1353,7 @@ function pgAdapter(options) {
1100
1353
  assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1101
1354
  if (statement.length > 0)
1102
1355
  dispatchParams.sent?.();
1103
- const burst = runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES }).then((outcome) => {
1356
+ const burst = turns.run(() => runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES })).then((outcome) => {
1104
1357
  const failure = firstBatchFailure({ outcome });
1105
1358
  if (failure !== undefined) {
1106
1359
  if (failure.index < openingSteps || outcome.transportError !== undefined && openingSteps > 0) {
@@ -1122,18 +1375,358 @@ function pgAdapter(options) {
1122
1375
  }
1123
1376
  return burst;
1124
1377
  };
1125
- const send = (dispatchParams) => {
1378
+ const guarded = (run) => {
1126
1379
  try {
1127
- return sendWithControl(dispatchParams);
1380
+ return run();
1128
1381
  } catch (error) {
1129
1382
  return Promise.reject(error);
1130
1383
  }
1131
1384
  };
1385
+ const send = (dispatchParams) => guarded(() => sendWithControl(dispatchParams));
1132
1386
  let tail = Promise.resolve();
1133
1387
  const activeDispatches = new Set;
1134
1388
  let cancellationQueue = false;
1389
+ const lanes = new Set;
1390
+ let queued = [];
1391
+ const settleQueued = (entry, outcome) => {
1392
+ if (entry.settled)
1393
+ return;
1394
+ entry.settled = true;
1395
+ entry.lane.waiting -= 1;
1396
+ if ("result" in outcome) {
1397
+ entry.resolve(outcome.result);
1398
+ } else {
1399
+ entry.failure = outcome.error;
1400
+ entry.reject(outcome.error);
1401
+ }
1402
+ };
1403
+ const distributeBurst = (burst) => {
1404
+ const { outcome, live, controlSteps, openingSteps } = burst;
1405
+ const failure = firstBatchFailure({ outcome });
1406
+ if (failure === undefined) {
1407
+ live.forEach((entry, index) => {
1408
+ const step = outcome.steps[controlSteps + index];
1409
+ settleQueued(entry, { result: step?.status === "ok" ? step.result : { rows: [], rowCount: 0 } });
1410
+ });
1411
+ return;
1412
+ }
1413
+ const mapped = mapPgDriverError({ error: failure.error });
1414
+ if (failure.index < openingSteps || outcome.transportError !== undefined && openingSteps > 0)
1415
+ openingFailure = mapped;
1416
+ if (typeof mapped.meta.sqlstate === "string")
1417
+ markTransactionAborted({ scope, cause: mapped });
1418
+ const answered = outcome.transportError === undefined;
1419
+ if (failure.index < controlSteps) {
1420
+ const later = answered || openingFailure !== undefined ? transactionAbortedError({ cause: openingFailure ?? mapped }) : mapped;
1421
+ live.forEach((entry, index) => {
1422
+ settleQueued(entry, { error: index === 0 ? mapped : later });
1423
+ });
1424
+ return;
1425
+ }
1426
+ let abortCause = mapped;
1427
+ live.forEach((entry, index) => {
1428
+ const position = controlSteps + index;
1429
+ const step = outcome.steps[position];
1430
+ if (step === undefined) {
1431
+ settleQueued(entry, { error: position === failure.index || openingFailure === undefined ? mapped : transactionAbortedError({ cause: openingFailure }) });
1432
+ } else if (step.status === "ok") {
1433
+ settleQueued(entry, { result: step.result });
1434
+ } else if (step.status === "failed") {
1435
+ const error = position === failure.index ? mapped : mapPgDriverError({ error: step.error });
1436
+ if (typeof error.meta.sqlstate === "string")
1437
+ abortCause = error;
1438
+ settleQueued(entry, { error });
1439
+ } else {
1440
+ settleQueued(entry, { error: transactionAbortedError({ cause: abortCause }) });
1441
+ }
1442
+ });
1443
+ };
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) => {
1446
+ const refusal = sendRefusal();
1447
+ const live = [];
1448
+ for (const entry of entries) {
1449
+ if (refusal !== undefined)
1450
+ settleQueued(entry, { error: refusal });
1451
+ else if (entry.execution?.signal?.aborted === true)
1452
+ settleQueued(entry, { error: readCancelledError2({ stage: "waiting-for-connection" }) });
1453
+ else
1454
+ live.push(entry);
1455
+ }
1456
+ if (live.length === 0)
1457
+ 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 });
1463
+ }).then(undefined, (error) => {
1464
+ if (openingSteps > 0)
1465
+ openingFailure ??= mapPgDriverError({ error });
1466
+ for (const entry of live)
1467
+ settleQueued(entry, { error });
1468
+ });
1469
+ if (carriesOpening) {
1470
+ const settled = burst.then(() => {
1471
+ if (inFlight === settled)
1472
+ inFlight = null;
1473
+ });
1474
+ inFlight = settled;
1475
+ }
1476
+ return burst;
1477
+ };
1478
+ const sendAlone = (entry, alone) => {
1479
+ const refusal = sendRefusal();
1480
+ if (refusal !== undefined) {
1481
+ settleQueued(entry, { error: refusal });
1482
+ return Promise.resolve();
1483
+ }
1484
+ return dispatchOrdinary(alone).then((result) => {
1485
+ settleQueued(entry, { result });
1486
+ }, (error) => {
1487
+ const mapped = mapPgDriverError({ error });
1488
+ if (typeof mapped.meta.sqlstate === "string")
1489
+ markTransactionAborted({ scope, cause: mapped });
1490
+ settleQueued(entry, { error });
1491
+ });
1492
+ };
1493
+ const prefixOf = (lane) => {
1494
+ const order = lane.order;
1495
+ if (order === undefined)
1496
+ return "issued";
1497
+ let state = "issued";
1498
+ for (let index = 0;index < order.index; index++) {
1499
+ const earlier = order.scope.lanes[index];
1500
+ if (earlier.order.issued > 0)
1501
+ continue;
1502
+ if (!earlier.open)
1503
+ return "closed";
1504
+ state = "open";
1505
+ }
1506
+ return state;
1507
+ };
1508
+ const refuseOrphaned = () => {
1509
+ if (!queued.some((entry) => prefixOf(entry.lane) === "closed"))
1510
+ return;
1511
+ const kept = [];
1512
+ for (const entry of queued) {
1513
+ if (prefixOf(entry.lane) === "closed")
1514
+ settleQueued(entry, { error: predecessorClosedError() });
1515
+ else
1516
+ kept.push(entry);
1517
+ }
1518
+ queued = kept;
1519
+ };
1520
+ const orderedSend = (entries, send) => () => {
1521
+ const live = [];
1522
+ for (const entry of entries) {
1523
+ const failed = entry.lane.order?.scope.failed;
1524
+ if (failed === undefined)
1525
+ live.push(entry);
1526
+ else
1527
+ settleQueued(entry, { error: sendRefusal() ?? transactionAbortedError({ cause: failed.cause }) });
1528
+ }
1529
+ if (live.length === 0)
1530
+ return Promise.resolve();
1531
+ return send(live).then(() => {
1532
+ for (const entry of live) {
1533
+ const order = entry.lane.order;
1534
+ if (order !== undefined && entry.failure !== undefined)
1535
+ order.scope.failed ??= { cause: mapPgDriverError({ error: entry.failure }) };
1536
+ }
1537
+ });
1538
+ };
1539
+ let orderedTail = null;
1540
+ const flushOrdered = () => {
1541
+ refuseOrphaned();
1542
+ const ready = [];
1543
+ const held = [];
1544
+ for (const entry of queued)
1545
+ (prefixOf(entry.lane) === "issued" ? ready : held).push(entry);
1546
+ queued = held;
1547
+ if (ready.length === 0)
1548
+ return;
1549
+ ready.sort((first, second) => (first.lane.order?.index ?? 0) - (second.lane.order?.index ?? 0));
1550
+ const sends = [];
1551
+ let run = [];
1552
+ for (const entry of ready) {
1553
+ const alone = entry.alone;
1554
+ if (alone === undefined) {
1555
+ run.push(entry);
1556
+ continue;
1557
+ }
1558
+ if (run.length > 0) {
1559
+ sends.push(orderedSend(run, sendQueued));
1560
+ run = [];
1561
+ }
1562
+ sends.push(orderedSend([entry], () => sendAlone(entry, alone)));
1563
+ }
1564
+ if (run.length > 0)
1565
+ sends.push(orderedSend(run, sendQueued));
1566
+ const before = [orderedTail, inFlight].filter((wait) => wait !== null);
1567
+ let chain = before.length === 0 ? sends.shift()() : Promise.all(before).then(ignore);
1568
+ for (const next of sends)
1569
+ chain = chain.then(next);
1570
+ const tail = chain;
1571
+ orderedTail = tail;
1572
+ tail.then(() => {
1573
+ if (orderedTail === tail)
1574
+ orderedTail = null;
1575
+ });
1576
+ };
1577
+ const flushQueue = () => {
1578
+ if (queued.length === 0)
1579
+ return;
1580
+ if (queued.some((entry) => entry.lane.order !== undefined)) {
1581
+ flushOrdered();
1582
+ return;
1583
+ }
1584
+ const entries = queued;
1585
+ queued = [];
1586
+ if (inFlight === null)
1587
+ sendQueued(entries);
1588
+ else
1589
+ inFlight.then(() => {
1590
+ sendQueued(entries);
1591
+ });
1592
+ };
1593
+ const flushIfReady = () => {
1594
+ if (queued.length === 0)
1595
+ return;
1596
+ for (const open of lanes)
1597
+ if (open.waiting === 0)
1598
+ return;
1599
+ flushQueue();
1600
+ };
1601
+ const pipelines = {
1602
+ open: (count) => Array.from({ length: count }, () => {
1603
+ const opened = { open: true, waiting: 0 };
1604
+ lanes.add(opened);
1605
+ return opened;
1606
+ }),
1607
+ openOrdered: (count) => {
1608
+ const ordered = { lanes: [] };
1609
+ for (let index = 0;index < count; index++) {
1610
+ const opened = { open: true, waiting: 0, order: { scope: ordered, index, issued: 0, followed: false } };
1611
+ ordered.lanes.push(opened);
1612
+ lanes.add(opened);
1613
+ }
1614
+ return [...ordered.lanes];
1615
+ },
1616
+ follow: (lane) => {
1617
+ const order = lane.order;
1618
+ if (order === undefined)
1619
+ throw orderedLaneMisuse({ message: "follow() exists only on the lanes of an ordered scope" });
1620
+ if (!lane.open)
1621
+ throw orderedLaneMisuse({ message: "follow() on a closed lane" });
1622
+ if (order.followed || order.scope.lanes.at(-1) !== lane)
1623
+ throw orderedLaneMisuse({ message: "follow() opens one lane after the LAST lane of its scope" });
1624
+ if (order.issued > 0)
1625
+ throw orderedLaneMisuse({ message: "follow() must come before the lane issues its statement" });
1626
+ order.followed = true;
1627
+ const next = { open: true, waiting: 0, order: { scope: order.scope, index: order.scope.lanes.length, issued: 0, followed: false } };
1628
+ order.scope.lanes.push(next);
1629
+ lanes.add(next);
1630
+ return next;
1631
+ },
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
+ },
1641
+ waitAs: ({ lane, wait }) => {
1642
+ if (!lane.open)
1643
+ return wait;
1644
+ lane.waiting += 1;
1645
+ flushIfReady();
1646
+ return wait.then((value) => {
1647
+ lane.waiting -= 1;
1648
+ return value;
1649
+ }, (error) => {
1650
+ lane.waiting -= 1;
1651
+ throw error;
1652
+ });
1653
+ },
1654
+ enter: (lane) => {
1655
+ if (!lane.open)
1656
+ return ignore;
1657
+ lane.waiting += 1;
1658
+ flushIfReady();
1659
+ let left = false;
1660
+ return () => {
1661
+ if (left)
1662
+ return;
1663
+ left = true;
1664
+ lane.waiting -= 1;
1665
+ };
1666
+ },
1667
+ flush: flushQueue
1668
+ };
1669
+ const endPipeline = () => {
1670
+ for (const open of lanes)
1671
+ open.open = false;
1672
+ lanes.clear();
1673
+ flushQueue();
1674
+ };
1675
+ const enqueue = (dispatchParams, lane, alone) => {
1676
+ if (cancellationAttempted)
1677
+ throw transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) });
1678
+ if (openingFailure !== undefined)
1679
+ throw transactionAbortedError({ cause: openingFailure });
1680
+ assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
1681
+ const values = alone === true ? [] : prepareBatchValues({ values: dispatchParams.config.values ?? [] });
1682
+ const pending = new Promise((resolve, reject) => {
1683
+ lane.waiting += 1;
1684
+ queued.push({
1685
+ text: dispatchParams.config.text,
1686
+ values,
1687
+ ...dispatchParams.execution === undefined ? {} : { execution: dispatchParams.execution },
1688
+ ...alone === true ? { alone: dispatchParams } : {},
1689
+ lane,
1690
+ resolve,
1691
+ reject,
1692
+ settled: false
1693
+ });
1694
+ });
1695
+ if (lane.order !== undefined)
1696
+ lane.order.issued += 1;
1697
+ activeDispatches.add(pending);
1698
+ pending.then(() => activeDispatches.delete(pending), () => activeDispatches.delete(pending));
1699
+ flushIfReady();
1700
+ return pending;
1701
+ };
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";
1135
1703
  const dispatch = (dispatchParams) => {
1704
+ const lane = dispatchParams.lane?.open === true ? dispatchParams.lane : undefined;
1705
+ const order = lane?.order;
1706
+ if (lane !== undefined && order !== undefined && dispatchParams.cleanup !== true) {
1707
+ return guarded(() => {
1708
+ if (order.followed && order.issued > 0)
1709
+ throw orderedLaneMisuse({ message: "a lane that has a follower may issue ONE statement" });
1710
+ return coalescable(dispatchParams) ? enqueue(dispatchParams, lane) : enqueue(dispatchParams, lane, true);
1711
+ });
1712
+ }
1713
+ if (lane !== undefined && coalescable(dispatchParams))
1714
+ return guarded(() => enqueue(dispatchParams, lane));
1715
+ if (lane === undefined)
1716
+ return dispatchOrdinary(dispatchParams);
1717
+ const leave = pipelines.enter(lane);
1718
+ flushQueue();
1719
+ return dispatchOrdinary(dispatchParams).then((result) => {
1720
+ leave();
1721
+ return result;
1722
+ }, (error) => {
1723
+ leave();
1724
+ throw error;
1725
+ });
1726
+ };
1727
+ const dispatchOrdinary = (dispatchParams) => {
1136
1728
  if (dispatchParams.execution?.cancellation === "running" && !cancellationQueue) {
1729
+ flushQueue();
1137
1730
  cancellationQueue = true;
1138
1731
  tail = Promise.allSettled([...activeDispatches]);
1139
1732
  }
@@ -1150,7 +1743,7 @@ function pgAdapter(options) {
1150
1743
  let discard = false;
1151
1744
  let released = false;
1152
1745
  let cancellationAttempted = false;
1153
- const cancellation = createPgReadCancellation({ connectionString: cancellationAddress, target: client, terminate: () => {
1746
+ const cancellation = createPgReadCancellation({ connectionString: cancellationAddress, target: onClient, terminate: () => {
1154
1747
  if (!released) {
1155
1748
  released = true;
1156
1749
  client.release(true);
@@ -1161,13 +1754,57 @@ function pgAdapter(options) {
1161
1754
  markTransactionAborted({ scope, cause: readCancelledError2({ stage: "running" }) });
1162
1755
  } });
1163
1756
  let commitProgress = "not-sent";
1757
+ let openingRollback;
1758
+ const openWithSettings = async (statement) => {
1759
+ let failed;
1760
+ if (capable) {
1761
+ const control = [beginStatement(openingOptions), ...openingOptions.timeout === undefined ? [] : [statementTimeoutStatement({ timeout: openingOptions.timeout })]];
1762
+ const outcome = await turns.run(() => runBatch({
1763
+ client,
1764
+ steps: [...control.map((text) => ({ text })), { text: statement.text, values: prepareBatchValues({ values: statement.values }) }],
1765
+ types: EXACT_TYPES
1766
+ }));
1767
+ const failure = firstBatchFailure({ outcome });
1768
+ if (failure === undefined)
1769
+ return;
1770
+ if (failure.index < control.length || outcome.transportError !== undefined)
1771
+ throw mapPgDriverError({ error: failure.error });
1772
+ failed = { error: failure.error };
1773
+ } else {
1774
+ await runMapped(() => onClient.query({ text: openText }));
1775
+ failed = await onClient.query({ text: statement.text, values: [...statement.values] }).then(() => {
1776
+ return;
1777
+ }, (error) => ({ error }));
1778
+ if (failed === undefined)
1779
+ return;
1780
+ }
1781
+ const cause = mapPgDriverError({ error: failed.error });
1782
+ if (typeof cause.meta.sqlstate !== "string")
1783
+ throw cause;
1784
+ openingRollback = await onClient.query({ text: "ROLLBACK" }).then(() => "acknowledged", () => "failed");
1785
+ if (openingRollback === "failed")
1786
+ discard = true;
1787
+ const setting = findRefusedSetting({ settings, cause });
1788
+ throw transactionSettingRefusedError({ provider: PROVIDER, settings, setting, cause });
1789
+ };
1164
1790
  try {
1165
- if (!deferOpening)
1166
- await runMapped(() => client.query({ text: openText }));
1791
+ if (settingsStatement !== undefined)
1792
+ await openWithSettings(settingsStatement);
1793
+ else if (!deferOpening)
1794
+ await runMapped(() => onClient.query({ text: openText }));
1167
1795
  const result = await runRootScope({
1168
1796
  scope,
1169
- body: () => fn(createClientAdapter({ dispatch, scope, budget, deferred: capable ? deferred : null, cancellation }))
1797
+ body: () => fn(createClientAdapter({
1798
+ dispatch,
1799
+ scope,
1800
+ budget,
1801
+ deferred: capable ? deferred : null,
1802
+ cancellation,
1803
+ pipelines: capable ? pipelines : null,
1804
+ ordered: capable && budget.totalMs === null && openingOptions.timeout === undefined && !((client.connectionParameters?.query_timeout ?? 0) > 0)
1805
+ }))
1170
1806
  });
1807
+ endPipeline();
1171
1808
  budget.assertUsable({ stage: "commit" });
1172
1809
  if (!deferred.opened()) {
1173
1810
  budget.close({ outcome: "committed" });
@@ -1203,13 +1840,16 @@ function pgAdapter(options) {
1203
1840
  budget.close({ outcome: "committed" });
1204
1841
  return result;
1205
1842
  } catch (error) {
1843
+ endPipeline();
1206
1844
  if (isSavepointRollbackFailedError(error) || scope.state.poisoned !== null) {
1207
1845
  discard = true;
1208
1846
  budget.close({ outcome: "unknown" });
1209
1847
  }
1210
1848
  let rollback = "not-needed";
1211
1849
  try {
1212
- if (deferred.opened()) {
1850
+ if (openingRollback !== undefined)
1851
+ rollback = openingRollback;
1852
+ else if (deferred.opened()) {
1213
1853
  await dispatch({ config: { text: "ROLLBACK" }, cleanup: true });
1214
1854
  rollback = "acknowledged";
1215
1855
  }
@@ -1244,7 +1884,10 @@ function pgAdapter(options) {
1244
1884
  budgets: BUDGETS,
1245
1885
  readCancellation,
1246
1886
  roundTrips: ROUND_TRIPS,
1887
+ readSnapshot: READ_SNAPSHOT,
1888
+ pipelining: PIPELINING,
1247
1889
  transactionOutcome: "reported",
1890
+ transactionSettings: "transaction-local",
1248
1891
  withSession({ run }) {
1249
1892
  return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });
1250
1893
  },
@@ -1297,4 +1940,4 @@ export {
1297
1940
  validateTransactionDeadline2 as validateTransactionDeadline
1298
1941
  };
1299
1942
 
1300
- //# debugId=3DBA8F0584DC025664756E2164756E21
1943
+ //# debugId=FB5284F22EED8D0A64756E2164756E21