kysely 0.28.18 → 0.28.19
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/README.md +1 -1
- package/dist/cjs/dialect/mssql/mssql-driver.d.ts +3 -0
- package/dist/cjs/dialect/mysql/mysql-driver.d.ts +3 -0
- package/dist/cjs/dialect/postgres/postgres-dialect-config.d.ts +2 -2
- package/dist/cjs/dialect/postgres/postgres-driver.d.ts +6 -1
- package/dist/cjs/dialect/postgres/postgres-driver.js +30 -3
- package/dist/cjs/dialect/sqlite/sqlite-driver.d.ts +3 -0
- package/dist/cjs/driver/aborted-transaction-error.d.ts +11 -0
- package/dist/cjs/driver/aborted-transaction-error.js +20 -0
- package/dist/cjs/driver/driver.d.ts +3 -0
- package/dist/cjs/driver/dummy-driver.d.ts +3 -0
- package/dist/cjs/driver/runtime-driver.d.ts +3 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/kysely.d.ts +9 -4
- package/dist/cjs/kysely.js +60 -38
- package/dist/esm/dialect/mssql/mssql-driver.d.ts +3 -0
- package/dist/esm/dialect/mysql/mysql-driver.d.ts +3 -0
- package/dist/esm/dialect/postgres/postgres-dialect-config.d.ts +2 -2
- package/dist/esm/dialect/postgres/postgres-driver.d.ts +6 -1
- package/dist/esm/dialect/postgres/postgres-driver.js +30 -3
- package/dist/esm/dialect/sqlite/sqlite-driver.d.ts +3 -0
- package/dist/esm/driver/aborted-transaction-error.d.ts +11 -0
- package/dist/esm/driver/aborted-transaction-error.js +16 -0
- package/dist/esm/driver/driver.d.ts +3 -0
- package/dist/esm/driver/dummy-driver.d.ts +3 -0
- package/dist/esm/driver/runtime-driver.d.ts +3 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/kysely.d.ts +9 -4
- package/dist/esm/kysely.js +60 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://stand-with-ukraine.pp.ua)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/kysely-org/kysely/releases/latest)
|
|
4
|
-
[](https://socket.dev/npm/package/kysely/overview/0.28.19)
|
|
5
5
|
[](https://github.com/kysely-org/kysely)
|
|
6
6
|
[](https://github.com/kysely-org/kysely/blob/master/LICENSE)
|
|
7
7
|
[](https://github.com/kysely-org/kysely/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)
|
|
@@ -25,6 +25,9 @@ export declare class MssqlDriver implements Driver {
|
|
|
25
25
|
beginTransaction(connection: MssqlConnection, settings: TransactionSettings): Promise<void>;
|
|
26
26
|
/**
|
|
27
27
|
* Commits a transaction.
|
|
28
|
+
*
|
|
29
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
30
|
+
* the transaction was rolled back instead of committed.
|
|
28
31
|
*/
|
|
29
32
|
commitTransaction(connection: MssqlConnection): Promise<void>;
|
|
30
33
|
/**
|
|
@@ -24,6 +24,9 @@ export declare class MysqlDriver implements Driver {
|
|
|
24
24
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
25
25
|
/**
|
|
26
26
|
* Commits a transaction.
|
|
27
|
+
*
|
|
28
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
29
|
+
* the transaction was rolled back instead of committed.
|
|
27
30
|
*/
|
|
28
31
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
29
32
|
/**
|
|
@@ -61,7 +61,7 @@ export interface PostgresPool {
|
|
|
61
61
|
export interface PostgresPoolClient {
|
|
62
62
|
query<R>(sql: string, parameters: ReadonlyArray<unknown>): Promise<PostgresQueryResult<R>>;
|
|
63
63
|
query<R>(cursor: PostgresCursor<R>): PostgresCursor<R>;
|
|
64
|
-
release(): void;
|
|
64
|
+
release(destroy?: boolean): void;
|
|
65
65
|
}
|
|
66
66
|
export interface PostgresCursor<T> {
|
|
67
67
|
read(rowsCount: number): Promise<T[]>;
|
|
@@ -69,7 +69,7 @@ export interface PostgresCursor<T> {
|
|
|
69
69
|
}
|
|
70
70
|
export type PostgresCursorConstructor = new <T>(sql: string, parameters: unknown[]) => PostgresCursor<T>;
|
|
71
71
|
export interface PostgresQueryResult<R> {
|
|
72
|
-
command: 'UPDATE' | 'DELETE' | 'INSERT' | 'SELECT' | 'MERGE';
|
|
72
|
+
command: 'UPDATE' | 'DELETE' | 'INSERT' | 'SELECT' | 'MERGE' | 'COMMIT' | 'ROLLBACK';
|
|
73
73
|
rowCount: number;
|
|
74
74
|
rows: R[];
|
|
75
75
|
}
|
|
@@ -4,6 +4,7 @@ import { CompiledQuery } from '../../query-compiler/compiled-query.js';
|
|
|
4
4
|
import type { QueryCompiler } from '../../query-compiler/query-compiler.js';
|
|
5
5
|
import type { PostgresCursorConstructor, PostgresDialectConfig, PostgresPoolClient } from './postgres-dialect-config.js';
|
|
6
6
|
declare const PRIVATE_RELEASE_METHOD: unique symbol;
|
|
7
|
+
declare const PRIVATE_MARK_FOR_DISCARD_METHOD: unique symbol;
|
|
7
8
|
export declare class PostgresDriver implements Driver {
|
|
8
9
|
#private;
|
|
9
10
|
constructor(config: PostgresDialectConfig);
|
|
@@ -24,12 +25,15 @@ export declare class PostgresDriver implements Driver {
|
|
|
24
25
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
25
26
|
/**
|
|
26
27
|
* Commits a transaction.
|
|
28
|
+
*
|
|
29
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
30
|
+
* the transaction was rolled back instead of committed.
|
|
27
31
|
*/
|
|
28
32
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
29
33
|
/**
|
|
30
34
|
* Rolls back a transaction.
|
|
31
35
|
*/
|
|
32
|
-
rollbackTransaction(connection:
|
|
36
|
+
rollbackTransaction(connection: PostgresConnection): Promise<void>;
|
|
33
37
|
savepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
34
38
|
rollbackToSavepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
35
39
|
releaseSavepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
@@ -51,5 +55,6 @@ declare class PostgresConnection implements DatabaseConnection {
|
|
|
51
55
|
executeQuery<O>(compiledQuery: CompiledQuery): Promise<QueryResult<O>>;
|
|
52
56
|
streamQuery<O>(compiledQuery: CompiledQuery, chunkSize: number): AsyncIterableIterator<QueryResult<O>>;
|
|
53
57
|
[PRIVATE_RELEASE_METHOD](): void;
|
|
58
|
+
[PRIVATE_MARK_FOR_DISCARD_METHOD](): void;
|
|
54
59
|
}
|
|
55
60
|
export {};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PostgresDriver = void 0;
|
|
4
|
+
const aborted_transaction_error_js_1 = require("../../driver/aborted-transaction-error.js");
|
|
4
5
|
const savepoint_parser_js_1 = require("../../parser/savepoint-parser.js");
|
|
5
6
|
const compiled_query_js_1 = require("../../query-compiler/compiled-query.js");
|
|
6
7
|
const object_utils_js_1 = require("../../util/object-utils.js");
|
|
7
8
|
const query_id_js_1 = require("../../util/query-id.js");
|
|
8
9
|
const stack_trace_utils_js_1 = require("../../util/stack-trace-utils.js");
|
|
9
10
|
const PRIVATE_RELEASE_METHOD = Symbol();
|
|
11
|
+
const PRIVATE_MARK_FOR_DISCARD_METHOD = Symbol();
|
|
10
12
|
class PostgresDriver {
|
|
11
13
|
#config;
|
|
12
14
|
#connections = new WeakMap();
|
|
@@ -60,10 +62,21 @@ class PostgresDriver {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
async commitTransaction(connection) {
|
|
63
|
-
await connection.executeQuery(compiled_query_js_1.CompiledQuery.raw('commit'));
|
|
65
|
+
const { rows } = await connection.executeQuery(compiled_query_js_1.CompiledQuery.raw('commit'));
|
|
66
|
+
// PostgreSQL completes COMMIT with a ROLLBACK tag if an earlier
|
|
67
|
+
// statement error left the transaction aborted.
|
|
68
|
+
if (rows[0]?.command === 'ROLLBACK') {
|
|
69
|
+
throw new aborted_transaction_error_js_1.AbortedTransactionError();
|
|
70
|
+
}
|
|
64
71
|
}
|
|
65
72
|
async rollbackTransaction(connection) {
|
|
66
|
-
|
|
73
|
+
try {
|
|
74
|
+
await connection.executeQuery(compiled_query_js_1.CompiledQuery.raw('rollback'));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
connection[PRIVATE_MARK_FOR_DISCARD_METHOD]();
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
async savepoint(connection, savepointName, compileQuery) {
|
|
69
82
|
await connection.executeQuery(compileQuery((0, savepoint_parser_js_1.parseSavepointCommand)('savepoint', savepointName), (0, query_id_js_1.createQueryId)()));
|
|
@@ -89,13 +102,18 @@ exports.PostgresDriver = PostgresDriver;
|
|
|
89
102
|
class PostgresConnection {
|
|
90
103
|
#client;
|
|
91
104
|
#options;
|
|
105
|
+
#discarded;
|
|
92
106
|
constructor(client, options) {
|
|
93
107
|
this.#client = client;
|
|
94
108
|
this.#options = options;
|
|
95
109
|
}
|
|
96
110
|
async executeQuery(compiledQuery) {
|
|
111
|
+
this.#assertNotDiscarded();
|
|
97
112
|
try {
|
|
98
113
|
const { command, rowCount, rows } = await this.#client.query(compiledQuery.sql, [...compiledQuery.parameters]);
|
|
114
|
+
if (command === 'ROLLBACK' || command === 'COMMIT') {
|
|
115
|
+
return { rows: [{ command }] };
|
|
116
|
+
}
|
|
99
117
|
return {
|
|
100
118
|
numAffectedRows: command === 'INSERT' ||
|
|
101
119
|
command === 'UPDATE' ||
|
|
@@ -117,6 +135,7 @@ class PostgresConnection {
|
|
|
117
135
|
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
|
|
118
136
|
throw new Error('chunkSize must be a positive integer');
|
|
119
137
|
}
|
|
138
|
+
this.#assertNotDiscarded();
|
|
120
139
|
const cursor = this.#client.query(new this.#options.cursor(compiledQuery.sql, compiledQuery.parameters.slice()));
|
|
121
140
|
try {
|
|
122
141
|
while (true) {
|
|
@@ -134,6 +153,14 @@ class PostgresConnection {
|
|
|
134
153
|
}
|
|
135
154
|
}
|
|
136
155
|
[PRIVATE_RELEASE_METHOD]() {
|
|
137
|
-
this.#client.release();
|
|
156
|
+
this.#client.release(Boolean(this.#discarded));
|
|
157
|
+
}
|
|
158
|
+
[PRIVATE_MARK_FOR_DISCARD_METHOD]() {
|
|
159
|
+
this.#discarded = true;
|
|
160
|
+
}
|
|
161
|
+
#assertNotDiscarded() {
|
|
162
|
+
if (this.#discarded) {
|
|
163
|
+
throw new Error('Postgres connection has been discarded');
|
|
164
|
+
}
|
|
138
165
|
}
|
|
139
166
|
}
|
|
@@ -22,6 +22,9 @@ export declare class SqliteDriver implements Driver {
|
|
|
22
22
|
beginTransaction(connection: DatabaseConnection): Promise<void>;
|
|
23
23
|
/**
|
|
24
24
|
* Commits a transaction.
|
|
25
|
+
*
|
|
26
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
27
|
+
* the transaction was rolled back instead of committed.
|
|
25
28
|
*/
|
|
26
29
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
27
30
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const ABORTED_TRANSACTION_ERROR: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when the database rolls back a transaction instead of committing it.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AbortedTransactionError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
readonly [ABORTED_TRANSACTION_ERROR] = true;
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare function isAbortedTransactionError(error: unknown): error is AbortedTransactionError;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AbortedTransactionError = void 0;
|
|
4
|
+
exports.isAbortedTransactionError = isAbortedTransactionError;
|
|
5
|
+
const object_utils_js_1 = require("../util/object-utils.js");
|
|
6
|
+
const ABORTED_TRANSACTION_ERROR = Symbol.for('kysely.AbortedTransactionError');
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when the database rolls back a transaction instead of committing it.
|
|
9
|
+
*/
|
|
10
|
+
class AbortedTransactionError extends Error {
|
|
11
|
+
name = 'AbortedTransactionError';
|
|
12
|
+
[ABORTED_TRANSACTION_ERROR] = true;
|
|
13
|
+
constructor() {
|
|
14
|
+
super('Transaction was rolled back instead of committed');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.AbortedTransactionError = AbortedTransactionError;
|
|
18
|
+
function isAbortedTransactionError(error) {
|
|
19
|
+
return (0, object_utils_js_1.isObject)(error) && ABORTED_TRANSACTION_ERROR in error;
|
|
20
|
+
}
|
|
@@ -23,6 +23,9 @@ export interface Driver {
|
|
|
23
23
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
24
24
|
/**
|
|
25
25
|
* Commits a transaction.
|
|
26
|
+
*
|
|
27
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
28
|
+
* the transaction was rolled back instead of committed.
|
|
26
29
|
*/
|
|
27
30
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
28
31
|
/**
|
|
@@ -55,6 +55,9 @@ export declare class DummyDriver implements Driver {
|
|
|
55
55
|
beginTransaction(): Promise<void>;
|
|
56
56
|
/**
|
|
57
57
|
* Commits a transaction.
|
|
58
|
+
*
|
|
59
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
60
|
+
* the transaction was rolled back instead of committed.
|
|
58
61
|
*/
|
|
59
62
|
commitTransaction(): Promise<void>;
|
|
60
63
|
/**
|
|
@@ -31,6 +31,9 @@ export declare class RuntimeDriver implements Driver {
|
|
|
31
31
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
32
32
|
/**
|
|
33
33
|
* Commits a transaction.
|
|
34
|
+
*
|
|
35
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
36
|
+
* the transaction was rolled back instead of committed.
|
|
34
37
|
*/
|
|
35
38
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
36
39
|
/**
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export * from './dynamic/dynamic.js';
|
|
|
57
57
|
export * from './dynamic/dynamic-reference-builder.js';
|
|
58
58
|
export * from './dynamic/dynamic-table-builder.js';
|
|
59
59
|
export * from './driver/driver.js';
|
|
60
|
+
export * from './driver/aborted-transaction-error.js';
|
|
60
61
|
export * from './driver/database-connection.js';
|
|
61
62
|
export * from './driver/connection-provider.js';
|
|
62
63
|
export * from './driver/default-connection-provider.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -75,6 +75,7 @@ __exportStar(require("./dynamic/dynamic.js"), exports);
|
|
|
75
75
|
__exportStar(require("./dynamic/dynamic-reference-builder.js"), exports);
|
|
76
76
|
__exportStar(require("./dynamic/dynamic-table-builder.js"), exports);
|
|
77
77
|
__exportStar(require("./driver/driver.js"), exports);
|
|
78
|
+
__exportStar(require("./driver/aborted-transaction-error.js"), exports);
|
|
78
79
|
__exportStar(require("./driver/database-connection.js"), exports);
|
|
79
80
|
__exportStar(require("./driver/connection-provider.js"), exports);
|
|
80
81
|
__exportStar(require("./driver/default-connection-provider.js"), exports);
|
package/dist/cjs/kysely.d.ts
CHANGED
|
@@ -665,6 +665,9 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
665
665
|
/**
|
|
666
666
|
* Commits the transaction.
|
|
667
667
|
*
|
|
668
|
+
* If the database rolls back instead of committing, marks the transaction
|
|
669
|
+
* as rolled back, releases the connection and throws {@link AbortedTransactionError}.
|
|
670
|
+
*
|
|
668
671
|
* Waits for ongoing queries to finish. Streams must be exhausted or closed
|
|
669
672
|
* before awaiting this command. When consuming a stream with `for await`,
|
|
670
673
|
* exit the loop before committing.
|
|
@@ -694,6 +697,9 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
694
697
|
/**
|
|
695
698
|
* Rolls back the transaction.
|
|
696
699
|
*
|
|
700
|
+
* If a commit already reported that the database rolled back the transaction,
|
|
701
|
+
* succeeds without sending another rollback command.
|
|
702
|
+
*
|
|
697
703
|
* The connection is released even if the rollback fails, so the transaction
|
|
698
704
|
* cannot be used afterwards in either case.
|
|
699
705
|
*
|
|
@@ -876,13 +882,12 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
876
882
|
*/
|
|
877
883
|
withTables<T extends Record<string, Record<string, any>>>(): ControlledTransaction<DrainOuterGeneric<DB & T>, S>;
|
|
878
884
|
}
|
|
879
|
-
interface
|
|
880
|
-
|
|
881
|
-
isRolledBack: boolean;
|
|
885
|
+
interface ControlledTransactionState {
|
|
886
|
+
status: 'active' | 'committed' | 'rolled back' | 'aborted';
|
|
882
887
|
}
|
|
883
888
|
interface ControlledTransactionProps extends KyselyProps {
|
|
884
889
|
readonly connection: ControlledConnection;
|
|
885
|
-
readonly
|
|
890
|
+
readonly transactionState?: ControlledTransactionState;
|
|
886
891
|
}
|
|
887
892
|
export declare class Command<T> {
|
|
888
893
|
#private;
|
package/dist/cjs/kysely.js
CHANGED
|
@@ -9,6 +9,7 @@ const query_creator_js_1 = require("./query-creator.js");
|
|
|
9
9
|
const default_query_executor_js_1 = require("./query-executor/default-query-executor.js");
|
|
10
10
|
const object_utils_js_1 = require("./util/object-utils.js");
|
|
11
11
|
const runtime_driver_js_1 = require("./driver/runtime-driver.js");
|
|
12
|
+
const aborted_transaction_error_js_1 = require("./driver/aborted-transaction-error.js");
|
|
12
13
|
const single_connection_provider_js_1 = require("./driver/single-connection-provider.js");
|
|
13
14
|
const driver_js_1 = require("./driver/driver.js");
|
|
14
15
|
const function_module_js_1 = require("./query-builder/function-module.js");
|
|
@@ -575,8 +576,10 @@ class TransactionBuilder {
|
|
|
575
576
|
const settings = { isolationLevel, accessMode };
|
|
576
577
|
(0, driver_js_1.validateTransactionSettings)(settings);
|
|
577
578
|
return this.#props.executor.provideConnection(async (connection) => {
|
|
578
|
-
const
|
|
579
|
-
|
|
579
|
+
const transactionState = {
|
|
580
|
+
status: 'active',
|
|
581
|
+
};
|
|
582
|
+
const executor = new NotCommittedOrRolledBackAssertingExecutor(this.#props.executor.withConnectionProvider(new single_connection_provider_js_1.SingleConnectionProvider(connection)), transactionState);
|
|
580
583
|
const transaction = new Transaction({
|
|
581
584
|
...kyselyProps,
|
|
582
585
|
executor,
|
|
@@ -587,13 +590,16 @@ class TransactionBuilder {
|
|
|
587
590
|
transactionBegun = true;
|
|
588
591
|
const result = await callback(transaction);
|
|
589
592
|
await this.#props.driver.commitTransaction(connection);
|
|
590
|
-
|
|
593
|
+
transactionState.status = 'committed';
|
|
591
594
|
return result;
|
|
592
595
|
}
|
|
593
596
|
catch (error) {
|
|
594
|
-
if (
|
|
597
|
+
if ((0, aborted_transaction_error_js_1.isAbortedTransactionError)(error)) {
|
|
598
|
+
transactionState.status = 'aborted';
|
|
599
|
+
}
|
|
600
|
+
else if (transactionBegun) {
|
|
595
601
|
await this.#props.driver.rollbackTransaction(connection);
|
|
596
|
-
|
|
602
|
+
transactionState.status = 'rolled back';
|
|
597
603
|
}
|
|
598
604
|
throw error;
|
|
599
605
|
}
|
|
@@ -633,7 +639,7 @@ class ControlledTransactionBuilder {
|
|
|
633
639
|
return new ControlledTransaction({
|
|
634
640
|
...props,
|
|
635
641
|
connection,
|
|
636
|
-
|
|
642
|
+
transactionState: { status: 'active' },
|
|
637
643
|
executor: this.#props.executor.withConnectionProvider(new single_connection_provider_js_1.SingleConnectionProvider(connection.connection)),
|
|
638
644
|
});
|
|
639
645
|
}
|
|
@@ -643,30 +649,33 @@ class ControlledTransaction extends Transaction {
|
|
|
643
649
|
#props;
|
|
644
650
|
#compileQuery;
|
|
645
651
|
constructor(props) {
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
isRolledBack: false,
|
|
652
|
+
const controlledState = props.transactionState ?? {
|
|
653
|
+
status: 'active',
|
|
649
654
|
};
|
|
650
655
|
const controlledProps = {
|
|
651
656
|
...props,
|
|
652
|
-
|
|
653
|
-
executor: new NotCommittedOrRolledBackAssertingExecutor(props.executor,
|
|
657
|
+
transactionState: controlledState,
|
|
658
|
+
executor: new NotCommittedOrRolledBackAssertingExecutor(props.executor, controlledState),
|
|
654
659
|
};
|
|
655
|
-
const { connection,
|
|
660
|
+
const { connection, transactionState, ...transactionProps } = controlledProps;
|
|
656
661
|
super(transactionProps);
|
|
657
662
|
this.#props = (0, object_utils_js_1.freeze)(controlledProps);
|
|
658
663
|
const queryId = (0, query_id_js_1.createQueryId)();
|
|
659
664
|
this.#compileQuery = (node) => controlledProps.executor.compileQuery(node, queryId);
|
|
660
665
|
}
|
|
661
666
|
get isCommitted() {
|
|
662
|
-
return this.#props.
|
|
667
|
+
return this.#props.transactionState.status === 'committed';
|
|
663
668
|
}
|
|
664
669
|
get isRolledBack() {
|
|
665
|
-
return this.#props.
|
|
670
|
+
return (this.#props.transactionState.status === 'rolled back' ||
|
|
671
|
+
this.#props.transactionState.status === 'aborted');
|
|
666
672
|
}
|
|
667
673
|
/**
|
|
668
674
|
* Commits the transaction.
|
|
669
675
|
*
|
|
676
|
+
* If the database rolls back instead of committing, marks the transaction
|
|
677
|
+
* as rolled back, releases the connection and throws {@link AbortedTransactionError}.
|
|
678
|
+
*
|
|
670
679
|
* Waits for ongoing queries to finish. Streams must be exhausted or closed
|
|
671
680
|
* before awaiting this command. When consuming a stream with `for await`,
|
|
672
681
|
* exit the loop before committing.
|
|
@@ -695,8 +704,17 @@ class ControlledTransaction extends Transaction {
|
|
|
695
704
|
commit() {
|
|
696
705
|
return new Command(async () => {
|
|
697
706
|
await this.#props.executor.provideConnection(async (connection) => {
|
|
698
|
-
|
|
699
|
-
|
|
707
|
+
try {
|
|
708
|
+
await this.#props.driver.commitTransaction(connection);
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
if ((0, aborted_transaction_error_js_1.isAbortedTransactionError)(error)) {
|
|
712
|
+
this.#props.transactionState.status = 'aborted';
|
|
713
|
+
this.#props.connection.release();
|
|
714
|
+
}
|
|
715
|
+
throw error;
|
|
716
|
+
}
|
|
717
|
+
this.#props.transactionState.status = 'committed';
|
|
700
718
|
this.#props.connection.release();
|
|
701
719
|
});
|
|
702
720
|
});
|
|
@@ -704,6 +722,9 @@ class ControlledTransaction extends Transaction {
|
|
|
704
722
|
/**
|
|
705
723
|
* Rolls back the transaction.
|
|
706
724
|
*
|
|
725
|
+
* If a commit already reported that the database rolled back the transaction,
|
|
726
|
+
* succeeds without sending another rollback command.
|
|
727
|
+
*
|
|
707
728
|
* The connection is released even if the rollback fails, so the transaction
|
|
708
729
|
* cannot be used afterwards in either case.
|
|
709
730
|
*
|
|
@@ -734,13 +755,16 @@ class ControlledTransaction extends Transaction {
|
|
|
734
755
|
*/
|
|
735
756
|
rollback() {
|
|
736
757
|
return new Command(async () => {
|
|
758
|
+
if (this.#props.transactionState.status === 'aborted') {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
737
761
|
await this.#props.executor.provideConnection(async (connection) => {
|
|
738
762
|
// A failed rollback can't be meaningfully retried (e.g. dead connection).
|
|
739
763
|
try {
|
|
740
764
|
await this.#props.driver.rollbackTransaction(connection);
|
|
741
765
|
}
|
|
742
766
|
finally {
|
|
743
|
-
this.#props.
|
|
767
|
+
this.#props.transactionState.status = 'rolled back';
|
|
744
768
|
this.#props.connection.release();
|
|
745
769
|
}
|
|
746
770
|
});
|
|
@@ -912,11 +936,11 @@ class Command {
|
|
|
912
936
|
}
|
|
913
937
|
}
|
|
914
938
|
exports.Command = Command;
|
|
915
|
-
function assertNotCommittedOrRolledBack(
|
|
916
|
-
if (
|
|
939
|
+
function assertNotCommittedOrRolledBack(transactionState) {
|
|
940
|
+
if (transactionState.status === 'committed') {
|
|
917
941
|
throw new Error('Transaction is already committed');
|
|
918
942
|
}
|
|
919
|
-
if (
|
|
943
|
+
if (transactionState.status !== 'active') {
|
|
920
944
|
throw new Error('Transaction is already rolled back');
|
|
921
945
|
}
|
|
922
946
|
}
|
|
@@ -928,15 +952,13 @@ function assertNotCommittedOrRolledBack(state) {
|
|
|
928
952
|
*/
|
|
929
953
|
class NotCommittedOrRolledBackAssertingExecutor {
|
|
930
954
|
#executor;
|
|
931
|
-
#
|
|
932
|
-
constructor(executor,
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
this.#state = state;
|
|
955
|
+
#transactionState;
|
|
956
|
+
constructor(executor, transactionState) {
|
|
957
|
+
this.#executor =
|
|
958
|
+
executor instanceof NotCommittedOrRolledBackAssertingExecutor
|
|
959
|
+
? executor.#executor
|
|
960
|
+
: executor;
|
|
961
|
+
this.#transactionState = transactionState;
|
|
940
962
|
}
|
|
941
963
|
get adapter() {
|
|
942
964
|
return this.#executor.adapter;
|
|
@@ -951,37 +973,37 @@ class NotCommittedOrRolledBackAssertingExecutor {
|
|
|
951
973
|
return this.#executor.compileQuery(node, queryId);
|
|
952
974
|
}
|
|
953
975
|
provideConnection(consumer) {
|
|
954
|
-
assertNotCommittedOrRolledBack(this.#
|
|
976
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
955
977
|
return this.#executor.provideConnection(async (connection) => {
|
|
956
|
-
assertNotCommittedOrRolledBack(this.#
|
|
978
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
957
979
|
return await consumer(connection);
|
|
958
980
|
});
|
|
959
981
|
}
|
|
960
982
|
executeQuery(compiledQuery) {
|
|
961
|
-
assertNotCommittedOrRolledBack(this.#
|
|
983
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
962
984
|
return this.#executor
|
|
963
985
|
.withConnectionProvider(this)
|
|
964
986
|
.executeQuery(compiledQuery);
|
|
965
987
|
}
|
|
966
988
|
stream(compiledQuery, chunkSize) {
|
|
967
|
-
assertNotCommittedOrRolledBack(this.#
|
|
989
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
968
990
|
return this.#executor
|
|
969
991
|
.withConnectionProvider(this)
|
|
970
992
|
.stream(compiledQuery, chunkSize);
|
|
971
993
|
}
|
|
972
994
|
withConnectionProvider(connectionProvider) {
|
|
973
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withConnectionProvider(connectionProvider), this.#
|
|
995
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withConnectionProvider(connectionProvider), this.#transactionState);
|
|
974
996
|
}
|
|
975
997
|
withPlugin(plugin) {
|
|
976
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugin(plugin), this.#
|
|
998
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugin(plugin), this.#transactionState);
|
|
977
999
|
}
|
|
978
1000
|
withPlugins(plugins) {
|
|
979
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugins(plugins), this.#
|
|
1001
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugins(plugins), this.#transactionState);
|
|
980
1002
|
}
|
|
981
1003
|
withPluginAtFront(plugin) {
|
|
982
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPluginAtFront(plugin), this.#
|
|
1004
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPluginAtFront(plugin), this.#transactionState);
|
|
983
1005
|
}
|
|
984
1006
|
withoutPlugins() {
|
|
985
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withoutPlugins(), this.#
|
|
1007
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withoutPlugins(), this.#transactionState);
|
|
986
1008
|
}
|
|
987
1009
|
}
|
|
@@ -25,6 +25,9 @@ export declare class MssqlDriver implements Driver {
|
|
|
25
25
|
beginTransaction(connection: MssqlConnection, settings: TransactionSettings): Promise<void>;
|
|
26
26
|
/**
|
|
27
27
|
* Commits a transaction.
|
|
28
|
+
*
|
|
29
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
30
|
+
* the transaction was rolled back instead of committed.
|
|
28
31
|
*/
|
|
29
32
|
commitTransaction(connection: MssqlConnection): Promise<void>;
|
|
30
33
|
/**
|
|
@@ -24,6 +24,9 @@ export declare class MysqlDriver implements Driver {
|
|
|
24
24
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
25
25
|
/**
|
|
26
26
|
* Commits a transaction.
|
|
27
|
+
*
|
|
28
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
29
|
+
* the transaction was rolled back instead of committed.
|
|
27
30
|
*/
|
|
28
31
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
29
32
|
/**
|
|
@@ -61,7 +61,7 @@ export interface PostgresPool {
|
|
|
61
61
|
export interface PostgresPoolClient {
|
|
62
62
|
query<R>(sql: string, parameters: ReadonlyArray<unknown>): Promise<PostgresQueryResult<R>>;
|
|
63
63
|
query<R>(cursor: PostgresCursor<R>): PostgresCursor<R>;
|
|
64
|
-
release(): void;
|
|
64
|
+
release(destroy?: boolean): void;
|
|
65
65
|
}
|
|
66
66
|
export interface PostgresCursor<T> {
|
|
67
67
|
read(rowsCount: number): Promise<T[]>;
|
|
@@ -69,7 +69,7 @@ export interface PostgresCursor<T> {
|
|
|
69
69
|
}
|
|
70
70
|
export type PostgresCursorConstructor = new <T>(sql: string, parameters: unknown[]) => PostgresCursor<T>;
|
|
71
71
|
export interface PostgresQueryResult<R> {
|
|
72
|
-
command: 'UPDATE' | 'DELETE' | 'INSERT' | 'SELECT' | 'MERGE';
|
|
72
|
+
command: 'UPDATE' | 'DELETE' | 'INSERT' | 'SELECT' | 'MERGE' | 'COMMIT' | 'ROLLBACK';
|
|
73
73
|
rowCount: number;
|
|
74
74
|
rows: R[];
|
|
75
75
|
}
|
|
@@ -4,6 +4,7 @@ import { CompiledQuery } from '../../query-compiler/compiled-query.js';
|
|
|
4
4
|
import type { QueryCompiler } from '../../query-compiler/query-compiler.js';
|
|
5
5
|
import type { PostgresCursorConstructor, PostgresDialectConfig, PostgresPoolClient } from './postgres-dialect-config.js';
|
|
6
6
|
declare const PRIVATE_RELEASE_METHOD: unique symbol;
|
|
7
|
+
declare const PRIVATE_MARK_FOR_DISCARD_METHOD: unique symbol;
|
|
7
8
|
export declare class PostgresDriver implements Driver {
|
|
8
9
|
#private;
|
|
9
10
|
constructor(config: PostgresDialectConfig);
|
|
@@ -24,12 +25,15 @@ export declare class PostgresDriver implements Driver {
|
|
|
24
25
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
25
26
|
/**
|
|
26
27
|
* Commits a transaction.
|
|
28
|
+
*
|
|
29
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
30
|
+
* the transaction was rolled back instead of committed.
|
|
27
31
|
*/
|
|
28
32
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
29
33
|
/**
|
|
30
34
|
* Rolls back a transaction.
|
|
31
35
|
*/
|
|
32
|
-
rollbackTransaction(connection:
|
|
36
|
+
rollbackTransaction(connection: PostgresConnection): Promise<void>;
|
|
33
37
|
savepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
34
38
|
rollbackToSavepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
35
39
|
releaseSavepoint(connection: DatabaseConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
@@ -51,5 +55,6 @@ declare class PostgresConnection implements DatabaseConnection {
|
|
|
51
55
|
executeQuery<O>(compiledQuery: CompiledQuery): Promise<QueryResult<O>>;
|
|
52
56
|
streamQuery<O>(compiledQuery: CompiledQuery, chunkSize: number): AsyncIterableIterator<QueryResult<O>>;
|
|
53
57
|
[PRIVATE_RELEASE_METHOD](): void;
|
|
58
|
+
[PRIVATE_MARK_FOR_DISCARD_METHOD](): void;
|
|
54
59
|
}
|
|
55
60
|
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/// <reference types="./postgres-driver.d.ts" />
|
|
2
|
+
import { AbortedTransactionError } from '../../driver/aborted-transaction-error.js';
|
|
2
3
|
import { parseSavepointCommand } from '../../parser/savepoint-parser.js';
|
|
3
4
|
import { CompiledQuery } from '../../query-compiler/compiled-query.js';
|
|
4
5
|
import { isFunction, freeze } from '../../util/object-utils.js';
|
|
5
6
|
import { createQueryId } from '../../util/query-id.js';
|
|
6
7
|
import { extendStackTrace } from '../../util/stack-trace-utils.js';
|
|
7
8
|
const PRIVATE_RELEASE_METHOD = Symbol();
|
|
9
|
+
const PRIVATE_MARK_FOR_DISCARD_METHOD = Symbol();
|
|
8
10
|
export class PostgresDriver {
|
|
9
11
|
#config;
|
|
10
12
|
#connections = new WeakMap();
|
|
@@ -58,10 +60,21 @@ export class PostgresDriver {
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
async commitTransaction(connection) {
|
|
61
|
-
await connection.executeQuery(CompiledQuery.raw('commit'));
|
|
63
|
+
const { rows } = await connection.executeQuery(CompiledQuery.raw('commit'));
|
|
64
|
+
// PostgreSQL completes COMMIT with a ROLLBACK tag if an earlier
|
|
65
|
+
// statement error left the transaction aborted.
|
|
66
|
+
if (rows[0]?.command === 'ROLLBACK') {
|
|
67
|
+
throw new AbortedTransactionError();
|
|
68
|
+
}
|
|
62
69
|
}
|
|
63
70
|
async rollbackTransaction(connection) {
|
|
64
|
-
|
|
71
|
+
try {
|
|
72
|
+
await connection.executeQuery(CompiledQuery.raw('rollback'));
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
connection[PRIVATE_MARK_FOR_DISCARD_METHOD]();
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
65
78
|
}
|
|
66
79
|
async savepoint(connection, savepointName, compileQuery) {
|
|
67
80
|
await connection.executeQuery(compileQuery(parseSavepointCommand('savepoint', savepointName), createQueryId()));
|
|
@@ -86,13 +99,18 @@ export class PostgresDriver {
|
|
|
86
99
|
class PostgresConnection {
|
|
87
100
|
#client;
|
|
88
101
|
#options;
|
|
102
|
+
#discarded;
|
|
89
103
|
constructor(client, options) {
|
|
90
104
|
this.#client = client;
|
|
91
105
|
this.#options = options;
|
|
92
106
|
}
|
|
93
107
|
async executeQuery(compiledQuery) {
|
|
108
|
+
this.#assertNotDiscarded();
|
|
94
109
|
try {
|
|
95
110
|
const { command, rowCount, rows } = await this.#client.query(compiledQuery.sql, [...compiledQuery.parameters]);
|
|
111
|
+
if (command === 'ROLLBACK' || command === 'COMMIT') {
|
|
112
|
+
return { rows: [{ command }] };
|
|
113
|
+
}
|
|
96
114
|
return {
|
|
97
115
|
numAffectedRows: command === 'INSERT' ||
|
|
98
116
|
command === 'UPDATE' ||
|
|
@@ -114,6 +132,7 @@ class PostgresConnection {
|
|
|
114
132
|
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
|
|
115
133
|
throw new Error('chunkSize must be a positive integer');
|
|
116
134
|
}
|
|
135
|
+
this.#assertNotDiscarded();
|
|
117
136
|
const cursor = this.#client.query(new this.#options.cursor(compiledQuery.sql, compiledQuery.parameters.slice()));
|
|
118
137
|
try {
|
|
119
138
|
while (true) {
|
|
@@ -131,6 +150,14 @@ class PostgresConnection {
|
|
|
131
150
|
}
|
|
132
151
|
}
|
|
133
152
|
[PRIVATE_RELEASE_METHOD]() {
|
|
134
|
-
this.#client.release();
|
|
153
|
+
this.#client.release(Boolean(this.#discarded));
|
|
154
|
+
}
|
|
155
|
+
[PRIVATE_MARK_FOR_DISCARD_METHOD]() {
|
|
156
|
+
this.#discarded = true;
|
|
157
|
+
}
|
|
158
|
+
#assertNotDiscarded() {
|
|
159
|
+
if (this.#discarded) {
|
|
160
|
+
throw new Error('Postgres connection has been discarded');
|
|
161
|
+
}
|
|
135
162
|
}
|
|
136
163
|
}
|
|
@@ -22,6 +22,9 @@ export declare class SqliteDriver implements Driver {
|
|
|
22
22
|
beginTransaction(connection: DatabaseConnection): Promise<void>;
|
|
23
23
|
/**
|
|
24
24
|
* Commits a transaction.
|
|
25
|
+
*
|
|
26
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
27
|
+
* the transaction was rolled back instead of committed.
|
|
25
28
|
*/
|
|
26
29
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
27
30
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const ABORTED_TRANSACTION_ERROR: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when the database rolls back a transaction instead of committing it.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AbortedTransactionError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
readonly [ABORTED_TRANSACTION_ERROR] = true;
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare function isAbortedTransactionError(error: unknown): error is AbortedTransactionError;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="./aborted-transaction-error.d.ts" />
|
|
2
|
+
import { isObject } from '../util/object-utils.js';
|
|
3
|
+
const ABORTED_TRANSACTION_ERROR = Symbol.for('kysely.AbortedTransactionError');
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when the database rolls back a transaction instead of committing it.
|
|
6
|
+
*/
|
|
7
|
+
export class AbortedTransactionError extends Error {
|
|
8
|
+
name = 'AbortedTransactionError';
|
|
9
|
+
[ABORTED_TRANSACTION_ERROR] = true;
|
|
10
|
+
constructor() {
|
|
11
|
+
super('Transaction was rolled back instead of committed');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function isAbortedTransactionError(error) {
|
|
15
|
+
return isObject(error) && ABORTED_TRANSACTION_ERROR in error;
|
|
16
|
+
}
|
|
@@ -23,6 +23,9 @@ export interface Driver {
|
|
|
23
23
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
24
24
|
/**
|
|
25
25
|
* Commits a transaction.
|
|
26
|
+
*
|
|
27
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
28
|
+
* the transaction was rolled back instead of committed.
|
|
26
29
|
*/
|
|
27
30
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
28
31
|
/**
|
|
@@ -55,6 +55,9 @@ export declare class DummyDriver implements Driver {
|
|
|
55
55
|
beginTransaction(): Promise<void>;
|
|
56
56
|
/**
|
|
57
57
|
* Commits a transaction.
|
|
58
|
+
*
|
|
59
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
60
|
+
* the transaction was rolled back instead of committed.
|
|
58
61
|
*/
|
|
59
62
|
commitTransaction(): Promise<void>;
|
|
60
63
|
/**
|
|
@@ -31,6 +31,9 @@ export declare class RuntimeDriver implements Driver {
|
|
|
31
31
|
beginTransaction(connection: DatabaseConnection, settings: TransactionSettings): Promise<void>;
|
|
32
32
|
/**
|
|
33
33
|
* Commits a transaction.
|
|
34
|
+
*
|
|
35
|
+
* Throws {@link AbortedTransactionError} if the database confirms that
|
|
36
|
+
* the transaction was rolled back instead of committed.
|
|
34
37
|
*/
|
|
35
38
|
commitTransaction(connection: DatabaseConnection): Promise<void>;
|
|
36
39
|
/**
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export * from './dynamic/dynamic.js';
|
|
|
57
57
|
export * from './dynamic/dynamic-reference-builder.js';
|
|
58
58
|
export * from './dynamic/dynamic-table-builder.js';
|
|
59
59
|
export * from './driver/driver.js';
|
|
60
|
+
export * from './driver/aborted-transaction-error.js';
|
|
60
61
|
export * from './driver/database-connection.js';
|
|
61
62
|
export * from './driver/connection-provider.js';
|
|
62
63
|
export * from './driver/default-connection-provider.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -58,6 +58,7 @@ export * from './dynamic/dynamic.js';
|
|
|
58
58
|
export * from './dynamic/dynamic-reference-builder.js';
|
|
59
59
|
export * from './dynamic/dynamic-table-builder.js';
|
|
60
60
|
export * from './driver/driver.js';
|
|
61
|
+
export * from './driver/aborted-transaction-error.js';
|
|
61
62
|
export * from './driver/database-connection.js';
|
|
62
63
|
export * from './driver/connection-provider.js';
|
|
63
64
|
export * from './driver/default-connection-provider.js';
|
package/dist/esm/kysely.d.ts
CHANGED
|
@@ -665,6 +665,9 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
665
665
|
/**
|
|
666
666
|
* Commits the transaction.
|
|
667
667
|
*
|
|
668
|
+
* If the database rolls back instead of committing, marks the transaction
|
|
669
|
+
* as rolled back, releases the connection and throws {@link AbortedTransactionError}.
|
|
670
|
+
*
|
|
668
671
|
* Waits for ongoing queries to finish. Streams must be exhausted or closed
|
|
669
672
|
* before awaiting this command. When consuming a stream with `for await`,
|
|
670
673
|
* exit the loop before committing.
|
|
@@ -694,6 +697,9 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
694
697
|
/**
|
|
695
698
|
* Rolls back the transaction.
|
|
696
699
|
*
|
|
700
|
+
* If a commit already reported that the database rolled back the transaction,
|
|
701
|
+
* succeeds without sending another rollback command.
|
|
702
|
+
*
|
|
697
703
|
* The connection is released even if the rollback fails, so the transaction
|
|
698
704
|
* cannot be used afterwards in either case.
|
|
699
705
|
*
|
|
@@ -876,13 +882,12 @@ export declare class ControlledTransaction<DB, S extends string[] = []> extends
|
|
|
876
882
|
*/
|
|
877
883
|
withTables<T extends Record<string, Record<string, any>>>(): ControlledTransaction<DrainOuterGeneric<DB & T>, S>;
|
|
878
884
|
}
|
|
879
|
-
interface
|
|
880
|
-
|
|
881
|
-
isRolledBack: boolean;
|
|
885
|
+
interface ControlledTransactionState {
|
|
886
|
+
status: 'active' | 'committed' | 'rolled back' | 'aborted';
|
|
882
887
|
}
|
|
883
888
|
interface ControlledTransactionProps extends KyselyProps {
|
|
884
889
|
readonly connection: ControlledConnection;
|
|
885
|
-
readonly
|
|
890
|
+
readonly transactionState?: ControlledTransactionState;
|
|
886
891
|
}
|
|
887
892
|
export declare class Command<T> {
|
|
888
893
|
#private;
|
package/dist/esm/kysely.js
CHANGED
|
@@ -6,6 +6,7 @@ import { QueryCreator } from './query-creator.js';
|
|
|
6
6
|
import { DefaultQueryExecutor } from './query-executor/default-query-executor.js';
|
|
7
7
|
import { freeze, isObject, isUndefined } from './util/object-utils.js';
|
|
8
8
|
import { RuntimeDriver } from './driver/runtime-driver.js';
|
|
9
|
+
import { isAbortedTransactionError, } from './driver/aborted-transaction-error.js';
|
|
9
10
|
import { SingleConnectionProvider } from './driver/single-connection-provider.js';
|
|
10
11
|
import { validateTransactionSettings, } from './driver/driver.js';
|
|
11
12
|
import { createFunctionModule, } from './query-builder/function-module.js';
|
|
@@ -569,8 +570,10 @@ export class TransactionBuilder {
|
|
|
569
570
|
const settings = { isolationLevel, accessMode };
|
|
570
571
|
validateTransactionSettings(settings);
|
|
571
572
|
return this.#props.executor.provideConnection(async (connection) => {
|
|
572
|
-
const
|
|
573
|
-
|
|
573
|
+
const transactionState = {
|
|
574
|
+
status: 'active',
|
|
575
|
+
};
|
|
576
|
+
const executor = new NotCommittedOrRolledBackAssertingExecutor(this.#props.executor.withConnectionProvider(new SingleConnectionProvider(connection)), transactionState);
|
|
574
577
|
const transaction = new Transaction({
|
|
575
578
|
...kyselyProps,
|
|
576
579
|
executor,
|
|
@@ -581,13 +584,16 @@ export class TransactionBuilder {
|
|
|
581
584
|
transactionBegun = true;
|
|
582
585
|
const result = await callback(transaction);
|
|
583
586
|
await this.#props.driver.commitTransaction(connection);
|
|
584
|
-
|
|
587
|
+
transactionState.status = 'committed';
|
|
585
588
|
return result;
|
|
586
589
|
}
|
|
587
590
|
catch (error) {
|
|
588
|
-
if (
|
|
591
|
+
if (isAbortedTransactionError(error)) {
|
|
592
|
+
transactionState.status = 'aborted';
|
|
593
|
+
}
|
|
594
|
+
else if (transactionBegun) {
|
|
589
595
|
await this.#props.driver.rollbackTransaction(connection);
|
|
590
|
-
|
|
596
|
+
transactionState.status = 'rolled back';
|
|
591
597
|
}
|
|
592
598
|
throw error;
|
|
593
599
|
}
|
|
@@ -626,7 +632,7 @@ export class ControlledTransactionBuilder {
|
|
|
626
632
|
return new ControlledTransaction({
|
|
627
633
|
...props,
|
|
628
634
|
connection,
|
|
629
|
-
|
|
635
|
+
transactionState: { status: 'active' },
|
|
630
636
|
executor: this.#props.executor.withConnectionProvider(new SingleConnectionProvider(connection.connection)),
|
|
631
637
|
});
|
|
632
638
|
}
|
|
@@ -635,30 +641,33 @@ export class ControlledTransaction extends Transaction {
|
|
|
635
641
|
#props;
|
|
636
642
|
#compileQuery;
|
|
637
643
|
constructor(props) {
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
isRolledBack: false,
|
|
644
|
+
const controlledState = props.transactionState ?? {
|
|
645
|
+
status: 'active',
|
|
641
646
|
};
|
|
642
647
|
const controlledProps = {
|
|
643
648
|
...props,
|
|
644
|
-
|
|
645
|
-
executor: new NotCommittedOrRolledBackAssertingExecutor(props.executor,
|
|
649
|
+
transactionState: controlledState,
|
|
650
|
+
executor: new NotCommittedOrRolledBackAssertingExecutor(props.executor, controlledState),
|
|
646
651
|
};
|
|
647
|
-
const { connection,
|
|
652
|
+
const { connection, transactionState, ...transactionProps } = controlledProps;
|
|
648
653
|
super(transactionProps);
|
|
649
654
|
this.#props = freeze(controlledProps);
|
|
650
655
|
const queryId = createQueryId();
|
|
651
656
|
this.#compileQuery = (node) => controlledProps.executor.compileQuery(node, queryId);
|
|
652
657
|
}
|
|
653
658
|
get isCommitted() {
|
|
654
|
-
return this.#props.
|
|
659
|
+
return this.#props.transactionState.status === 'committed';
|
|
655
660
|
}
|
|
656
661
|
get isRolledBack() {
|
|
657
|
-
return this.#props.
|
|
662
|
+
return (this.#props.transactionState.status === 'rolled back' ||
|
|
663
|
+
this.#props.transactionState.status === 'aborted');
|
|
658
664
|
}
|
|
659
665
|
/**
|
|
660
666
|
* Commits the transaction.
|
|
661
667
|
*
|
|
668
|
+
* If the database rolls back instead of committing, marks the transaction
|
|
669
|
+
* as rolled back, releases the connection and throws {@link AbortedTransactionError}.
|
|
670
|
+
*
|
|
662
671
|
* Waits for ongoing queries to finish. Streams must be exhausted or closed
|
|
663
672
|
* before awaiting this command. When consuming a stream with `for await`,
|
|
664
673
|
* exit the loop before committing.
|
|
@@ -687,8 +696,17 @@ export class ControlledTransaction extends Transaction {
|
|
|
687
696
|
commit() {
|
|
688
697
|
return new Command(async () => {
|
|
689
698
|
await this.#props.executor.provideConnection(async (connection) => {
|
|
690
|
-
|
|
691
|
-
|
|
699
|
+
try {
|
|
700
|
+
await this.#props.driver.commitTransaction(connection);
|
|
701
|
+
}
|
|
702
|
+
catch (error) {
|
|
703
|
+
if (isAbortedTransactionError(error)) {
|
|
704
|
+
this.#props.transactionState.status = 'aborted';
|
|
705
|
+
this.#props.connection.release();
|
|
706
|
+
}
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
this.#props.transactionState.status = 'committed';
|
|
692
710
|
this.#props.connection.release();
|
|
693
711
|
});
|
|
694
712
|
});
|
|
@@ -696,6 +714,9 @@ export class ControlledTransaction extends Transaction {
|
|
|
696
714
|
/**
|
|
697
715
|
* Rolls back the transaction.
|
|
698
716
|
*
|
|
717
|
+
* If a commit already reported that the database rolled back the transaction,
|
|
718
|
+
* succeeds without sending another rollback command.
|
|
719
|
+
*
|
|
699
720
|
* The connection is released even if the rollback fails, so the transaction
|
|
700
721
|
* cannot be used afterwards in either case.
|
|
701
722
|
*
|
|
@@ -726,13 +747,16 @@ export class ControlledTransaction extends Transaction {
|
|
|
726
747
|
*/
|
|
727
748
|
rollback() {
|
|
728
749
|
return new Command(async () => {
|
|
750
|
+
if (this.#props.transactionState.status === 'aborted') {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
729
753
|
await this.#props.executor.provideConnection(async (connection) => {
|
|
730
754
|
// A failed rollback can't be meaningfully retried (e.g. dead connection).
|
|
731
755
|
try {
|
|
732
756
|
await this.#props.driver.rollbackTransaction(connection);
|
|
733
757
|
}
|
|
734
758
|
finally {
|
|
735
|
-
this.#props.
|
|
759
|
+
this.#props.transactionState.status = 'rolled back';
|
|
736
760
|
this.#props.connection.release();
|
|
737
761
|
}
|
|
738
762
|
});
|
|
@@ -902,11 +926,11 @@ export class Command {
|
|
|
902
926
|
return await this.#cb();
|
|
903
927
|
}
|
|
904
928
|
}
|
|
905
|
-
function assertNotCommittedOrRolledBack(
|
|
906
|
-
if (
|
|
929
|
+
function assertNotCommittedOrRolledBack(transactionState) {
|
|
930
|
+
if (transactionState.status === 'committed') {
|
|
907
931
|
throw new Error('Transaction is already committed');
|
|
908
932
|
}
|
|
909
|
-
if (
|
|
933
|
+
if (transactionState.status !== 'active') {
|
|
910
934
|
throw new Error('Transaction is already rolled back');
|
|
911
935
|
}
|
|
912
936
|
}
|
|
@@ -918,15 +942,13 @@ function assertNotCommittedOrRolledBack(state) {
|
|
|
918
942
|
*/
|
|
919
943
|
class NotCommittedOrRolledBackAssertingExecutor {
|
|
920
944
|
#executor;
|
|
921
|
-
#
|
|
922
|
-
constructor(executor,
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
this.#state = state;
|
|
945
|
+
#transactionState;
|
|
946
|
+
constructor(executor, transactionState) {
|
|
947
|
+
this.#executor =
|
|
948
|
+
executor instanceof NotCommittedOrRolledBackAssertingExecutor
|
|
949
|
+
? executor.#executor
|
|
950
|
+
: executor;
|
|
951
|
+
this.#transactionState = transactionState;
|
|
930
952
|
}
|
|
931
953
|
get adapter() {
|
|
932
954
|
return this.#executor.adapter;
|
|
@@ -941,37 +963,37 @@ class NotCommittedOrRolledBackAssertingExecutor {
|
|
|
941
963
|
return this.#executor.compileQuery(node, queryId);
|
|
942
964
|
}
|
|
943
965
|
provideConnection(consumer) {
|
|
944
|
-
assertNotCommittedOrRolledBack(this.#
|
|
966
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
945
967
|
return this.#executor.provideConnection(async (connection) => {
|
|
946
|
-
assertNotCommittedOrRolledBack(this.#
|
|
968
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
947
969
|
return await consumer(connection);
|
|
948
970
|
});
|
|
949
971
|
}
|
|
950
972
|
executeQuery(compiledQuery) {
|
|
951
|
-
assertNotCommittedOrRolledBack(this.#
|
|
973
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
952
974
|
return this.#executor
|
|
953
975
|
.withConnectionProvider(this)
|
|
954
976
|
.executeQuery(compiledQuery);
|
|
955
977
|
}
|
|
956
978
|
stream(compiledQuery, chunkSize) {
|
|
957
|
-
assertNotCommittedOrRolledBack(this.#
|
|
979
|
+
assertNotCommittedOrRolledBack(this.#transactionState);
|
|
958
980
|
return this.#executor
|
|
959
981
|
.withConnectionProvider(this)
|
|
960
982
|
.stream(compiledQuery, chunkSize);
|
|
961
983
|
}
|
|
962
984
|
withConnectionProvider(connectionProvider) {
|
|
963
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withConnectionProvider(connectionProvider), this.#
|
|
985
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withConnectionProvider(connectionProvider), this.#transactionState);
|
|
964
986
|
}
|
|
965
987
|
withPlugin(plugin) {
|
|
966
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugin(plugin), this.#
|
|
988
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugin(plugin), this.#transactionState);
|
|
967
989
|
}
|
|
968
990
|
withPlugins(plugins) {
|
|
969
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugins(plugins), this.#
|
|
991
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPlugins(plugins), this.#transactionState);
|
|
970
992
|
}
|
|
971
993
|
withPluginAtFront(plugin) {
|
|
972
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPluginAtFront(plugin), this.#
|
|
994
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withPluginAtFront(plugin), this.#transactionState);
|
|
973
995
|
}
|
|
974
996
|
withoutPlugins() {
|
|
975
|
-
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withoutPlugins(), this.#
|
|
997
|
+
return new NotCommittedOrRolledBackAssertingExecutor(this.#executor.withoutPlugins(), this.#transactionState);
|
|
976
998
|
}
|
|
977
999
|
}
|