knex 3.2.3 → 3.2.5
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 +2447 -2441
- package/CONTRIBUTING.md +190 -190
- package/LICENSE +22 -22
- package/README.md +177 -156
- package/UPGRADING.md +245 -245
- package/bin/cli.js +516 -516
- package/bin/knexfile-runtime-error.js +27 -27
- package/bin/utils/cli-config-utils.js +217 -217
- package/bin/utils/constants.js +7 -7
- package/bin/utils/migrationsLister.js +37 -37
- package/knex.js +23 -23
- package/knex.mjs +11 -11
- package/lib/builder-interface-augmenter.js +120 -120
- package/lib/client.js +585 -585
- package/lib/constants.js +61 -61
- package/lib/dialects/better-sqlite3/index.js +101 -101
- package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
- package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
- package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
- package/lib/dialects/cockroachdb/crdb-tablecompiler.js +46 -46
- package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
- package/lib/dialects/cockroachdb/index.js +86 -86
- package/lib/dialects/index.js +34 -34
- package/lib/dialects/mssql/index.js +498 -498
- package/lib/dialects/mssql/mssql-formatter.js +34 -34
- package/lib/dialects/mssql/query/mssql-querycompiler.js +601 -601
- package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
- package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
- package/lib/dialects/mssql/schema/mssql-tablecompiler.js +393 -393
- package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
- package/lib/dialects/mssql/transaction.js +176 -176
- package/lib/dialects/mysql/index.js +317 -317
- package/lib/dialects/mysql/query/mysql-querybuilder.js +14 -14
- package/lib/dialects/mysql/query/mysql-querycompiler.js +292 -292
- package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
- package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
- package/lib/dialects/mysql/schema/mysql-tablecompiler.js +426 -426
- package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
- package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
- package/lib/dialects/mysql/transaction.js +46 -46
- package/lib/dialects/mysql2/index.js +53 -53
- package/lib/dialects/mysql2/transaction.js +44 -44
- package/lib/dialects/oracle/DEAD_CODE.md +5 -5
- package/lib/dialects/oracle/index.js +92 -92
- package/lib/dialects/oracle/query/oracle-querycompiler.js +343 -343
- package/lib/dialects/oracle/schema/internal/incrementUtils.js +22 -22
- package/lib/dialects/oracle/schema/internal/trigger.js +155 -155
- package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
- package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
- package/lib/dialects/oracle/schema/oracle-compiler.js +124 -124
- package/lib/dialects/oracle/schema/oracle-tablecompiler.js +210 -210
- package/lib/dialects/oracle/utils.js +107 -107
- package/lib/dialects/oracledb/index.js +381 -381
- package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
- package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +61 -61
- package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
- package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
- package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
- package/lib/dialects/oracledb/transaction.js +98 -98
- package/lib/dialects/oracledb/utils.js +208 -208
- package/lib/dialects/pgnative/index.js +60 -60
- package/lib/dialects/postgres/execution/pg-transaction.js +19 -19
- package/lib/dialects/postgres/index.js +373 -373
- package/lib/dialects/postgres/query/pg-querybuilder.js +43 -43
- package/lib/dialects/postgres/query/pg-querycompiler.js +400 -400
- package/lib/dialects/postgres/schema/pg-columncompiler.js +162 -162
- package/lib/dialects/postgres/schema/pg-compiler.js +138 -138
- package/lib/dialects/postgres/schema/pg-tablecompiler.js +331 -331
- package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
- package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
- package/lib/dialects/redshift/index.js +86 -86
- package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
- package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
- package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
- package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
- package/lib/dialects/redshift/schema/redshift-tablecompiler.js +134 -134
- package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
- package/lib/dialects/redshift/transaction.js +32 -32
- package/lib/dialects/sqlite3/execution/sqlite-transaction.js +172 -172
- package/lib/dialects/sqlite3/index.js +263 -263
- package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
- package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +341 -341
- package/lib/dialects/sqlite3/schema/ddl.js +380 -380
- package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
- package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
- package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
- package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
- package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
- package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
- package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
- package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
- package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +364 -364
- package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
- package/lib/execution/batch-insert.js +51 -51
- package/lib/execution/internal/delay.js +6 -6
- package/lib/execution/internal/ensure-connection-callback.js +41 -41
- package/lib/execution/internal/query-executioner.js +62 -62
- package/lib/execution/runner.js +325 -325
- package/lib/execution/transaction.js +417 -417
- package/lib/formatter/formatterUtils.js +42 -42
- package/lib/formatter/rawFormatter.js +84 -84
- package/lib/formatter/wrappingFormatter.js +253 -253
- package/lib/formatter.js +25 -25
- package/lib/index.js +3 -3
- package/lib/knex-builder/FunctionHelper.js +80 -80
- package/lib/knex-builder/Knex.js +59 -59
- package/lib/knex-builder/internal/config-resolver.js +57 -57
- package/lib/knex-builder/internal/parse-connection.js +87 -87
- package/lib/knex-builder/make-knex.js +345 -345
- package/lib/logger.js +76 -76
- package/lib/migrations/common/MigrationsLoader.js +36 -36
- package/lib/migrations/migrate/MigrationGenerator.js +84 -84
- package/lib/migrations/migrate/Migrator.js +632 -632
- package/lib/migrations/migrate/migrate-stub.js +17 -17
- package/lib/migrations/migrate/migration-list-resolver.js +33 -33
- package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
- package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
- package/lib/migrations/migrate/stub/cjs.stub +15 -15
- package/lib/migrations/migrate/stub/coffee.stub +13 -13
- package/lib/migrations/migrate/stub/eg.stub +14 -14
- package/lib/migrations/migrate/stub/js-schema.stub +22 -22
- package/lib/migrations/migrate/stub/js.stub +22 -22
- package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
- package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
- package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
- package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
- package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
- package/lib/migrations/migrate/stub/ls.stub +14 -14
- package/lib/migrations/migrate/stub/mjs.stub +23 -23
- package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
- package/lib/migrations/migrate/stub/ts.stub +21 -21
- package/lib/migrations/migrate/table-creator.js +77 -77
- package/lib/migrations/migrate/table-resolver.js +27 -27
- package/lib/migrations/seed/Seeder.js +137 -137
- package/lib/migrations/seed/seed-stub.js +13 -13
- package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
- package/lib/migrations/seed/sources/fs-seeds.js +65 -65
- package/lib/migrations/seed/stub/coffee.stub +9 -9
- package/lib/migrations/seed/stub/eg.stub +11 -11
- package/lib/migrations/seed/stub/js.stub +13 -13
- package/lib/migrations/seed/stub/ls.stub +11 -11
- package/lib/migrations/seed/stub/mjs.stub +12 -12
- package/lib/migrations/seed/stub/ts.stub +13 -13
- package/lib/migrations/util/fs.js +86 -86
- package/lib/migrations/util/import-file.js +12 -12
- package/lib/migrations/util/is-module-type.js +9 -9
- package/lib/migrations/util/template.js +52 -52
- package/lib/migrations/util/timestamp.js +14 -14
- package/lib/query/analytic.js +52 -52
- package/lib/query/constants.js +15 -15
- package/lib/query/joinclause.js +270 -270
- package/lib/query/method-constants.js +136 -136
- package/lib/query/querybuilder.js +1793 -1793
- package/lib/query/querycompiler.js +1634 -1634
- package/lib/raw.js +139 -139
- package/lib/ref.js +39 -39
- package/lib/schema/builder.js +115 -115
- package/lib/schema/columnbuilder.js +146 -146
- package/lib/schema/columncompiler.js +307 -307
- package/lib/schema/compiler.js +187 -187
- package/lib/schema/internal/helpers.js +55 -55
- package/lib/schema/tablebuilder.js +379 -379
- package/lib/schema/tablecompiler.js +450 -450
- package/lib/schema/viewbuilder.js +92 -92
- package/lib/schema/viewcompiler.js +138 -138
- package/lib/util/finally-mixin.js +13 -13
- package/lib/util/helpers.js +95 -95
- package/lib/util/is.js +32 -32
- package/lib/util/nanoid.js +40 -40
- package/lib/util/noop.js +1 -1
- package/lib/util/save-async-stack.js +14 -14
- package/lib/util/security.js +32 -32
- package/lib/util/string.js +190 -190
- package/lib/util/timeout.js +29 -29
- package/package.json +295 -293
- package/scripts/act-testing/act.sh +19 -19
- package/scripts/act-testing/merged-no-label.json +11 -11
- package/scripts/act-testing/merged-patch-labeled.json +12 -12
- package/scripts/act-testing/merged-skip-labeled.json +12 -12
- package/scripts/act-testing/not-merged-patch-labeled.json +12 -12
- package/scripts/build-for-release.sh +121 -121
- package/scripts/build.js +125 -125
- package/scripts/clean.js +31 -31
- package/scripts/docker-compose.yml +150 -150
- package/scripts/format-changelog.js +55 -55
- package/scripts/next-release-howto.md +24 -24
- package/scripts/oracledb-install-driver-libs.sh +82 -82
- package/scripts/release.sh +36 -36
- package/scripts/runkit-example.js +35 -35
- package/scripts/stress-test/README.txt +18 -18
- package/scripts/stress-test/docker-compose.yml +55 -55
- package/scripts/stress-test/knex-stress-test.js +212 -212
- package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +149 -149
- package/scripts/stress-test/mysql2-sudden-exit-without-error.js +101 -101
- package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +188 -188
- package/types/index.d.mts +14 -0
- package/types/index.d.ts +3321 -3321
- package/types/result.d.ts +27 -27
- package/types/tables.d.ts +4 -4
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
const Transaction = require('../../../execution/transaction');
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
isForeignCheckEnabled,
|
|
5
|
-
setForeignCheck,
|
|
6
|
-
executeForeignCheck,
|
|
7
|
-
} = require('../schema/internal/sqlite-ddl-operations');
|
|
8
|
-
|
|
9
|
-
class Transaction_Sqlite extends Transaction {
|
|
10
|
-
// Change the `foreign_keys` pragma if it doesn't match what we want it to be.
|
|
11
|
-
// Return what it should be set to (if anything) when the transaction completes.
|
|
12
|
-
async _setForeignCheck(conn, enforce) {
|
|
13
|
-
// do nothing if we're not explicitly opted in
|
|
14
|
-
if (enforce == null) return null;
|
|
15
|
-
|
|
16
|
-
// see what the current pragma is
|
|
17
|
-
const result = await this.client
|
|
18
|
-
.raw(isForeignCheckEnabled())
|
|
19
|
-
.connection(conn);
|
|
20
|
-
const isEnabled = result[0].foreign_keys === 1;
|
|
21
|
-
|
|
22
|
-
// do nothing if it's already what we require it to be
|
|
23
|
-
if (enforce === isEnabled) return null;
|
|
24
|
-
|
|
25
|
-
// make the change and return what it used to be so we can set it back
|
|
26
|
-
await this.client.raw(setForeignCheck(enforce)).connection(conn);
|
|
27
|
-
return isEnabled;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// When a boolean is supplied, unconditionally set the `foreign_keys` pragma to
|
|
31
|
-
// the given value. Otherwise do nothing.
|
|
32
|
-
async _restoreForeignCheck(conn, enable) {
|
|
33
|
-
if (typeof enable !== 'boolean') return;
|
|
34
|
-
await this.client.raw(setForeignCheck(enable)).connection(conn);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Override Transaction's behavior. Sqlite3 will not error on a `pragma foreign_keys = <value>` statement
|
|
38
|
-
// inside of a transaction; it will just silently not take effect: https://sqlite.org/pragma.html#pragma_foreign_keys
|
|
39
|
-
// To deal with this, we introduce a config option "enforceForeignCheck". When set to a value, Transaction_Sqlite
|
|
40
|
-
// ensures that the transaction received by the caller has this pragma enabled or disabled, and puts it back
|
|
41
|
-
// when the transaction is done.
|
|
42
|
-
async _evaluateContainer(config, container) {
|
|
43
|
-
// this is the same condition used in Transaction._onAcquire() to decide whether to use "BEGIN" or "SAVEPOINT"
|
|
44
|
-
const hasOuterTransaction = this.client.transacting;
|
|
45
|
-
|
|
46
|
-
// this is true when our client was created by Client_SQLite3._strict()
|
|
47
|
-
const strictForeignKeyPragma = this.client.strictForeignKeyPragma;
|
|
48
|
-
|
|
49
|
-
// this comes from the options bag passed to client.transaction()
|
|
50
|
-
// undefined = wasn't set by caller
|
|
51
|
-
// true = ensure foreign_keys pragma is enabled within the transaction
|
|
52
|
-
// false = ensure foreign_keys pragma is disabled within the transaction
|
|
53
|
-
// null = leave it however it already is
|
|
54
|
-
const enforceForeignCheck = config.enforceForeignCheck;
|
|
55
|
-
|
|
56
|
-
// if we're in strict mode, require the caller to be explicit about foreign key
|
|
57
|
-
// constraint requirements
|
|
58
|
-
if (strictForeignKeyPragma === true && enforceForeignCheck === undefined) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
'Refusing to create an unsafe transaction: client.strictForeignKeyPragma is true, but check.enforceForeignCheck is unspecified'
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// call the base class's acquireConnection logic to get ahold of a connection before the transaction is created
|
|
65
|
-
return this.acquireConnection(config, async (conn) => {
|
|
66
|
-
let restoreForeignCheck = undefined;
|
|
67
|
-
try {
|
|
68
|
-
// change the `foreign_keys` pragma if we need to, and decide what we should set it back to, if anything
|
|
69
|
-
restoreForeignCheck = await this._setForeignCheck(
|
|
70
|
-
conn,
|
|
71
|
-
enforceForeignCheck
|
|
72
|
-
);
|
|
73
|
-
} catch (e) {
|
|
74
|
-
// We don't need to dispose the connection here, because none of the things that can throw an error
|
|
75
|
-
// can leave the connection in an unexpected state. Just reject the begin transaction.
|
|
76
|
-
const error = new Error(
|
|
77
|
-
`Refusing to create transaction: failed to set \`foreign_keys\` pragma to the required value of ${enforceForeignCheck}`
|
|
78
|
-
);
|
|
79
|
-
error.cause = e;
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// if:
|
|
84
|
-
// - we're in a nested transaction
|
|
85
|
-
// - _and_ we're in strict mode
|
|
86
|
-
// - _and_ we are required to change the pragma
|
|
87
|
-
// then: we cannot continue, it's out of our hands
|
|
88
|
-
if (
|
|
89
|
-
strictForeignKeyPragma &&
|
|
90
|
-
hasOuterTransaction &&
|
|
91
|
-
restoreForeignCheck !== undefined
|
|
92
|
-
) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`Refusing to create transaction: unable to change \`foreign_keys\` pragma inside a nested transaction`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let maybeWrappedContainer = container;
|
|
99
|
-
if (restoreForeignCheck === true) {
|
|
100
|
-
// in the case where we are turning foreign key checks off for the duration of a transaction,
|
|
101
|
-
// we need to assert that there are no violations once the work of the transaction has been
|
|
102
|
-
// completed. this relies on the fact that Transaction._onAcquire runs the "container" promise
|
|
103
|
-
// to completion before executing "COMMIT"
|
|
104
|
-
maybeWrappedContainer = async (trx) => {
|
|
105
|
-
const res = await container(trx);
|
|
106
|
-
|
|
107
|
-
const foreignViolations = await this.client
|
|
108
|
-
.raw(executeForeignCheck())
|
|
109
|
-
.connection(conn);
|
|
110
|
-
|
|
111
|
-
if (foreignViolations.length > 0) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Transaction concluded with ${foreignViolations.length} foreign key violations`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
return res;
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
// call out to the base class to actually do the work as it normally would
|
|
122
|
-
// note: the await is required here! we need to resolve the promise, not
|
|
123
|
-
// return it
|
|
124
|
-
return await this._onAcquire(maybeWrappedContainer, conn);
|
|
125
|
-
} finally {
|
|
126
|
-
// set the foreign_keys pragma back to what it was before we performed the transaction
|
|
127
|
-
this._restoreForeignCheck(conn, restoreForeignCheck).catch((e) => {
|
|
128
|
-
// we were unable to put it back like we found it. dispose the connection and
|
|
129
|
-
// allow any further queries to re-acquire a new, clean connection
|
|
130
|
-
this._logAndDispose(
|
|
131
|
-
conn,
|
|
132
|
-
'Failed to restore foreign check to expected state',
|
|
133
|
-
e
|
|
134
|
-
);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
_logAndDispose(conn, message, cause) {
|
|
141
|
-
const error = new Error(message);
|
|
142
|
-
error.cause = cause;
|
|
143
|
-
conn.__knex__disposed = error;
|
|
144
|
-
this.client.logger.warn(
|
|
145
|
-
`Transaction_Sqlite: ${message}:\n${
|
|
146
|
-
cause instanceof Error ? cause.message : String(cause)
|
|
147
|
-
}`
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
begin(conn) {
|
|
152
|
-
// SQLite doesn't really support isolation levels, it is serializable by
|
|
153
|
-
// default and so we override it to ignore isolation level.
|
|
154
|
-
// There is a `PRAGMA read_uncommitted = true;`, but that's probably not
|
|
155
|
-
// what the user wants
|
|
156
|
-
if (this.isolationLevel) {
|
|
157
|
-
this.client.logger.warn(
|
|
158
|
-
'sqlite3 only supports serializable transactions, ignoring the isolation level param'
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
// SQLite infers read vs write transactions from the statement operation
|
|
162
|
-
// https://www.sqlite.org/lang_transaction.html#read_transactions_versus_write_transactions
|
|
163
|
-
if (this.readOnly) {
|
|
164
|
-
this.client.logger.warn(
|
|
165
|
-
'sqlite3 implicitly handles read vs write transactions'
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
return this.query(conn, 'BEGIN;');
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
module.exports = Transaction_Sqlite;
|
|
1
|
+
const Transaction = require('../../../execution/transaction');
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
isForeignCheckEnabled,
|
|
5
|
+
setForeignCheck,
|
|
6
|
+
executeForeignCheck,
|
|
7
|
+
} = require('../schema/internal/sqlite-ddl-operations');
|
|
8
|
+
|
|
9
|
+
class Transaction_Sqlite extends Transaction {
|
|
10
|
+
// Change the `foreign_keys` pragma if it doesn't match what we want it to be.
|
|
11
|
+
// Return what it should be set to (if anything) when the transaction completes.
|
|
12
|
+
async _setForeignCheck(conn, enforce) {
|
|
13
|
+
// do nothing if we're not explicitly opted in
|
|
14
|
+
if (enforce == null) return null;
|
|
15
|
+
|
|
16
|
+
// see what the current pragma is
|
|
17
|
+
const result = await this.client
|
|
18
|
+
.raw(isForeignCheckEnabled())
|
|
19
|
+
.connection(conn);
|
|
20
|
+
const isEnabled = result[0].foreign_keys === 1;
|
|
21
|
+
|
|
22
|
+
// do nothing if it's already what we require it to be
|
|
23
|
+
if (enforce === isEnabled) return null;
|
|
24
|
+
|
|
25
|
+
// make the change and return what it used to be so we can set it back
|
|
26
|
+
await this.client.raw(setForeignCheck(enforce)).connection(conn);
|
|
27
|
+
return isEnabled;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// When a boolean is supplied, unconditionally set the `foreign_keys` pragma to
|
|
31
|
+
// the given value. Otherwise do nothing.
|
|
32
|
+
async _restoreForeignCheck(conn, enable) {
|
|
33
|
+
if (typeof enable !== 'boolean') return;
|
|
34
|
+
await this.client.raw(setForeignCheck(enable)).connection(conn);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Override Transaction's behavior. Sqlite3 will not error on a `pragma foreign_keys = <value>` statement
|
|
38
|
+
// inside of a transaction; it will just silently not take effect: https://sqlite.org/pragma.html#pragma_foreign_keys
|
|
39
|
+
// To deal with this, we introduce a config option "enforceForeignCheck". When set to a value, Transaction_Sqlite
|
|
40
|
+
// ensures that the transaction received by the caller has this pragma enabled or disabled, and puts it back
|
|
41
|
+
// when the transaction is done.
|
|
42
|
+
async _evaluateContainer(config, container) {
|
|
43
|
+
// this is the same condition used in Transaction._onAcquire() to decide whether to use "BEGIN" or "SAVEPOINT"
|
|
44
|
+
const hasOuterTransaction = this.client.transacting;
|
|
45
|
+
|
|
46
|
+
// this is true when our client was created by Client_SQLite3._strict()
|
|
47
|
+
const strictForeignKeyPragma = this.client.strictForeignKeyPragma;
|
|
48
|
+
|
|
49
|
+
// this comes from the options bag passed to client.transaction()
|
|
50
|
+
// undefined = wasn't set by caller
|
|
51
|
+
// true = ensure foreign_keys pragma is enabled within the transaction
|
|
52
|
+
// false = ensure foreign_keys pragma is disabled within the transaction
|
|
53
|
+
// null = leave it however it already is
|
|
54
|
+
const enforceForeignCheck = config.enforceForeignCheck;
|
|
55
|
+
|
|
56
|
+
// if we're in strict mode, require the caller to be explicit about foreign key
|
|
57
|
+
// constraint requirements
|
|
58
|
+
if (strictForeignKeyPragma === true && enforceForeignCheck === undefined) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'Refusing to create an unsafe transaction: client.strictForeignKeyPragma is true, but check.enforceForeignCheck is unspecified'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// call the base class's acquireConnection logic to get ahold of a connection before the transaction is created
|
|
65
|
+
return this.acquireConnection(config, async (conn) => {
|
|
66
|
+
let restoreForeignCheck = undefined;
|
|
67
|
+
try {
|
|
68
|
+
// change the `foreign_keys` pragma if we need to, and decide what we should set it back to, if anything
|
|
69
|
+
restoreForeignCheck = await this._setForeignCheck(
|
|
70
|
+
conn,
|
|
71
|
+
enforceForeignCheck
|
|
72
|
+
);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// We don't need to dispose the connection here, because none of the things that can throw an error
|
|
75
|
+
// can leave the connection in an unexpected state. Just reject the begin transaction.
|
|
76
|
+
const error = new Error(
|
|
77
|
+
`Refusing to create transaction: failed to set \`foreign_keys\` pragma to the required value of ${enforceForeignCheck}`
|
|
78
|
+
);
|
|
79
|
+
error.cause = e;
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// if:
|
|
84
|
+
// - we're in a nested transaction
|
|
85
|
+
// - _and_ we're in strict mode
|
|
86
|
+
// - _and_ we are required to change the pragma
|
|
87
|
+
// then: we cannot continue, it's out of our hands
|
|
88
|
+
if (
|
|
89
|
+
strictForeignKeyPragma &&
|
|
90
|
+
hasOuterTransaction &&
|
|
91
|
+
restoreForeignCheck !== undefined
|
|
92
|
+
) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Refusing to create transaction: unable to change \`foreign_keys\` pragma inside a nested transaction`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let maybeWrappedContainer = container;
|
|
99
|
+
if (restoreForeignCheck === true) {
|
|
100
|
+
// in the case where we are turning foreign key checks off for the duration of a transaction,
|
|
101
|
+
// we need to assert that there are no violations once the work of the transaction has been
|
|
102
|
+
// completed. this relies on the fact that Transaction._onAcquire runs the "container" promise
|
|
103
|
+
// to completion before executing "COMMIT"
|
|
104
|
+
maybeWrappedContainer = async (trx) => {
|
|
105
|
+
const res = await container(trx);
|
|
106
|
+
|
|
107
|
+
const foreignViolations = await this.client
|
|
108
|
+
.raw(executeForeignCheck())
|
|
109
|
+
.connection(conn);
|
|
110
|
+
|
|
111
|
+
if (foreignViolations.length > 0) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Transaction concluded with ${foreignViolations.length} foreign key violations`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return res;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// call out to the base class to actually do the work as it normally would
|
|
122
|
+
// note: the await is required here! we need to resolve the promise, not
|
|
123
|
+
// return it
|
|
124
|
+
return await this._onAcquire(maybeWrappedContainer, conn);
|
|
125
|
+
} finally {
|
|
126
|
+
// set the foreign_keys pragma back to what it was before we performed the transaction
|
|
127
|
+
this._restoreForeignCheck(conn, restoreForeignCheck).catch((e) => {
|
|
128
|
+
// we were unable to put it back like we found it. dispose the connection and
|
|
129
|
+
// allow any further queries to re-acquire a new, clean connection
|
|
130
|
+
this._logAndDispose(
|
|
131
|
+
conn,
|
|
132
|
+
'Failed to restore foreign check to expected state',
|
|
133
|
+
e
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_logAndDispose(conn, message, cause) {
|
|
141
|
+
const error = new Error(message);
|
|
142
|
+
error.cause = cause;
|
|
143
|
+
conn.__knex__disposed = error;
|
|
144
|
+
this.client.logger.warn(
|
|
145
|
+
`Transaction_Sqlite: ${message}:\n${
|
|
146
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
147
|
+
}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
begin(conn) {
|
|
152
|
+
// SQLite doesn't really support isolation levels, it is serializable by
|
|
153
|
+
// default and so we override it to ignore isolation level.
|
|
154
|
+
// There is a `PRAGMA read_uncommitted = true;`, but that's probably not
|
|
155
|
+
// what the user wants
|
|
156
|
+
if (this.isolationLevel) {
|
|
157
|
+
this.client.logger.warn(
|
|
158
|
+
'sqlite3 only supports serializable transactions, ignoring the isolation level param'
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
// SQLite infers read vs write transactions from the statement operation
|
|
162
|
+
// https://www.sqlite.org/lang_transaction.html#read_transactions_versus_write_transactions
|
|
163
|
+
if (this.readOnly) {
|
|
164
|
+
this.client.logger.warn(
|
|
165
|
+
'sqlite3 implicitly handles read vs write transactions'
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return this.query(conn, 'BEGIN;');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = Transaction_Sqlite;
|