@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.
- package/CHANGELOG.md +113 -0
- package/README.md +29 -4
- package/dist/packages/postgres/src/bulk.d.ts +30 -0
- package/dist/packages/postgres/src/bulk.d.ts.map +1 -0
- package/dist/packages/postgres/src/bulk.js +228 -0
- package/dist/packages/postgres/src/bulk.js.map +1 -0
- package/dist/packages/postgres/src/index.d.ts +5 -1
- package/dist/packages/postgres/src/index.d.ts.map +1 -1
- package/dist/packages/postgres/src/index.js +17 -4
- package/dist/packages/postgres/src/index.js.map +1 -1
- package/dist/packages/postgres/src/pg.d.ts +57 -2
- package/dist/packages/postgres/src/pg.d.ts.map +1 -1
- package/dist/packages/postgres/src/pg.js +346 -2
- package/dist/packages/postgres/src/pg.js.map +1 -1
- package/dist/packages/postgres/src/provider.d.ts +2 -1
- package/dist/packages/postgres/src/provider.d.ts.map +1 -1
- package/dist/packages/postgres/src/provider.js +3 -1
- package/dist/packages/postgres/src/provider.js.map +1 -1
- package/dist/packages/postgres/src/resolver.d.ts.map +1 -1
- package/dist/packages/postgres/src/resolver.js +11 -1
- package/dist/packages/postgres/src/resolver.js.map +1 -1
- package/dist/packages/postgres/src/routing.d.ts +22 -0
- package/dist/packages/postgres/src/routing.d.ts.map +1 -0
- package/dist/packages/postgres/src/routing.js +70 -0
- package/dist/packages/postgres/src/routing.js.map +1 -0
- package/dist/packages/postgres/src/runtime.d.ts +14 -1
- package/dist/packages/postgres/src/runtime.d.ts.map +1 -1
- package/dist/packages/postgres/src/runtime.js +440 -28
- package/dist/packages/postgres/src/runtime.js.map +1 -1
- package/dist/packages/postgres/src/semantics.d.ts +5 -0
- package/dist/packages/postgres/src/semantics.d.ts.map +1 -0
- package/dist/packages/postgres/src/semantics.js +211 -0
- package/dist/packages/postgres/src/semantics.js.map +1 -0
- package/dist/packages/postgres/src/stream.d.ts +7 -7
- package/dist/packages/postgres/src/stream.d.ts.map +1 -1
- package/dist/packages/postgres/src/stream.js.map +1 -1
- package/dist/packages/postgres/src/version.d.ts +2 -0
- package/dist/packages/postgres/src/version.d.ts.map +1 -0
- package/dist/packages/postgres/src/version.js +2 -0
- package/dist/packages/postgres/src/version.js.map +1 -0
- package/package.json +4 -4
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
|
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(
|
|
527
|
+
this.#transactionState.activeStreams.add(exposedStream);
|
|
328
528
|
},
|
|
329
529
|
onClose: () => {
|
|
330
|
-
this.#transactionState.activeStreams.delete(
|
|
331
|
-
this.#transactionStreams.delete(
|
|
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(
|
|
338
|
-
return
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|