ts-prorm-orm 1.2.2 → 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 +209 -0
- package/README.md +32 -24
- package/dist/compliance/index.d.ts +2 -0
- package/dist/compliance/index.js +13 -1
- package/dist/connection-manager.js +13 -1
- package/dist/core/expressions.d.ts +58 -0
- package/dist/core/expressions.js +83 -0
- package/dist/core/hooks-manager.d.ts +179 -0
- package/dist/core/hooks-manager.js +291 -0
- package/dist/core/types.d.ts +235 -0
- package/dist/core/types.js +9 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +11 -1
- package/dist/diagrams/chen-diagram.d.ts +15 -12
- package/dist/diagrams/chen-diagram.js +76 -85
- package/dist/diagrams/class-diagram.d.ts +9 -8
- package/dist/diagrams/class-diagram.js +60 -82
- package/dist/diagrams/core/index.d.ts +13 -0
- package/dist/diagrams/core/index.js +33 -0
- package/dist/diagrams/core/layout.d.ts +104 -0
- package/dist/diagrams/core/layout.js +223 -0
- package/dist/diagrams/{svg-dom.d.ts → core/svg.d.ts} +8 -20
- package/dist/diagrams/{svg-dom.js → core/svg.js} +21 -29
- package/dist/diagrams/core/theme.d.ts +84 -0
- package/dist/diagrams/core/theme.js +62 -0
- package/dist/diagrams/dependency-diagram.d.ts +15 -14
- package/dist/diagrams/dependency-diagram.js +71 -105
- package/dist/diagrams/er-diagram.d.ts +13 -26
- package/dist/diagrams/er-diagram.js +180 -259
- package/dist/diagrams/flow-diagram.d.ts +16 -11
- package/dist/diagrams/flow-diagram.js +96 -91
- package/dist/diagrams/gantt-diagram.d.ts +10 -5
- package/dist/diagrams/gantt-diagram.js +77 -103
- package/dist/diagrams/index-diagram.d.ts +10 -10
- package/dist/diagrams/index-diagram.js +106 -116
- package/dist/diagrams/index.d.ts +6 -9
- package/dist/diagrams/index.js +9 -24
- package/dist/diagrams/migration-diagram.d.ts +8 -4
- package/dist/diagrams/migration-diagram.js +189 -161
- package/dist/diagrams/model-diagram.d.ts +34 -34
- package/dist/diagrams/model-diagram.js +106 -485
- package/dist/diagrams/package-diagram.d.ts +9 -6
- package/dist/diagrams/package-diagram.js +64 -101
- package/dist/diagrams/relational-diagram.d.ts +12 -10
- package/dist/diagrams/relational-diagram.js +87 -114
- package/dist/diagrams/schemadoc-diagram.d.ts +8 -8
- package/dist/diagrams/schemadoc-diagram.js +71 -93
- package/dist/diagrams/sequence-diagram.d.ts +10 -10
- package/dist/diagrams/sequence-diagram.js +64 -89
- package/dist/diagrams/state-diagram.d.ts +10 -9
- package/dist/diagrams/state-diagram.js +69 -92
- package/dist/diagrams/tree-diagram.d.ts +11 -5
- package/dist/diagrams/tree-diagram.js +41 -65
- package/dist/dialects/clickhouse/index.js +15 -0
- package/dist/dialects/cockroachdb/index.d.ts +1 -1
- package/dist/dialects/cockroachdb/index.js +32 -63
- package/dist/dialects/db2/index.js +23 -8
- package/dist/dialects/dialect.d.ts +29 -5
- package/dist/dialects/duckdb/index.js +9 -2
- package/dist/dialects/hana/index.js +22 -5
- package/dist/dialects/lock-clause-helper.d.ts +167 -0
- package/dist/dialects/lock-clause-helper.js +316 -0
- package/dist/dialects/mariadb/index.d.ts +7 -4
- package/dist/dialects/mariadb/index.js +36 -60
- package/dist/dialects/mssql/index.js +34 -3
- package/dist/dialects/mysql/index.d.ts +1 -1
- package/dist/dialects/mysql/index.js +17 -45
- package/dist/dialects/oracle/index.d.ts +14 -3
- package/dist/dialects/oracle/index.js +66 -36
- package/dist/dialects/order-expression-helper.d.ts +72 -0
- package/dist/dialects/order-expression-helper.js +154 -0
- package/dist/dialects/partial-index.d.ts +52 -0
- package/dist/dialects/partial-index.js +251 -0
- package/dist/dialects/postgres/index.d.ts +1 -1
- package/dist/dialects/postgres/index.js +43 -68
- package/dist/dialects/redshift/index.js +9 -2
- package/dist/dialects/snowflake/index.js +17 -0
- package/dist/dialects/spanner/index.js +7 -2
- package/dist/dialects/sqlite/driver.d.ts +55 -0
- package/dist/dialects/sqlite/driver.js +102 -0
- package/dist/dialects/sqlite/index.d.ts +1 -1
- package/dist/dialects/sqlite/index.js +49 -27
- package/dist/graph/base-graph-dialect.d.ts +24 -3
- package/dist/graph/base-graph-dialect.js +43 -2
- package/dist/graph/dgraph/index.js +6 -0
- package/dist/graph/gremlin/index.d.ts +6 -0
- package/dist/graph/gremlin/index.js +12 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/neo4j/index.d.ts +2 -0
- package/dist/graph/neo4j/index.js +6 -1
- package/dist/graph/types.d.ts +12 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +54 -7
- package/dist/logging/index.d.ts +10 -0
- package/dist/logging/index.js +23 -0
- package/dist/logging/query-logging.d.ts +82 -0
- package/dist/logging/query-logging.js +102 -0
- package/dist/models/decorators.d.ts +39 -3
- package/dist/models/decorators.js +74 -0
- package/dist/models/eager-load.d.ts +99 -0
- package/dist/models/eager-load.js +348 -5
- package/dist/models/indexes.d.ts +3 -2
- package/dist/models/indexes.js +7 -1
- package/dist/models/model.js +69 -43
- package/dist/prorm.d.ts +175 -409
- package/dist/prorm.js +1142 -557
- package/dist/query-builders/order-limit-builder.js +12 -10
- package/dist/query-builders/sql-compiler.d.ts +10 -3
- package/dist/query-builders/sql-compiler.js +14 -43
- package/dist/query-interface.d.ts +2 -1
- package/dist/sql-constants.d.ts +44 -0
- package/dist/sql-constants.js +138 -1
- package/dist/types/index.d.ts +53 -17
- package/dist/types/index.js +10 -15
- package/dist/types/query-types.d.ts +26 -0
- package/dist/types/query-types.js +30 -0
- package/package.json +32 -5
- package/dist/decorators/belongs-to-many.js +0 -115
- package/dist/decorators/belongs-to.js +0 -115
- package/dist/decorators/has-many.js +0 -127
- package/dist/decorators/has-one.js +0 -116
- package/dist/diagrams/palette.d.ts +0 -138
- package/dist/diagrams/palette.js +0 -194
- package/dist/hooks/hooks-manager.d.ts +0 -189
- package/dist/hooks/hooks-manager.js +0 -350
- package/dist/hooks/index.d.ts +0 -8
- package/dist/hooks/index.js +0 -37
- /package/dist/{logging.d.ts → logging/logger.d.ts} +0 -0
- /package/dist/{logging.js → logging/logger.js} +0 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared compiler for `FindOptions.lock` (row-level locking).
|
|
4
|
+
*
|
|
5
|
+
* Every dialect used to re-implement the `lock` option inline inside its own
|
|
6
|
+
* `buildSelectQuery()`, and every one of those copies understood a slightly
|
|
7
|
+
* different subset of the documented `LockOptions` shapes. The object forms
|
|
8
|
+
* (`{ level }`, `{ level, nowait }`, `{ level, skipLocked }`, `{ skipLocked }`)
|
|
9
|
+
* fell through most of them and compiled to *nothing* - the caller asked for a
|
|
10
|
+
* lock, got a plain `SELECT`, and only found out under concurrency. This module
|
|
11
|
+
* centralises the parsing (`normalizeLockOptions`) and the SQL assembly
|
|
12
|
+
* (`buildLockClause`) so a shape can only be dropped by deliberately leaving it
|
|
13
|
+
* out of a dialect's {@link LockClauseSpec} - and a spec that cannot express
|
|
14
|
+
* what was asked for throws instead of silently emitting a weaker lock.
|
|
15
|
+
*
|
|
16
|
+
* Dialects with no row-level locking at all (SQLite and friends) simply never
|
|
17
|
+
* call in here: ignoring `lock` is a deliberate, documented decision for them
|
|
18
|
+
* (SQLite serialises writers anyway), not an oversight.
|
|
19
|
+
*
|
|
20
|
+
* SQL Server is the odd one out - it expresses row locking as a table hint
|
|
21
|
+
* (`FROM t WITH (UPDLOCK, ROWLOCK)`) rather than a trailing clause - so it
|
|
22
|
+
* shares the parsing/validation via {@link buildSqlServerLockHint} while
|
|
23
|
+
* rendering its own syntax.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.HANA_LOCK_SPEC = exports.DB2_LOCK_SPEC = exports.ORACLE_LOCK_SPEC = exports.MARIADB_LOCK_SPEC = exports.MYSQL_LOCK_SPEC = exports.COCKROACHDB_LOCK_SPEC = exports.POSTGRES_LOCK_SPEC = void 0;
|
|
27
|
+
exports.normalizeLockOptions = normalizeLockOptions;
|
|
28
|
+
exports.buildLockClause = buildLockClause;
|
|
29
|
+
exports.appendLockClause = appendLockClause;
|
|
30
|
+
exports.buildSqlServerLockHint = buildSqlServerLockHint;
|
|
31
|
+
exports.getLockSpecForDialect = getLockSpecForDialect;
|
|
32
|
+
const KNOWN_LEVELS = ['UPDATE', 'SHARE', 'KEY SHARE', 'NO KEY UPDATE'];
|
|
33
|
+
function levelList(spec) {
|
|
34
|
+
return Object.keys(spec.levels).join(', ');
|
|
35
|
+
}
|
|
36
|
+
function parseLevel(value) {
|
|
37
|
+
if (typeof value !== 'string') {
|
|
38
|
+
throw new Error(`Invalid lock level ${JSON.stringify(value)}. Expected one of: ${KNOWN_LEVELS.join(', ')}.`);
|
|
39
|
+
}
|
|
40
|
+
const normalized = value.trim().toUpperCase().replace(/\s+/g, ' ');
|
|
41
|
+
if (KNOWN_LEVELS.includes(normalized)) {
|
|
42
|
+
return normalized;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Unknown lock level ${JSON.stringify(value)}. Expected one of: ${KNOWN_LEVELS.join(', ')}.`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Collapse any documented `lock` shape into a {@link NormalizedLock}.
|
|
48
|
+
*
|
|
49
|
+
* Returns `undefined` for a falsy lock (`undefined`, `false`, `null`), which
|
|
50
|
+
* every caller treats as "no locking clause".
|
|
51
|
+
*
|
|
52
|
+
* Accepted shapes:
|
|
53
|
+
* - `true` -> `FOR UPDATE`
|
|
54
|
+
* - `'UPDATE' | 'SHARE' | 'KEY SHARE' | 'NO KEY UPDATE'` (case-insensitive)
|
|
55
|
+
* - `{ of: Model }` -> `FOR UPDATE` scoped to that table
|
|
56
|
+
* - `{ level?, nowait?, skipLocked?, of? }` -> level defaults to `UPDATE`
|
|
57
|
+
*
|
|
58
|
+
* `mode` is accepted as an alias for `level` and `noWait` for `nowait`, since
|
|
59
|
+
* the MariaDB dialect has always documented those spellings.
|
|
60
|
+
*
|
|
61
|
+
* Throws on a level it doesn't recognise rather than quietly emitting no lock.
|
|
62
|
+
*/
|
|
63
|
+
function normalizeLockOptions(lock) {
|
|
64
|
+
if (!lock) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (lock === true) {
|
|
68
|
+
return { level: 'UPDATE', nowait: false, skipLocked: false };
|
|
69
|
+
}
|
|
70
|
+
if (typeof lock === 'string') {
|
|
71
|
+
return { level: parseLevel(lock), nowait: false, skipLocked: false };
|
|
72
|
+
}
|
|
73
|
+
if (typeof lock !== 'object') {
|
|
74
|
+
throw new Error(`Invalid "lock" option: expected true, a lock level string, or an options object, got ${typeof lock}.`);
|
|
75
|
+
}
|
|
76
|
+
const raw = lock;
|
|
77
|
+
const rawLevel = raw.level ?? raw.mode;
|
|
78
|
+
const level = rawLevel === undefined || rawLevel === null ? 'UPDATE' : parseLevel(rawLevel);
|
|
79
|
+
const nowait = raw.nowait === true || raw.noWait === true;
|
|
80
|
+
const skipLocked = raw.skipLocked === true;
|
|
81
|
+
if (nowait && skipLocked) {
|
|
82
|
+
throw new Error('Invalid "lock" option: NOWAIT and SKIP LOCKED are mutually exclusive - set only one of lock.nowait / lock.skipLocked.');
|
|
83
|
+
}
|
|
84
|
+
return { level, nowait, skipLocked, of: raw.of };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build the trailing locking clause for a dialect, e.g. `FOR UPDATE OF "users" NOWAIT`.
|
|
88
|
+
*
|
|
89
|
+
* Returns `''` when there is no lock to apply. Throws when the requested lock
|
|
90
|
+
* cannot be expressed by this dialect - a lock the caller believes they hold
|
|
91
|
+
* but don't is far worse than a loud failure at query-build time.
|
|
92
|
+
*
|
|
93
|
+
* @param lock - the raw `options.lock` value
|
|
94
|
+
* @param spec - the dialect's locking capabilities
|
|
95
|
+
* @param escapeId - the dialect's identifier quoter, used for `lock.of`
|
|
96
|
+
*/
|
|
97
|
+
function buildLockClause(lock, spec, escapeId = (id) => id) {
|
|
98
|
+
const normalized = normalizeLockOptions(lock);
|
|
99
|
+
if (!normalized) {
|
|
100
|
+
return '';
|
|
101
|
+
}
|
|
102
|
+
const levelSql = spec.levels[normalized.level];
|
|
103
|
+
if (levelSql === undefined) {
|
|
104
|
+
throw new Error(`${spec.name} does not support ${normalized.level} row locking. Supported lock levels: ${levelList(spec)}.`);
|
|
105
|
+
}
|
|
106
|
+
let clause = typeof levelSql === 'function' ? levelSql(normalized) : levelSql;
|
|
107
|
+
if (normalized.of && spec.renderOf) {
|
|
108
|
+
const ofLevels = spec.ofLevels ?? Object.keys(spec.levels);
|
|
109
|
+
if (ofLevels.includes(normalized.level)) {
|
|
110
|
+
const ofSql = spec.renderOf(normalized.of, escapeId);
|
|
111
|
+
if (ofSql) {
|
|
112
|
+
clause += ` ${ofSql}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (normalized.nowait) {
|
|
117
|
+
if (!spec.nowaitLevels?.includes(normalized.level)) {
|
|
118
|
+
throw new Error(`${spec.name} does not support NOWAIT with a ${normalized.level} lock. ` +
|
|
119
|
+
`Remove lock.nowait, or use a lock level that supports it${spec.nowaitLevels?.length ? ` (${spec.nowaitLevels.join(', ')})` : ''}.`);
|
|
120
|
+
}
|
|
121
|
+
clause += ' NOWAIT';
|
|
122
|
+
}
|
|
123
|
+
else if (normalized.skipLocked) {
|
|
124
|
+
if (!spec.skipLockedLevels?.includes(normalized.level)) {
|
|
125
|
+
throw new Error(`${spec.name} does not support SKIP LOCKED with a ${normalized.level} lock. ` +
|
|
126
|
+
`Remove lock.skipLocked, or use a lock level that supports it${spec.skipLockedLevels?.length ? ` (${spec.skipLockedLevels.join(', ')})` : ''}.`);
|
|
127
|
+
}
|
|
128
|
+
clause += ' SKIP LOCKED';
|
|
129
|
+
}
|
|
130
|
+
return clause;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Same as {@link buildLockClause} but returns the clause with a leading space
|
|
134
|
+
* (or `''`), which is how every `buildSelectQuery()` wants to append it.
|
|
135
|
+
*/
|
|
136
|
+
function appendLockClause(lock, spec, escapeId) {
|
|
137
|
+
const clause = buildLockClause(lock, spec, escapeId);
|
|
138
|
+
return clause ? ` ${clause}` : '';
|
|
139
|
+
}
|
|
140
|
+
/* -------------------------------------------------------------------------- */
|
|
141
|
+
/* Per-dialect specs */
|
|
142
|
+
/* -------------------------------------------------------------------------- */
|
|
143
|
+
/**
|
|
144
|
+
* PostgreSQL: `FOR UPDATE | NO KEY UPDATE | SHARE | KEY SHARE [OF table] [NOWAIT | SKIP LOCKED]`.
|
|
145
|
+
* The full matrix - every level accepts both modifiers and the `OF` clause.
|
|
146
|
+
*/
|
|
147
|
+
exports.POSTGRES_LOCK_SPEC = {
|
|
148
|
+
name: 'PostgreSQL',
|
|
149
|
+
levels: {
|
|
150
|
+
UPDATE: 'FOR UPDATE',
|
|
151
|
+
'NO KEY UPDATE': 'FOR NO KEY UPDATE',
|
|
152
|
+
SHARE: 'FOR SHARE',
|
|
153
|
+
'KEY SHARE': 'FOR KEY SHARE',
|
|
154
|
+
},
|
|
155
|
+
nowaitLevels: KNOWN_LEVELS,
|
|
156
|
+
skipLockedLevels: KNOWN_LEVELS,
|
|
157
|
+
// Historically `OF` was emitted even for a model with no tableName (as `OF ""`);
|
|
158
|
+
// keep that byte-for-byte rather than silently dropping the requested scope.
|
|
159
|
+
renderOf: (model, escapeId) => `OF ${escapeId(model.tableName || '')}`,
|
|
160
|
+
};
|
|
161
|
+
/** CockroachDB implements the PostgreSQL locking grammar. */
|
|
162
|
+
exports.COCKROACHDB_LOCK_SPEC = {
|
|
163
|
+
...exports.POSTGRES_LOCK_SPEC,
|
|
164
|
+
name: 'CockroachDB',
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* MySQL 8.0+: `FOR UPDATE [OF tbl] [NOWAIT | SKIP LOCKED]` and
|
|
168
|
+
* `FOR SHARE [OF tbl] [NOWAIT | SKIP LOCKED]`.
|
|
169
|
+
*
|
|
170
|
+
* A bare shared lock still compiles to the pre-8.0 `LOCK IN SHARE MODE`
|
|
171
|
+
* spelling (which every supported MySQL understands, and which existing
|
|
172
|
+
* `lock: 'SHARE'` callers already get); it only switches to the 8.0 `FOR SHARE`
|
|
173
|
+
* form when a modifier is requested, because `LOCK IN SHARE MODE` accepts
|
|
174
|
+
* neither `NOWAIT` nor `SKIP LOCKED`.
|
|
175
|
+
*/
|
|
176
|
+
exports.MYSQL_LOCK_SPEC = {
|
|
177
|
+
name: 'MySQL',
|
|
178
|
+
levels: {
|
|
179
|
+
UPDATE: 'FOR UPDATE',
|
|
180
|
+
SHARE: (lock) => (lock.nowait || lock.skipLocked ? 'FOR SHARE' : 'LOCK IN SHARE MODE'),
|
|
181
|
+
},
|
|
182
|
+
nowaitLevels: ['UPDATE', 'SHARE'],
|
|
183
|
+
skipLockedLevels: ['UPDATE', 'SHARE'],
|
|
184
|
+
renderOf: (model, escapeId) => (model.tableName ? `OF ${escapeId(model.tableName)}` : ''),
|
|
185
|
+
ofLevels: ['UPDATE'],
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* MariaDB: `FOR UPDATE` / `LOCK IN SHARE MODE` on every version, with
|
|
189
|
+
* `FOR SHARE` and the `SKIP LOCKED` / `NOWAIT` modifiers from 10.6.
|
|
190
|
+
*
|
|
191
|
+
* MariaDB has no `FOR UPDATE OF <table>`, so `lock.of` only signals that a lock
|
|
192
|
+
* was wanted (matching this dialect's long-standing behaviour).
|
|
193
|
+
*/
|
|
194
|
+
exports.MARIADB_LOCK_SPEC = {
|
|
195
|
+
name: 'MariaDB',
|
|
196
|
+
levels: {
|
|
197
|
+
UPDATE: 'FOR UPDATE',
|
|
198
|
+
SHARE: (lock) => (lock.nowait || lock.skipLocked ? 'FOR SHARE' : 'LOCK IN SHARE MODE'),
|
|
199
|
+
},
|
|
200
|
+
nowaitLevels: ['UPDATE', 'SHARE'],
|
|
201
|
+
skipLockedLevels: ['UPDATE', 'SHARE'],
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Oracle: `FOR UPDATE [OF column] [NOWAIT | SKIP LOCKED]`.
|
|
205
|
+
*
|
|
206
|
+
* Oracle has no shared row lock (`SHARE`/`KEY SHARE` have no equivalent), and
|
|
207
|
+
* its `OF` names *columns*, not a table - a `lock.of` model can't be turned into
|
|
208
|
+
* one, so `of` is treated as a plain `FOR UPDATE` request exactly as it always
|
|
209
|
+
* has been.
|
|
210
|
+
*/
|
|
211
|
+
exports.ORACLE_LOCK_SPEC = {
|
|
212
|
+
name: 'Oracle',
|
|
213
|
+
levels: {
|
|
214
|
+
UPDATE: 'FOR UPDATE',
|
|
215
|
+
},
|
|
216
|
+
nowaitLevels: ['UPDATE'],
|
|
217
|
+
skipLockedLevels: ['UPDATE'],
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Db2: `FOR UPDATE`, plus the `WITH RS` isolation clause for a read-stability
|
|
221
|
+
* (shared) lock. Db2 spells lock-avoidance as `SKIP LOCKED DATA` inside an
|
|
222
|
+
* isolation clause and has no `NOWAIT` at all, so both modifiers throw rather
|
|
223
|
+
* than being approximated.
|
|
224
|
+
*/
|
|
225
|
+
exports.DB2_LOCK_SPEC = {
|
|
226
|
+
name: 'Db2',
|
|
227
|
+
levels: {
|
|
228
|
+
UPDATE: 'FOR UPDATE',
|
|
229
|
+
SHARE: 'FOR UPDATE WITH RS',
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
/** SAP HANA: `FOR UPDATE [NOWAIT]`. No shared row lock and no `SKIP LOCKED`. */
|
|
233
|
+
exports.HANA_LOCK_SPEC = {
|
|
234
|
+
name: 'SAP HANA',
|
|
235
|
+
levels: {
|
|
236
|
+
UPDATE: 'FOR UPDATE',
|
|
237
|
+
},
|
|
238
|
+
nowaitLevels: ['UPDATE'],
|
|
239
|
+
};
|
|
240
|
+
/* -------------------------------------------------------------------------- */
|
|
241
|
+
/* SQL Server - table hints rather than a trailing clause */
|
|
242
|
+
/* -------------------------------------------------------------------------- */
|
|
243
|
+
const SQLSERVER_LEVEL_HINTS = {
|
|
244
|
+
UPDATE: 'UPDLOCK',
|
|
245
|
+
SHARE: 'HOLDLOCK',
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Build the SQL Server locking table hint, e.g. `WITH (UPDLOCK, ROWLOCK)`.
|
|
249
|
+
*
|
|
250
|
+
* T-SQL has no `FOR UPDATE`; row locking is requested with hints on the table
|
|
251
|
+
* reference: `UPDLOCK`/`HOLDLOCK` for the lock itself, `ROWLOCK` to pin the
|
|
252
|
+
* granularity to rows, `READPAST` for `SKIP LOCKED` and `NOWAIT` for `NOWAIT`.
|
|
253
|
+
*
|
|
254
|
+
* Returns `''` when there is no lock to apply.
|
|
255
|
+
*
|
|
256
|
+
* @param lock - the raw `options.lock` value
|
|
257
|
+
* @param tableName - the table the hint will be attached to, so a `lock.of`
|
|
258
|
+
* naming some *other* table can be rejected instead of silently locking the
|
|
259
|
+
* wrong one (a hint only applies to the table reference it sits on).
|
|
260
|
+
*/
|
|
261
|
+
function buildSqlServerLockHint(lock, tableName) {
|
|
262
|
+
const normalized = normalizeLockOptions(lock);
|
|
263
|
+
if (!normalized) {
|
|
264
|
+
return '';
|
|
265
|
+
}
|
|
266
|
+
const levelHint = SQLSERVER_LEVEL_HINTS[normalized.level];
|
|
267
|
+
if (!levelHint) {
|
|
268
|
+
throw new Error(`SQL Server does not support ${normalized.level} row locking. Supported lock levels: UPDATE, SHARE.`);
|
|
269
|
+
}
|
|
270
|
+
if (normalized.of) {
|
|
271
|
+
const ofTable = normalized.of.tableName || normalized.of.name;
|
|
272
|
+
if (ofTable && tableName && ofTable !== tableName) {
|
|
273
|
+
throw new Error(`SQL Server cannot scope a lock to ${JSON.stringify(ofTable)} from a query on ${JSON.stringify(tableName)}: locking is expressed as a table hint, which only applies to the table it is attached to.`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const hints = [levelHint, 'ROWLOCK'];
|
|
277
|
+
if (normalized.nowait) {
|
|
278
|
+
hints.push('NOWAIT');
|
|
279
|
+
}
|
|
280
|
+
else if (normalized.skipLocked) {
|
|
281
|
+
hints.push('READPAST');
|
|
282
|
+
}
|
|
283
|
+
return `WITH (${hints.join(', ')})`;
|
|
284
|
+
}
|
|
285
|
+
/* -------------------------------------------------------------------------- */
|
|
286
|
+
/* Dialect-name lookup */
|
|
287
|
+
/* -------------------------------------------------------------------------- */
|
|
288
|
+
const SPECS_BY_DIALECT = {
|
|
289
|
+
postgres: exports.POSTGRES_LOCK_SPEC,
|
|
290
|
+
postgresql: exports.POSTGRES_LOCK_SPEC,
|
|
291
|
+
pg: exports.POSTGRES_LOCK_SPEC,
|
|
292
|
+
greenplum: exports.POSTGRES_LOCK_SPEC,
|
|
293
|
+
timescaledb: exports.POSTGRES_LOCK_SPEC,
|
|
294
|
+
yugabytedb: exports.POSTGRES_LOCK_SPEC,
|
|
295
|
+
vertica: exports.POSTGRES_LOCK_SPEC,
|
|
296
|
+
cockroachdb: exports.COCKROACHDB_LOCK_SPEC,
|
|
297
|
+
mysql: exports.MYSQL_LOCK_SPEC,
|
|
298
|
+
tidb: exports.MYSQL_LOCK_SPEC,
|
|
299
|
+
singlestore: exports.MYSQL_LOCK_SPEC,
|
|
300
|
+
mariadb: exports.MARIADB_LOCK_SPEC,
|
|
301
|
+
oracle: exports.ORACLE_LOCK_SPEC,
|
|
302
|
+
db2: exports.DB2_LOCK_SPEC,
|
|
303
|
+
hana: exports.HANA_LOCK_SPEC,
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Look up the locking spec for a dialect name. Returns `undefined` for a
|
|
307
|
+
* dialect with no row-level locking (SQLite and friends) and for SQL Server,
|
|
308
|
+
* whose locking is a table hint rather than a trailing clause - callers should
|
|
309
|
+
* treat `undefined` as "emit no trailing lock clause".
|
|
310
|
+
*/
|
|
311
|
+
function getLockSpecForDialect(dialect) {
|
|
312
|
+
if (typeof dialect !== 'string') {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
return SPECS_BY_DIALECT[dialect.toLowerCase()];
|
|
316
|
+
}
|
|
@@ -740,10 +740,13 @@ export declare class MariaDBDialect implements Dialect {
|
|
|
740
740
|
*/
|
|
741
741
|
removeIndex(tableName: string, indexName: string): Promise<void>;
|
|
742
742
|
/**
|
|
743
|
-
*
|
|
744
|
-
*
|
|
743
|
+
* Partial (filtered) indexes - not available on MariaDB.
|
|
744
|
+
*
|
|
745
|
+
* The claim that MariaDB 10.0.3 added them is wrong: `CREATE INDEX` has never
|
|
746
|
+
* taken a `WHERE` clause on MariaDB (a live mariadb:11 server answers
|
|
747
|
+
* ER_PARSE_ERROR), so this throws rather than build that statement.
|
|
745
748
|
*/
|
|
746
|
-
createPartialIndex(
|
|
749
|
+
createPartialIndex(_tableName: string, _indexName: string, _fields: string[], _where: string, _options?: IndexOptions): Promise<void>;
|
|
747
750
|
/**
|
|
748
751
|
* Create an index on a table with full options support
|
|
749
752
|
*/
|
|
@@ -753,7 +756,7 @@ export declare class MariaDBDialect implements Dialect {
|
|
|
753
756
|
type?: string;
|
|
754
757
|
using?: string;
|
|
755
758
|
fields: string[];
|
|
756
|
-
where?: WhereOptions;
|
|
759
|
+
where?: WhereOptions | string;
|
|
757
760
|
invisible?: boolean;
|
|
758
761
|
}): Promise<void>;
|
|
759
762
|
/**
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.MariaDBRowFormats = exports.MariaDBConnectTypes = exports.MariaDBStorageEngines = exports.MariaDBTransaction = exports.MariaDBDialect = void 0;
|
|
16
16
|
exports.createMariaDBDialect = createMariaDBDialect;
|
|
17
17
|
const operators_1 = require("../../operators");
|
|
18
|
+
const partial_index_1 = require("../partial-index");
|
|
18
19
|
// Lazily-loaded `mariadb` driver. Required only when a connection is opened,
|
|
19
20
|
// so importing this ORM never loads the mariadb driver.
|
|
20
21
|
let mariadbModule = null;
|
|
@@ -32,8 +33,10 @@ function loadMariaDb() {
|
|
|
32
33
|
return mariadbModule;
|
|
33
34
|
}
|
|
34
35
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
36
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
35
37
|
const prorm_1 = require("../../prorm");
|
|
36
38
|
const operators_2 = require("../../operators");
|
|
39
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
37
40
|
/**
|
|
38
41
|
* MariaDB dialect class that implements the Dialect interface
|
|
39
42
|
* Extends MySQL compatibility with MariaDB-specific features
|
|
@@ -1835,10 +1838,12 @@ class MariaDBDialect {
|
|
|
1835
1838
|
if (options?.include && options.include.length > 0) {
|
|
1836
1839
|
sql += ` INCLUDE (${options.include.map((f) => this.escapeId(f)).join(', ')})`;
|
|
1837
1840
|
}
|
|
1838
|
-
//
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1841
|
+
// MariaDB has no partial/filtered indexes at any version - `CREATE INDEX`
|
|
1842
|
+
// takes no WHERE clause. This used to compile the predicate through the
|
|
1843
|
+
// ordinary WHERE builder and append it, producing SQL the server rejects
|
|
1844
|
+
// (with unbound `?` placeholders in it, no less), so fail with a reason.
|
|
1845
|
+
if (options?.where) {
|
|
1846
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
1842
1847
|
}
|
|
1843
1848
|
// Index visibility (MariaDB 10.6+). Unlike MySQL, MariaDB uses the
|
|
1844
1849
|
// `IGNORED` keyword for indexes (not `INVISIBLE` — that keyword is
|
|
@@ -1858,15 +1863,16 @@ class MariaDBDialect {
|
|
|
1858
1863
|
await this.query(sql);
|
|
1859
1864
|
}
|
|
1860
1865
|
/**
|
|
1861
|
-
*
|
|
1862
|
-
*
|
|
1866
|
+
* Partial (filtered) indexes - not available on MariaDB.
|
|
1867
|
+
*
|
|
1868
|
+
* The claim that MariaDB 10.0.3 added them is wrong: `CREATE INDEX` has never
|
|
1869
|
+
* taken a `WHERE` clause on MariaDB (a live mariadb:11 server answers
|
|
1870
|
+
* ER_PARSE_ERROR), so this throws rather than build that statement.
|
|
1863
1871
|
*/
|
|
1864
|
-
async createPartialIndex(
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
sql = sql.replace('CREATE INDEX', 'CREATE UNIQUE INDEX');
|
|
1869
|
-
await this.query(sql);
|
|
1872
|
+
async createPartialIndex(_tableName, _indexName, _fields, _where, _options) {
|
|
1873
|
+
// This used to emit `CREATE INDEX ... WHERE ...`, which MariaDB has never
|
|
1874
|
+
// accepted; the statement failed at the server with a syntax error.
|
|
1875
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
1870
1876
|
}
|
|
1871
1877
|
/**
|
|
1872
1878
|
* Create an index on a table with full options support
|
|
@@ -1891,10 +1897,9 @@ class MariaDBDialect {
|
|
|
1891
1897
|
if (indexDef.using) {
|
|
1892
1898
|
sql += ` USING ${indexDef.using}`;
|
|
1893
1899
|
}
|
|
1894
|
-
//
|
|
1895
|
-
if (indexDef.where
|
|
1896
|
-
|
|
1897
|
-
sql += ` WHERE ${whereClause.sql}`;
|
|
1900
|
+
// No partial/filtered indexes on MariaDB - see addIndex.
|
|
1901
|
+
if (indexDef.where) {
|
|
1902
|
+
throw (0, partial_index_1.unsupportedPartialIndex)(this.name);
|
|
1898
1903
|
}
|
|
1899
1904
|
// Index visibility (MariaDB 10.6+). Unlike MySQL, MariaDB uses
|
|
1900
1905
|
// `IGNORED` (not `INVISIBLE`) for indexes. Not ignored by default.
|
|
@@ -2368,12 +2373,18 @@ class MariaDBDialect {
|
|
|
2368
2373
|
* Build an ORDER BY clause
|
|
2369
2374
|
*/
|
|
2370
2375
|
buildOrderClause(order, options) {
|
|
2371
|
-
const orderArray = order;
|
|
2372
|
-
if (
|
|
2376
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
2377
|
+
if (orderArray.length === 0) {
|
|
2373
2378
|
return '';
|
|
2374
2379
|
}
|
|
2375
2380
|
const orderParts = [];
|
|
2376
2381
|
for (const item of orderArray) {
|
|
2382
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
2383
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
2384
|
+
if (orderExpr !== null) {
|
|
2385
|
+
orderParts.push(orderExpr);
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2377
2388
|
if (Array.isArray(item)) {
|
|
2378
2389
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
2379
2390
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -3025,48 +3036,13 @@ class MariaDBDialect {
|
|
|
3025
3036
|
}
|
|
3026
3037
|
// Build LIMIT/OFFSET clause
|
|
3027
3038
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
3028
|
-
// Handle row-level locking
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
lockType = 'UPDATE';
|
|
3036
|
-
}
|
|
3037
|
-
else if (typeof options.lock === 'string') {
|
|
3038
|
-
lockType = options.lock;
|
|
3039
|
-
}
|
|
3040
|
-
else if (typeof options.lock === 'object' && options.lock !== null) {
|
|
3041
|
-
// lock: { of: Model, mode: 'UPDATE' | 'SHARE', skipLocked?, noWait? }
|
|
3042
|
-
lockType = options.lock.mode || (options.lock.of ? 'UPDATE' : undefined);
|
|
3043
|
-
skipLocked = !!options.lock.skipLocked;
|
|
3044
|
-
noWait = !!options.lock.noWait;
|
|
3045
|
-
}
|
|
3046
|
-
if (lockType === 'UPDATE') {
|
|
3047
|
-
sql += ' FOR UPDATE';
|
|
3048
|
-
}
|
|
3049
|
-
else if (lockType === 'SHARE') {
|
|
3050
|
-
// MariaDB 10.6+ prefers `FOR SHARE` over the legacy `LOCK IN SHARE MODE`
|
|
3051
|
-
// syntax, but only `LOCK IN SHARE MODE` supports no modifiers, so use
|
|
3052
|
-
// `FOR SHARE` whenever a SKIP LOCKED/NOWAIT modifier is requested.
|
|
3053
|
-
if (skipLocked || noWait) {
|
|
3054
|
-
sql += ' FOR SHARE';
|
|
3055
|
-
}
|
|
3056
|
-
else {
|
|
3057
|
-
sql += ' LOCK IN SHARE MODE';
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
|
-
// MariaDB 10.6+ locking modifiers
|
|
3061
|
-
if (lockType === 'UPDATE' || lockType === 'SHARE') {
|
|
3062
|
-
if (skipLocked) {
|
|
3063
|
-
sql += ' SKIP LOCKED';
|
|
3064
|
-
}
|
|
3065
|
-
else if (noWait) {
|
|
3066
|
-
sql += ' NOWAIT';
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
}
|
|
3039
|
+
// Handle row-level locking (MariaDB 10.6+). The shared helper parses
|
|
3040
|
+
// every documented `lock` shape - including `{ level, nowait }`, which this dialect only understood
|
|
3041
|
+
// under its own `{ mode, noWait }` spelling and otherwise compiled to no
|
|
3042
|
+
// lock at all. Both spellings are still accepted. A shared lock uses the
|
|
3043
|
+
// legacy `LOCK IN SHARE MODE` unless a modifier is requested, since only
|
|
3044
|
+
// `FOR SHARE` accepts SKIP LOCKED / NOWAIT.
|
|
3045
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.MARIADB_LOCK_SPEC);
|
|
3070
3046
|
// Build UNION/EXCEPT/INTERSECT clause if provided
|
|
3071
3047
|
// MariaDB 10.3+ supports EXCEPT/EXCEPT ALL/INTERSECT/INTERSECT ALL in
|
|
3072
3048
|
// addition to UNION/UNION ALL.
|
|
@@ -8,9 +8,12 @@ exports.MSSQLDialect = void 0;
|
|
|
8
8
|
exports.createMSSQLDialect = createMSSQLDialect;
|
|
9
9
|
const mssql_1 = require("mssql");
|
|
10
10
|
const operators_1 = require("../../operators");
|
|
11
|
+
const partial_index_1 = require("../partial-index");
|
|
11
12
|
const types_1 = require("../../types");
|
|
12
13
|
const query_stream_helper_1 = require("../query-stream-helper");
|
|
14
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
13
15
|
const prorm_1 = require("../../prorm");
|
|
16
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
14
17
|
/**
|
|
15
18
|
* Default retry options for connection
|
|
16
19
|
*/
|
|
@@ -975,10 +978,15 @@ class MSSQLDialect {
|
|
|
975
978
|
if (options?.using)
|
|
976
979
|
sql += ` USING ${options.using}`;
|
|
977
980
|
// Filtered index predicate (a real, commonly-used MSSQL feature) - was
|
|
978
|
-
// previously accepted by createIndex() but silently dropped here.
|
|
981
|
+
// previously accepted by createIndex() but silently dropped here. The
|
|
982
|
+
// ordinary WHERE builder emits `@p1`-style placeholders and this DDL is
|
|
983
|
+
// executed without a parameter list, so the predicate is compiled with its
|
|
984
|
+
// values inlined as escaped literals.
|
|
979
985
|
if (options?.where) {
|
|
980
|
-
const
|
|
981
|
-
|
|
986
|
+
const predicate = (0, partial_index_1.buildPartialIndexPredicate)(options.where, this.name, this);
|
|
987
|
+
if (predicate) {
|
|
988
|
+
sql += ` WHERE ${predicate}`;
|
|
989
|
+
}
|
|
982
990
|
}
|
|
983
991
|
await this.query(sql);
|
|
984
992
|
}
|
|
@@ -1333,6 +1341,12 @@ class MSSQLDialect {
|
|
|
1333
1341
|
return '';
|
|
1334
1342
|
const orderParts = [];
|
|
1335
1343
|
const processOrder = (orderItem) => {
|
|
1344
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
1345
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, orderItem);
|
|
1346
|
+
if (orderExpr !== null) {
|
|
1347
|
+
orderParts.push(orderExpr);
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1336
1350
|
if (typeof orderItem === 'string') {
|
|
1337
1351
|
const parts = orderItem.split(' ');
|
|
1338
1352
|
if (parts.length === 1) {
|
|
@@ -1342,6 +1356,16 @@ class MSSQLDialect {
|
|
|
1342
1356
|
orderParts.push(`${this.escapeId(parts[0])} ${parts[1].toUpperCase()}`);
|
|
1343
1357
|
}
|
|
1344
1358
|
}
|
|
1359
|
+
else if (Array.isArray(orderItem)) {
|
|
1360
|
+
// Tuple form — `['name', 'DESC']` / `['name']`, the elements of the
|
|
1361
|
+
// primary documented `[['name', 'DESC'], ['id', 'ASC']]` shape. An
|
|
1362
|
+
// array is also an object, so without this branch each tuple fell
|
|
1363
|
+
// through to the map branch below and its *indices* were emitted as
|
|
1364
|
+
// column names (`ORDER BY [0] NAME, [1] DESC`).
|
|
1365
|
+
const [field, direction] = orderItem;
|
|
1366
|
+
const column = typeof field === 'string' ? this.escapeId(field) : String(field);
|
|
1367
|
+
orderParts.push(direction ? `${column} ${String(direction).toUpperCase()}` : column);
|
|
1368
|
+
}
|
|
1345
1369
|
else if (typeof orderItem === 'object' && orderItem !== null) {
|
|
1346
1370
|
for (const [key, value] of Object.entries(orderItem)) {
|
|
1347
1371
|
if (key === '$raw') {
|
|
@@ -1569,6 +1593,13 @@ class MSSQLDialect {
|
|
|
1569
1593
|
else if (options.temporalAll) {
|
|
1570
1594
|
sql += ' FOR SYSTEM_TIME ALL';
|
|
1571
1595
|
}
|
|
1596
|
+
// Row-level locking. T-SQL has no `FOR UPDATE`; a lock is requested with a
|
|
1597
|
+
// table hint on the table reference, which has to sit immediately after it
|
|
1598
|
+
// (and after any FOR SYSTEM_TIME clause). See lock-clause-helper.ts.
|
|
1599
|
+
const lockHint = (0, lock_clause_helper_1.buildSqlServerLockHint)(options.lock, options.tableName);
|
|
1600
|
+
if (lockHint) {
|
|
1601
|
+
sql += ` ${lockHint}`;
|
|
1602
|
+
}
|
|
1572
1603
|
if (options.include && options.include.length > 0) {
|
|
1573
1604
|
for (const include of options.include) {
|
|
1574
1605
|
// CROSS APPLY / OUTER APPLY — a lateral join, needed for anything an
|
|
@@ -12,6 +12,8 @@ const promise_1 = __importDefault(require("mysql2/promise"));
|
|
|
12
12
|
const prorm_1 = require("../../prorm");
|
|
13
13
|
const operators_1 = require("../../operators");
|
|
14
14
|
const operators_2 = require("../../operators");
|
|
15
|
+
const lock_clause_helper_1 = require("../lock-clause-helper");
|
|
16
|
+
const order_expression_helper_1 = require("../order-expression-helper");
|
|
15
17
|
/**
|
|
16
18
|
* Default retry options for connection
|
|
17
19
|
*/
|
|
@@ -2881,12 +2883,18 @@ class MySQLDialect {
|
|
|
2881
2883
|
* Build an ORDER BY clause
|
|
2882
2884
|
*/
|
|
2883
2885
|
buildOrderClause(order, options) {
|
|
2884
|
-
const orderArray = order;
|
|
2885
|
-
if (
|
|
2886
|
+
const orderArray = (0, order_expression_helper_1.normalizeOrderItems)(order);
|
|
2887
|
+
if (orderArray.length === 0) {
|
|
2886
2888
|
return '';
|
|
2887
2889
|
}
|
|
2888
2890
|
const orderParts = [];
|
|
2889
2891
|
for (const item of orderArray) {
|
|
2892
|
+
// Order helpers: asc('name') / desc('name') / random()
|
|
2893
|
+
const orderExpr = (0, order_expression_helper_1.renderOrderExpressionItem)(this, item);
|
|
2894
|
+
if (orderExpr !== null) {
|
|
2895
|
+
orderParts.push(orderExpr);
|
|
2896
|
+
continue;
|
|
2897
|
+
}
|
|
2890
2898
|
if (Array.isArray(item)) {
|
|
2891
2899
|
const field = typeof item[0] === 'string' ? this.escapeId(item[0]) : item[0];
|
|
2892
2900
|
const direction = item[1] ? ` ${item[1]}` : '';
|
|
@@ -3483,49 +3491,13 @@ class MySQLDialect {
|
|
|
3483
3491
|
// Build LIMIT/OFFSET clause
|
|
3484
3492
|
sql += this.buildLimitOffset(options.limit, options.offset);
|
|
3485
3493
|
// Handle row-level locking
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
}
|
|
3494
|
-
else if (typeof options.lock === 'string') {
|
|
3495
|
-
lockType = options.lock;
|
|
3496
|
-
}
|
|
3497
|
-
else if (typeof options.lock === 'object') {
|
|
3498
|
-
// Handle { level: 'UPDATE', nowait: true } or { level: 'UPDATE', skipLocked: true }
|
|
3499
|
-
lockType = options.lock.level || (options.lock.of ? 'UPDATE' : undefined);
|
|
3500
|
-
nowait = options.lock.nowait === true;
|
|
3501
|
-
skipLocked = options.lock.skipLocked === true;
|
|
3502
|
-
}
|
|
3503
|
-
if (lockType === 'UPDATE') {
|
|
3504
|
-
sql += ' FOR UPDATE';
|
|
3505
|
-
// MySQL 8.0+ supports table-scoped locking via `FOR UPDATE OF <table>`
|
|
3506
|
-
if (typeof options.lock === 'object' && options.lock.of) {
|
|
3507
|
-
const model = options.lock.of;
|
|
3508
|
-
if (model.tableName) {
|
|
3509
|
-
sql += ` OF ${this.escapeId(model.tableName)}`;
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
|
-
// MySQL 8.0+ supports NOWAIT and SKIP LOCKED
|
|
3513
|
-
if (nowait) {
|
|
3514
|
-
sql += ' NOWAIT';
|
|
3515
|
-
}
|
|
3516
|
-
else if (skipLocked) {
|
|
3517
|
-
sql += ' SKIP LOCKED';
|
|
3518
|
-
}
|
|
3519
|
-
}
|
|
3520
|
-
else if (lockType === 'SHARE') {
|
|
3521
|
-
sql += ' LOCK IN SHARE MODE';
|
|
3522
|
-
// MySQL 8.0+ supports NOWAIT for SHARE mode
|
|
3523
|
-
if (nowait) {
|
|
3524
|
-
sql += ' NOWAIT';
|
|
3525
|
-
}
|
|
3526
|
-
// MySQL doesn't support SKIP LOCKED for LOCK IN SHARE MODE
|
|
3527
|
-
}
|
|
3528
|
-
}
|
|
3494
|
+
// `FOR UPDATE [OF tbl] [NOWAIT | SKIP LOCKED]` / `LOCK IN SHARE MODE`, with
|
|
3495
|
+
// every documented `lock` shape parsed by the shared helper. A shared lock
|
|
3496
|
+
// switches to the MySQL 8.0 `FOR SHARE` spelling when a NOWAIT/SKIP LOCKED
|
|
3497
|
+
// modifier is requested, since `LOCK IN SHARE MODE` accepts neither (it
|
|
3498
|
+
// used to emit an invalid `LOCK IN SHARE MODE NOWAIT`, and drop SKIP LOCKED
|
|
3499
|
+
// on the floor). See src/dialects/lock-clause-helper.ts.
|
|
3500
|
+
sql += (0, lock_clause_helper_1.appendLockClause)(options.lock, lock_clause_helper_1.MYSQL_LOCK_SPEC, (id) => this.escapeId(id));
|
|
3529
3501
|
// Build UNION clause if provided
|
|
3530
3502
|
if (options.union && options.union.length > 0) {
|
|
3531
3503
|
const unionType = options.unionType || 'UNION';
|