@typed-sql/postgres 1.0.0 → 2.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +29 -4
  3. package/dist/packages/postgres/src/bulk.d.ts +30 -0
  4. package/dist/packages/postgres/src/bulk.d.ts.map +1 -0
  5. package/dist/packages/postgres/src/bulk.js +228 -0
  6. package/dist/packages/postgres/src/bulk.js.map +1 -0
  7. package/dist/packages/postgres/src/index.d.ts +5 -1
  8. package/dist/packages/postgres/src/index.d.ts.map +1 -1
  9. package/dist/packages/postgres/src/index.js +17 -4
  10. package/dist/packages/postgres/src/index.js.map +1 -1
  11. package/dist/packages/postgres/src/pg.d.ts +57 -2
  12. package/dist/packages/postgres/src/pg.d.ts.map +1 -1
  13. package/dist/packages/postgres/src/pg.js +346 -2
  14. package/dist/packages/postgres/src/pg.js.map +1 -1
  15. package/dist/packages/postgres/src/provider.d.ts +2 -1
  16. package/dist/packages/postgres/src/provider.d.ts.map +1 -1
  17. package/dist/packages/postgres/src/provider.js +3 -1
  18. package/dist/packages/postgres/src/provider.js.map +1 -1
  19. package/dist/packages/postgres/src/resolver.d.ts.map +1 -1
  20. package/dist/packages/postgres/src/resolver.js +11 -1
  21. package/dist/packages/postgres/src/resolver.js.map +1 -1
  22. package/dist/packages/postgres/src/routing.d.ts +22 -0
  23. package/dist/packages/postgres/src/routing.d.ts.map +1 -0
  24. package/dist/packages/postgres/src/routing.js +70 -0
  25. package/dist/packages/postgres/src/routing.js.map +1 -0
  26. package/dist/packages/postgres/src/runtime.d.ts +14 -1
  27. package/dist/packages/postgres/src/runtime.d.ts.map +1 -1
  28. package/dist/packages/postgres/src/runtime.js +440 -28
  29. package/dist/packages/postgres/src/runtime.js.map +1 -1
  30. package/dist/packages/postgres/src/semantics.d.ts +5 -0
  31. package/dist/packages/postgres/src/semantics.d.ts.map +1 -0
  32. package/dist/packages/postgres/src/semantics.js +211 -0
  33. package/dist/packages/postgres/src/semantics.js.map +1 -0
  34. package/dist/packages/postgres/src/stream.d.ts +7 -7
  35. package/dist/packages/postgres/src/stream.d.ts.map +1 -1
  36. package/dist/packages/postgres/src/stream.js.map +1 -1
  37. package/dist/packages/postgres/src/version.d.ts +2 -0
  38. package/dist/packages/postgres/src/version.d.ts.map +1 -0
  39. package/dist/packages/postgres/src/version.js +2 -0
  40. package/dist/packages/postgres/src/version.js.map +1 -0
  41. package/package.json +4 -4
@@ -1,10 +1,28 @@
1
1
  var _a;
2
- import { renderQuery, } from "@typed-sql/core";
2
+ import { createHash } from "node:crypto";
3
+ import { adapterCapabilities, assertExecutionCapabilities, createAdapterCapabilityResolver, databaseErrorCompletion, hasQueryResultValidator, observeQueryStream, QueryCardinalityError, queryResultValidationSource, renderQuery, runControlledExecution, startDatabaseObservation, validateQueryResultRows, validateQueryResultStream, } from "@typed-sql/core";
4
+ import { createPostgresCopyCapability, postgresCopy } from "./bulk.js";
3
5
  import { createPostgresPreparedQueryState, preparePostgresQuery, } from "./prepared.js";
4
6
  import { PostgresQueryStream, validatePostgresStreamBatchSize } from "./stream.js";
5
7
  import { defaultPostgresTypePolicy } from "./type-policy.js";
8
+ import { POSTGRES_DIALECT_VERSION } from "./version.js";
6
9
  const defaultPolicy = defaultPostgresTypePolicy;
7
10
  const emptyBatchResults = Object.freeze([]);
11
+ function createPostgresObservationState(observer) {
12
+ return { observer, fingerprints: observer === undefined ? undefined : new WeakMap() };
13
+ }
14
+ function postgresQueryFingerprint(state, query) {
15
+ const key = query;
16
+ const cached = state.fingerprints?.get(key);
17
+ if (cached !== undefined)
18
+ return cached;
19
+ const text = renderQuery(query, postgresRenderer).text;
20
+ const fingerprint = `sha256:${createHash("sha256")
21
+ .update(`postgres\0${POSTGRES_DIALECT_VERSION}\0${text}`)
22
+ .digest("hex")}`;
23
+ state.fingerprints?.set(key, fingerprint);
24
+ return fingerprint;
25
+ }
8
26
  const oids = {
9
27
  int8: 20,
10
28
  date: 1082,
@@ -125,6 +143,7 @@ class PostgresDatabaseImplementation {
125
143
  #parsers;
126
144
  #ownsPool;
127
145
  #prepared;
146
+ #observation;
128
147
  #transactionDepth;
129
148
  #transactionScopeOpen = true;
130
149
  #transactionOperationFailed = false;
@@ -132,12 +151,16 @@ class PostgresDatabaseImplementation {
132
151
  #transactionState;
133
152
  #transactionExecutes = new Set();
134
153
  #transactionStreams = new Set();
135
- constructor(pool, client, parsers, ownsPool, depth, prepared, transactionState = {
154
+ executionCapabilities;
155
+ [adapterCapabilities];
156
+ constructor(pool, client, parsers, ownsPool, depth, prepared, observation, transactionState = {
136
157
  activeBatch: undefined,
158
+ activeCopy: undefined,
137
159
  activeExecutes: new Set(),
138
160
  activePipeline: undefined,
139
161
  activeStreams: new Set(),
140
162
  discardLease: false,
163
+ leaseReleased: false,
141
164
  firstDatabaseOperationError: undefined,
142
165
  unsafe: false,
143
166
  unsafeDepth: undefined,
@@ -150,12 +173,29 @@ class PostgresDatabaseImplementation {
150
173
  this.#ownsPool = ownsPool;
151
174
  this.#transactionDepth = depth;
152
175
  this.#prepared = prepared;
176
+ this.#observation = observation;
153
177
  this.#transactionState = transactionState;
178
+ this.executionCapabilities = Object.freeze({
179
+ cancellation: pool.executionCapabilities?.cancellation ?? false,
180
+ deadlines: pool.executionCapabilities?.deadlines ?? false,
181
+ });
182
+ const copyTransport = {
183
+ copyFrom: (statement, chunks, options) => this.#copyFrom(statement, chunks, options),
184
+ copyTo: (statement, options) => this.#copyTo(statement, options),
185
+ };
186
+ this[adapterCapabilities] = createAdapterCapabilityResolver(pool.ensureCopy === undefined ? [] : [[postgresCopy, createPostgresCopyCapability(copyTransport)]]);
154
187
  }
155
188
  async execute(query) {
189
+ if (this.#observation.observer === undefined) {
190
+ return this.#validateRows(query, await this.#executeUnobserved(query));
191
+ }
192
+ return this.#observeQuery(query, "many", async () => this.#validateRows(query, await this.#executeUnobserved(query)));
193
+ }
194
+ async #executeUnobserved(query) {
156
195
  this.#assertTransactionScopeOpen();
157
196
  this.#rejectOperationDuringTransactionStream("execute another query");
158
197
  this.#rejectOperationDuringTransactionBatch("execute another query");
198
+ this.#rejectOperationDuringTransactionCopy("execute another query");
159
199
  this.#rejectOperationDuringTransactionPipeline("execute another query");
160
200
  const config = this.#queryConfig(query);
161
201
  let operation;
@@ -183,10 +223,135 @@ class PostgresDatabaseImplementation {
183
223
  this.#transactionState.activeExecutes.delete(operation);
184
224
  }
185
225
  }
226
+ async all(query, options) {
227
+ if (this.#observation.observer === undefined) {
228
+ return this.#validateRows(query, await this.#allUnobserved(query, options));
229
+ }
230
+ return this.#observeQuery(query, "many", async () => this.#validateRows(query, await this.#allUnobserved(query, options)));
231
+ }
232
+ async #allUnobserved(query, options) {
233
+ if (options === undefined || (options.signal === undefined && options.deadline === undefined))
234
+ return this.#executeUnobserved(query);
235
+ assertExecutionCapabilities(this.executionCapabilities, options);
236
+ this.#assertTransactionScopeOpen();
237
+ this.#rejectOperationDuringTransactionStream("execute another query");
238
+ this.#rejectOperationDuringTransactionBatch("execute another query");
239
+ this.#rejectOperationDuringTransactionCopy("execute another query");
240
+ this.#rejectOperationDuringTransactionPipeline("execute another query");
241
+ if (options.signal?.aborted || (options.deadline !== undefined && Number(options.deadline) <= Date.now())) {
242
+ return runControlledExecution(options, async () => [], () => undefined);
243
+ }
244
+ if (this.#client === undefined) {
245
+ const client = await this.#pool.connect();
246
+ let discarded = false;
247
+ try {
248
+ return await this.#executeControlledOn(client, query, options, (error) => {
249
+ client.release(error);
250
+ discarded = true;
251
+ });
252
+ }
253
+ finally {
254
+ if (!discarded)
255
+ client.release();
256
+ }
257
+ }
258
+ const client = this.#client;
259
+ const operation = this.#executeControlledOn(client, query, options, (error) => {
260
+ this.#transactionState.discardLease = true;
261
+ this.#markTransactionStateUnsafe(error);
262
+ try {
263
+ client.release(error);
264
+ this.#transactionState.leaseReleased = true;
265
+ }
266
+ catch {
267
+ /* The transaction finalizer also owns lease cleanup. */
268
+ }
269
+ });
270
+ this.#transactionExecutes.add(operation);
271
+ this.#transactionState.activeExecutes.add(operation);
272
+ try {
273
+ return await operation;
274
+ }
275
+ catch (error) {
276
+ this.#recordTransactionOperationFailure(error);
277
+ throw error;
278
+ }
279
+ finally {
280
+ this.#transactionExecutes.delete(operation);
281
+ this.#transactionState.activeExecutes.delete(operation);
282
+ }
283
+ }
284
+ async #executeControlledOn(client, query, options, cancel) {
285
+ const result = await runControlledExecution(options, () => client.query(this.#queryConfig(query)), cancel);
286
+ return result.rows;
287
+ }
288
+ async one(query, options) {
289
+ if (this.#observation.observer === undefined) {
290
+ const rows = await this.#validateRows(query, await this.#allUnobserved(query, options));
291
+ if (rows.length !== 1)
292
+ throw new QueryCardinalityError("one", rows.length);
293
+ return rows[0];
294
+ }
295
+ const rows = await this.#observeQuery(query, "one", async () => {
296
+ const result = await this.#validateRows(query, await this.#allUnobserved(query, options));
297
+ if (result.length !== 1)
298
+ throw new QueryCardinalityError("one", result.length);
299
+ return result;
300
+ });
301
+ return rows[0];
302
+ }
303
+ async maybeOne(query, options) {
304
+ if (this.#observation.observer === undefined) {
305
+ const rows = await this.#validateRows(query, await this.#allUnobserved(query, options));
306
+ if (rows.length > 1)
307
+ throw new QueryCardinalityError("maybeOne", rows.length);
308
+ return rows[0];
309
+ }
310
+ const rows = await this.#observeQuery(query, "maybeOne", async () => {
311
+ const result = await this.#validateRows(query, await this.#allUnobserved(query, options));
312
+ if (result.length > 1)
313
+ throw new QueryCardinalityError("maybeOne", result.length);
314
+ return result;
315
+ });
316
+ return rows[0];
317
+ }
318
+ async #observeQuery(query, cardinality, operation) {
319
+ if (this.#observation.observer === undefined)
320
+ return operation();
321
+ const observation = startDatabaseObservation(this.#observation.observer, {
322
+ kind: "query",
323
+ dialect: "postgres",
324
+ grammarVersion: POSTGRES_DIALECT_VERSION,
325
+ transactionDepth: this.#transactionDepth,
326
+ fingerprint: postgresQueryFingerprint(this.#observation, query),
327
+ cardinality,
328
+ prepared: this.#prepared.queries.has(queryResultValidationSource(query)),
329
+ });
330
+ if (observation === undefined)
331
+ return operation();
332
+ try {
333
+ const rows = await observation.run(operation);
334
+ observation.end({ status: "success", rowCount: rows.length });
335
+ return rows;
336
+ }
337
+ catch (error) {
338
+ observation.end(databaseErrorCompletion(error), error);
339
+ throw error;
340
+ }
341
+ }
186
342
  async batch(queries) {
343
+ if (queries.length === 0)
344
+ return emptyBatchResults;
345
+ if (this.#observation.observer === undefined) {
346
+ return this.#validateBatch(queries, await this.#batchUnobserved(queries));
347
+ }
348
+ return this.#observeGroup("batch", queries, async () => this.#validateBatch(queries, await this.#batchUnobserved(queries)));
349
+ }
350
+ async #batchUnobserved(queries) {
187
351
  this.#assertTransactionScopeOpen();
188
352
  this.#rejectOperationDuringTransactionStream("execute a query batch");
189
353
  this.#rejectOperationDuringTransactionBatch("execute another query batch");
354
+ this.#rejectOperationDuringTransactionCopy("execute a query batch");
190
355
  this.#rejectOperationDuringTransactionPipeline("execute a query batch");
191
356
  if (queries.length === 0)
192
357
  return emptyBatchResults;
@@ -224,9 +389,18 @@ class PostgresDatabaseImplementation {
224
389
  return results;
225
390
  }
226
391
  async pipeline(queries) {
392
+ if (queries.length === 0)
393
+ return emptyBatchResults;
394
+ if (this.#observation.observer === undefined) {
395
+ return this.#validateBatch(queries, await this.#pipelineUnobserved(queries));
396
+ }
397
+ return this.#observeGroup("pipeline", queries, async () => this.#validateBatch(queries, await this.#pipelineUnobserved(queries)));
398
+ }
399
+ async #pipelineUnobserved(queries) {
227
400
  this.#assertTransactionScopeOpen();
228
401
  this.#rejectOperationDuringTransactionStream("execute a query pipeline");
229
402
  this.#rejectOperationDuringTransactionBatch("execute a query pipeline");
403
+ this.#rejectOperationDuringTransactionCopy("execute a query pipeline");
230
404
  this.#rejectOperationDuringTransactionExecute("execute a query pipeline");
231
405
  this.#rejectOperationDuringTransactionPipeline("execute another query pipeline");
232
406
  if (queries.length === 0)
@@ -276,15 +450,40 @@ class PostgresDatabaseImplementation {
276
450
  throw error;
277
451
  }
278
452
  }
453
+ async #observeGroup(kind, queries, operation) {
454
+ if (this.#observation.observer === undefined)
455
+ return operation();
456
+ const fingerprints = Object.freeze([...queries].map((query) => postgresQueryFingerprint(this.#observation, query)));
457
+ const observation = startDatabaseObservation(this.#observation.observer, {
458
+ kind,
459
+ dialect: "postgres",
460
+ grammarVersion: POSTGRES_DIALECT_VERSION,
461
+ transactionDepth: this.#transactionDepth,
462
+ fingerprints,
463
+ size: queries.length,
464
+ });
465
+ if (observation === undefined)
466
+ return operation();
467
+ try {
468
+ const results = await observation.run(operation);
469
+ observation.end({ status: "success" });
470
+ return results;
471
+ }
472
+ catch (error) {
473
+ observation.end(databaseErrorCompletion(error), error);
474
+ throw error;
475
+ }
476
+ }
279
477
  stream(query, options = {}) {
280
478
  this.#assertTransactionScopeOpen();
281
479
  this.#rejectOperationDuringTransactionStream("start another query stream");
282
480
  this.#rejectOperationDuringTransactionBatch("start a query stream");
481
+ this.#rejectOperationDuringTransactionCopy("start a query stream");
283
482
  this.#rejectOperationDuringTransactionPipeline("start a query stream");
284
483
  const batchSize = validatePostgresStreamBatchSize(options.batchSize);
285
484
  const config = this.#queryConfig(query);
286
- let stream;
287
- stream = new PostgresQueryStream({
485
+ let exposedStream;
486
+ const stream = new PostgresQueryStream({
288
487
  batchSize,
289
488
  start: async () => {
290
489
  await this.#pool.ensureCursor?.();
@@ -303,7 +502,7 @@ class PostgresDatabaseImplementation {
303
502
  }
304
503
  try {
305
504
  const cursor = await client.openCursor(config);
306
- return { cursor, ...(release === undefined ? {} : { release }) };
505
+ return { cursor: cursor, ...(release === undefined ? {} : { release }) };
307
506
  }
308
507
  catch (error) {
309
508
  this.#recordTransactionOperationFailure(error);
@@ -323,38 +522,226 @@ class PostgresDatabaseImplementation {
323
522
  this.#assertTransactionScopeOpen();
324
523
  this.#rejectOperationDuringTransactionStream("start another query stream");
325
524
  this.#rejectOperationDuringTransactionBatch("start a query stream");
525
+ this.#rejectOperationDuringTransactionCopy("start a query stream");
326
526
  this.#rejectOperationDuringTransactionPipeline("start a query stream");
327
- this.#transactionState.activeStreams.add(stream);
527
+ this.#transactionState.activeStreams.add(exposedStream);
328
528
  },
329
529
  onClose: () => {
330
- this.#transactionState.activeStreams.delete(stream);
331
- this.#transactionStreams.delete(stream);
530
+ this.#transactionState.activeStreams.delete(exposedStream);
531
+ this.#transactionStreams.delete(exposedStream);
332
532
  },
333
533
  onError: (error) => this.#recordTransactionOperationFailure(error),
334
534
  }),
335
535
  });
536
+ const validatedStream = hasQueryResultValidator(query)
537
+ ? validateQueryResultStream(query, stream, postgresQueryFingerprint(this.#observation, query))
538
+ : stream;
539
+ exposedStream =
540
+ this.#observation.observer === undefined
541
+ ? validatedStream
542
+ : observeQueryStream(validatedStream, this.#observation.observer, {
543
+ kind: "stream",
544
+ dialect: "postgres",
545
+ grammarVersion: POSTGRES_DIALECT_VERSION,
546
+ transactionDepth: this.#transactionDepth,
547
+ fingerprint: postgresQueryFingerprint(this.#observation, query),
548
+ prepared: this.#prepared.queries.has(queryResultValidationSource(query)),
549
+ });
336
550
  if (this.#client !== undefined)
337
- this.#transactionStreams.add(stream);
338
- return stream;
551
+ this.#transactionStreams.add(exposedStream);
552
+ return exposedStream;
553
+ }
554
+ async #validateRows(query, rows) {
555
+ if (!hasQueryResultValidator(query))
556
+ return rows;
557
+ return validateQueryResultRows(query, rows, postgresQueryFingerprint(this.#observation, query));
558
+ }
559
+ async #validateBatch(queries, results) {
560
+ let validated;
561
+ const queryList = queries;
562
+ const resultList = results;
563
+ for (let index = 0; index < queryList.length; index += 1) {
564
+ const query = queryList[index];
565
+ if (!hasQueryResultValidator(query))
566
+ continue;
567
+ validated ??= [...resultList];
568
+ validated[index] = await validateQueryResultRows(query, resultList[index], postgresQueryFingerprint(this.#observation, query));
569
+ }
570
+ return (validated ?? results);
571
+ }
572
+ async #copyFrom(statement, chunks, options) {
573
+ assertExecutionCapabilities(this.executionCapabilities, options);
574
+ this.#assertTransactionScopeOpen();
575
+ this.#rejectOperationDuringTransactionStream("start COPY FROM");
576
+ this.#rejectOperationDuringTransactionBatch("start COPY FROM");
577
+ this.#rejectOperationDuringTransactionCopy("start another COPY operation");
578
+ this.#rejectOperationDuringTransactionExecute("start COPY FROM");
579
+ this.#rejectOperationDuringTransactionPipeline("start COPY FROM");
580
+ await this.#pool.ensureCopy?.();
581
+ const client = this.#client ?? (await this.#pool.connect());
582
+ let sink;
583
+ const operation = runControlledExecution(options, async () => {
584
+ if (client.openCopyFrom === undefined) {
585
+ throw new Error("PostgreSQL COPY requires the application-owned pg-copy-streams package. Install it with: pnpm add pg-copy-streams");
586
+ }
587
+ sink = await client.openCopyFrom(statement);
588
+ try {
589
+ for await (const chunk of chunks)
590
+ await sink.write(chunk);
591
+ await sink.finish();
592
+ }
593
+ catch (error) {
594
+ try {
595
+ await sink.abort(error);
596
+ }
597
+ catch {
598
+ /* Preserve the producer or database failure. */
599
+ }
600
+ throw error;
601
+ }
602
+ }, (error) => {
603
+ void sink?.abort(error).catch(() => undefined);
604
+ });
605
+ if (this.#client !== undefined)
606
+ this.#transactionState.activeCopy = operation;
607
+ try {
608
+ await operation;
609
+ if (this.#client === undefined)
610
+ client.release();
611
+ }
612
+ catch (error) {
613
+ this.#recordTransactionOperationFailure(error);
614
+ if (this.#client === undefined) {
615
+ try {
616
+ client.release(error instanceof Error ? error : true);
617
+ }
618
+ catch {
619
+ /* Preserve the COPY failure. */
620
+ }
621
+ }
622
+ throw error;
623
+ }
624
+ finally {
625
+ if (this.#transactionState.activeCopy === operation)
626
+ this.#transactionState.activeCopy = undefined;
627
+ }
628
+ }
629
+ #copyTo(statement, options) {
630
+ assertExecutionCapabilities(this.executionCapabilities, options);
631
+ this.#assertTransactionScopeOpen();
632
+ this.#rejectOperationDuringTransactionStream("start COPY TO");
633
+ this.#rejectOperationDuringTransactionBatch("start COPY TO");
634
+ this.#rejectOperationDuringTransactionCopy("start COPY TO");
635
+ this.#rejectOperationDuringTransactionPipeline("start COPY TO");
636
+ let exposedStream;
637
+ const stream = new PostgresQueryStream({
638
+ batchSize: 1,
639
+ start: async () => {
640
+ await this.#pool.ensureCopy?.();
641
+ const client = this.#client ?? (await this.#pool.connect());
642
+ const release = this.#client === undefined
643
+ ? (cleanupError) => client.release(cleanupError === undefined ? undefined : cleanupError instanceof Error ? cleanupError : true)
644
+ : undefined;
645
+ if (client.openCopyTo === undefined) {
646
+ try {
647
+ release?.();
648
+ }
649
+ catch {
650
+ /* The missing optional dependency remains primary. */
651
+ }
652
+ throw new Error("PostgreSQL COPY requires the application-owned pg-copy-streams package. Install it with: pnpm add pg-copy-streams");
653
+ }
654
+ try {
655
+ const source = await client.openCopyTo(statement);
656
+ return {
657
+ cursor: {
658
+ async read() {
659
+ const result = await runControlledExecution(options, () => source.next(), (error) => {
660
+ void source.abort(error).catch(() => undefined);
661
+ });
662
+ return result.done === true ? [] : [result.value];
663
+ },
664
+ close: () => source.close(),
665
+ },
666
+ ...(release === undefined ? {} : { release }),
667
+ };
668
+ }
669
+ catch (error) {
670
+ this.#recordTransactionOperationFailure(error);
671
+ try {
672
+ release?.(error);
673
+ }
674
+ catch {
675
+ /* Preserve source creation or optional-dependency failures. */
676
+ }
677
+ throw error;
678
+ }
679
+ },
680
+ ...(this.#client === undefined
681
+ ? {}
682
+ : {
683
+ onStart: () => {
684
+ this.#assertTransactionScopeOpen();
685
+ this.#rejectOperationDuringTransactionStream("start COPY TO");
686
+ this.#rejectOperationDuringTransactionBatch("start COPY TO");
687
+ this.#rejectOperationDuringTransactionCopy("start COPY TO");
688
+ this.#rejectOperationDuringTransactionPipeline("start COPY TO");
689
+ this.#transactionState.activeStreams.add(exposedStream);
690
+ },
691
+ onClose: () => {
692
+ this.#transactionState.activeStreams.delete(exposedStream);
693
+ this.#transactionStreams.delete(exposedStream);
694
+ },
695
+ onError: (error) => this.#recordTransactionOperationFailure(error),
696
+ }),
697
+ });
698
+ exposedStream = stream;
699
+ if (this.#client !== undefined)
700
+ this.#transactionStreams.add(exposedStream);
701
+ return exposedStream;
339
702
  }
340
703
  prepare(statementName, factory) {
341
704
  this.#assertTransactionScopeOpen();
342
705
  return preparePostgresQuery(this.#prepared, postgresRenderer, statementName, factory);
343
706
  }
344
707
  async transaction(fn) {
708
+ if (this.#observation.observer === undefined)
709
+ return this.#transactionUnobserved(fn);
710
+ const observation = startDatabaseObservation(this.#observation.observer, {
711
+ kind: "transaction",
712
+ dialect: "postgres",
713
+ grammarVersion: POSTGRES_DIALECT_VERSION,
714
+ transactionDepth: this.#transactionDepth + 1,
715
+ });
716
+ if (observation === undefined)
717
+ return this.#transactionUnobserved(fn);
718
+ try {
719
+ const result = await observation.run(() => this.#transactionUnobserved(fn));
720
+ observation.end({ status: "success" });
721
+ return result;
722
+ }
723
+ catch (error) {
724
+ observation.end(databaseErrorCompletion(error), error);
725
+ throw error;
726
+ }
727
+ }
728
+ async #transactionUnobserved(fn) {
345
729
  this.#assertTransactionScopeOpen();
346
730
  this.#rejectOperationDuringTransactionStream("start a nested transaction");
347
731
  this.#rejectOperationDuringTransactionBatch("start a nested transaction");
732
+ this.#rejectOperationDuringTransactionCopy("start a nested transaction");
348
733
  this.#rejectOperationDuringTransactionPipeline("start a nested transaction");
349
734
  if (this.#client !== undefined)
350
735
  return this.#nestedTransaction(fn);
351
736
  const client = await this.#pool.connect();
352
- const scope = new _a(this.#pool, client, this.#parsers, false, 1, this.#prepared, {
737
+ const scope = new _a(this.#pool, client, this.#parsers, false, 1, this.#prepared, this.#observation, {
353
738
  activeBatch: undefined,
739
+ activeCopy: undefined,
354
740
  activeExecutes: new Set(),
355
741
  activePipeline: undefined,
356
742
  activeStreams: new Set(),
357
743
  discardLease: false,
744
+ leaseReleased: false,
358
745
  firstDatabaseOperationError: undefined,
359
746
  unsafe: false,
360
747
  unsafeDepth: undefined,
@@ -394,18 +781,21 @@ class PostgresDatabaseImplementation {
394
781
  await scope.#closeTransactionStreamsPreservingError();
395
782
  await scope.#settleTransactionPipelinePreservingError();
396
783
  await scope.#settleTransactionBatchPreservingError();
397
- try {
398
- await client.query("ROLLBACK");
399
- }
400
- catch (cleanupError) {
401
- scope.#markTransactionStateUnsafe(cleanupError);
402
- /* Preserve the original error. */
403
- }
404
- try {
405
- client.release(scope.#transactionLeaseReleaseError());
406
- }
407
- catch {
408
- /* Preserve the original error. */
784
+ await scope.#settleTransactionCopyPreservingError();
785
+ if (!scope.#transactionState.leaseReleased) {
786
+ try {
787
+ await client.query("ROLLBACK");
788
+ }
789
+ catch (cleanupError) {
790
+ scope.#markTransactionStateUnsafe(cleanupError);
791
+ /* Preserve the original error. */
792
+ }
793
+ try {
794
+ client.release(scope.#transactionLeaseReleaseError());
795
+ }
796
+ catch {
797
+ /* Preserve the original error. */
798
+ }
409
799
  }
410
800
  throw error;
411
801
  }
@@ -423,7 +813,7 @@ class PostgresDatabaseImplementation {
423
813
  this.#recordTransactionOperationFailure(error);
424
814
  throw error;
425
815
  }
426
- const scope = new _a(this.#pool, client, this.#parsers, false, depth, this.#prepared, this.#transactionState);
816
+ const scope = new _a(this.#pool, client, this.#parsers, false, depth, this.#prepared, this.#observation, this.#transactionState);
427
817
  try {
428
818
  let result;
429
819
  try {
@@ -451,7 +841,8 @@ class PostgresDatabaseImplementation {
451
841
  await scope.#closeTransactionStreamsPreservingError();
452
842
  await scope.#settleTransactionPipelinePreservingError();
453
843
  await scope.#settleTransactionBatchPreservingError();
454
- if (scope.#transactionState.valid) {
844
+ await scope.#settleTransactionCopyPreservingError();
845
+ if (scope.#transactionState.valid && !scope.#transactionState.leaseReleased) {
455
846
  try {
456
847
  await client.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
457
848
  scope.#recoverTransactionStateAfterSavepoint(depth);
@@ -465,7 +856,7 @@ class PostgresDatabaseImplementation {
465
856
  }
466
857
  }
467
858
  #queryConfig(query) {
468
- const prepared = this.#prepared.queries.get(query);
859
+ const prepared = this.#prepared.queries.get(queryResultValidationSource(query));
469
860
  const rendered = prepared?.rendered ?? renderQuery(query, postgresRenderer);
470
861
  return {
471
862
  ...(prepared === undefined ? {} : { name: prepared.statementName }),
@@ -519,12 +910,14 @@ class PostgresDatabaseImplementation {
519
910
  const leakedExecutes = this.#transactionExecutes.size > 0 || this.#transactionState.activeExecutes.size > 0;
520
911
  const leakedPipeline = this.#transactionState.activePipeline !== undefined;
521
912
  const leakedBatch = this.#transactionState.activeBatch !== undefined;
522
- if (!leakedStreams && !leakedExecutes && !leakedPipeline && !leakedBatch)
913
+ const leakedCopy = this.#transactionState.activeCopy !== undefined;
914
+ if (!leakedStreams && !leakedExecutes && !leakedPipeline && !leakedBatch && !leakedCopy)
523
915
  return;
524
916
  await this.#settleTransactionExecutesPreservingError();
525
917
  await this.#closeTransactionStreamsPreservingError();
526
918
  await this.#settleTransactionPipelinePreservingError();
527
919
  await this.#settleTransactionBatchPreservingError();
920
+ await this.#settleTransactionCopyPreservingError();
528
921
  if (leakedExecutes) {
529
922
  throw new Error("A PostgreSQL transaction callback returned before an execute operation completed; await execute before returning");
530
923
  }
@@ -534,6 +927,9 @@ class PostgresDatabaseImplementation {
534
927
  if (leakedPipeline) {
535
928
  throw new Error("A PostgreSQL transaction callback returned before its query pipeline completed; await the pipeline before returning");
536
929
  }
930
+ if (leakedCopy) {
931
+ throw new Error("A PostgreSQL transaction callback returned before its COPY operation completed; await COPY before returning");
932
+ }
537
933
  throw new Error("A PostgreSQL transaction callback returned before all query streams were completed or closed");
538
934
  }
539
935
  async #settleTransactionExecutesPreservingError() {
@@ -580,6 +976,17 @@ class PostgresDatabaseImplementation {
580
976
  /* The callback or transaction misuse error remains primary. */
581
977
  }
582
978
  }
979
+ async #settleTransactionCopyPreservingError() {
980
+ const operation = this.#transactionState.activeCopy;
981
+ if (operation === undefined)
982
+ return;
983
+ try {
984
+ await operation;
985
+ }
986
+ catch {
987
+ /* The callback or transaction misuse error remains primary. */
988
+ }
989
+ }
583
990
  #rejectOperationDuringTransactionStream(operation) {
584
991
  if (this.#client !== undefined && this.#transactionState.activeStreams.size > 0) {
585
992
  throw new Error(`Cannot ${operation} while a PostgreSQL transaction query stream is still open; complete or close the stream first`);
@@ -590,6 +997,11 @@ class PostgresDatabaseImplementation {
590
997
  throw new Error(`Cannot ${operation} while a PostgreSQL transaction query batch is still running; await the batch first`);
591
998
  }
592
999
  }
1000
+ #rejectOperationDuringTransactionCopy(operation) {
1001
+ if (this.#client !== undefined && this.#transactionState.activeCopy !== undefined) {
1002
+ throw new Error(`Cannot ${operation} while a PostgreSQL transaction COPY operation is still running; await COPY first`);
1003
+ }
1004
+ }
593
1005
  #rejectOperationDuringTransactionExecute(operation) {
594
1006
  if (this.#client !== undefined && this.#transactionState.activeExecutes.size > 0) {
595
1007
  throw new Error(`Cannot ${operation} while a PostgreSQL transaction execute operation is still running; await execute first`);
@@ -674,6 +1086,6 @@ class PostgresDatabaseImplementation {
674
1086
  }
675
1087
  _a = PostgresDatabaseImplementation;
676
1088
  export function createPostgresDatabase(options) {
677
- return new PostgresDatabaseImplementation(options.pool, undefined, createPostgresTypeParsers(options.typePolicy ?? defaultPolicy, options.decimal, options.fallbackTypeParsers), options.ownsPool ?? false, 0, createPostgresPreparedQueryState());
1089
+ return new PostgresDatabaseImplementation(options.pool, undefined, createPostgresTypeParsers(options.typePolicy ?? defaultPolicy, options.decimal, options.fallbackTypeParsers), options.ownsPool ?? false, 0, createPostgresPreparedQueryState(), createPostgresObservationState(options.observer));
678
1090
  }
679
1091
  //# sourceMappingURL=runtime.js.map