@mikro-orm/sql 7.0.0-dev.97 → 7.0.0-dev.99
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/AbstractSqlConnection.d.ts +57 -0
- package/AbstractSqlConnection.js +239 -0
- package/AbstractSqlDriver.d.ts +94 -0
- package/AbstractSqlDriver.js +1387 -0
- package/AbstractSqlPlatform.d.ts +38 -0
- package/AbstractSqlPlatform.js +104 -0
- package/LICENSE +21 -0
- package/PivotCollectionPersister.d.ts +22 -0
- package/PivotCollectionPersister.js +159 -0
- package/README.md +390 -0
- package/SqlEntityManager.d.ts +33 -0
- package/SqlEntityManager.js +44 -0
- package/SqlEntityRepository.d.ts +19 -0
- package/SqlEntityRepository.js +26 -0
- package/dialects/index.d.ts +4 -0
- package/dialects/index.js +4 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +14 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +200 -0
- package/dialects/mssql/index.d.ts +1 -0
- package/dialects/mssql/index.js +1 -0
- package/dialects/mysql/MySqlExceptionConverter.d.ts +9 -0
- package/dialects/mysql/MySqlExceptionConverter.js +80 -0
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +7 -0
- package/dialects/mysql/MySqlNativeQueryBuilder.js +77 -0
- package/dialects/mysql/MySqlPlatform.d.ts +45 -0
- package/dialects/mysql/MySqlPlatform.js +116 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +36 -0
- package/dialects/mysql/MySqlSchemaHelper.js +269 -0
- package/dialects/mysql/index.d.ts +4 -0
- package/dialects/mysql/index.js +4 -0
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +5 -0
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +8 -0
- package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +1 -0
- package/dialects/postgresql/PostgreSqlTableCompiler.js +1 -0
- package/dialects/postgresql/index.d.ts +1 -0
- package/dialects/postgresql/index.js +1 -0
- package/dialects/sqlite/BaseSqliteConnection.d.ts +6 -0
- package/dialects/sqlite/BaseSqliteConnection.js +8 -0
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +70 -0
- package/dialects/sqlite/BaseSqlitePlatform.js +104 -0
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +9 -0
- package/dialects/sqlite/SqliteExceptionConverter.js +54 -0
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +6 -0
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +11 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +38 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +379 -0
- package/dialects/sqlite/index.d.ts +5 -0
- package/dialects/sqlite/index.js +5 -0
- package/index.d.ts +19 -0
- package/index.js +19 -0
- package/package.json +3 -3
- package/plugin/index.d.ts +53 -0
- package/plugin/index.js +42 -0
- package/plugin/transformer.d.ts +115 -0
- package/plugin/transformer.js +883 -0
- package/query/ArrayCriteriaNode.d.ts +11 -0
- package/query/ArrayCriteriaNode.js +24 -0
- package/query/CriteriaNode.d.ts +29 -0
- package/query/CriteriaNode.js +121 -0
- package/query/CriteriaNodeFactory.d.ts +12 -0
- package/query/CriteriaNodeFactory.js +90 -0
- package/query/NativeQueryBuilder.d.ts +108 -0
- package/query/NativeQueryBuilder.js +425 -0
- package/query/ObjectCriteriaNode.d.ts +19 -0
- package/query/ObjectCriteriaNode.js +249 -0
- package/query/QueryBuilder.d.ts +389 -0
- package/query/QueryBuilder.js +1558 -0
- package/query/QueryBuilderHelper.d.ts +73 -0
- package/query/QueryBuilderHelper.js +756 -0
- package/query/ScalarCriteriaNode.d.ts +10 -0
- package/query/ScalarCriteriaNode.js +49 -0
- package/query/enums.d.ts +18 -0
- package/query/enums.js +20 -0
- package/query/index.d.ts +10 -0
- package/query/index.js +10 -0
- package/query/raw.d.ts +59 -0
- package/query/raw.js +68 -0
- package/schema/DatabaseSchema.d.ts +45 -0
- package/schema/DatabaseSchema.js +185 -0
- package/schema/DatabaseTable.d.ts +68 -0
- package/schema/DatabaseTable.js +793 -0
- package/schema/SchemaComparator.d.ts +58 -0
- package/schema/SchemaComparator.js +577 -0
- package/schema/SchemaHelper.d.ts +76 -0
- package/schema/SchemaHelper.js +545 -0
- package/schema/SqlSchemaGenerator.d.ts +65 -0
- package/schema/SqlSchemaGenerator.js +375 -0
- package/schema/index.d.ts +5 -0
- package/schema/index.js +5 -0
- package/typings.d.ts +272 -0
- package/typings.js +1 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { LockMode, raw, RawQueryFragment, Utils } from '@mikro-orm/core';
|
|
2
|
+
import { QueryType } from './enums.js';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export class NativeQueryBuilder {
|
|
5
|
+
platform;
|
|
6
|
+
type;
|
|
7
|
+
parts = [];
|
|
8
|
+
params = [];
|
|
9
|
+
options = {};
|
|
10
|
+
constructor(platform) {
|
|
11
|
+
this.platform = platform;
|
|
12
|
+
}
|
|
13
|
+
select(fields) {
|
|
14
|
+
this.type = QueryType.SELECT;
|
|
15
|
+
this.options.select ??= [];
|
|
16
|
+
this.options.select.push(...Utils.asArray(fields));
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
count(fields = '*', distinct) {
|
|
20
|
+
this.type = QueryType.COUNT;
|
|
21
|
+
this.options.select = Utils.asArray(fields);
|
|
22
|
+
this.options.distinct = distinct;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
into(tableName, options) {
|
|
26
|
+
return this.from(tableName, options);
|
|
27
|
+
}
|
|
28
|
+
from(tableName, options) {
|
|
29
|
+
if (tableName instanceof NativeQueryBuilder) {
|
|
30
|
+
const { sql, params } = tableName.compile();
|
|
31
|
+
tableName = raw(sql, params);
|
|
32
|
+
}
|
|
33
|
+
if (typeof tableName === 'string') {
|
|
34
|
+
const alias = options?.alias ? ` as ${this.platform.quoteIdentifier(options.alias)}` : '';
|
|
35
|
+
const schema = options?.schema && options.schema !== this.platform.getDefaultSchemaName() ? `${options.schema}.` : '';
|
|
36
|
+
tableName = this.quote(schema + tableName) + alias;
|
|
37
|
+
}
|
|
38
|
+
this.options.tableName = tableName;
|
|
39
|
+
this.options.indexHint = options?.indexHint;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
where(sql, params) {
|
|
43
|
+
this.options.where = { sql, params };
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
having(sql, params) {
|
|
47
|
+
this.options.having = { sql, params };
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
groupBy(groupBy) {
|
|
51
|
+
this.options.groupBy = groupBy;
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
join(sql, params) {
|
|
55
|
+
this.options.joins ??= [];
|
|
56
|
+
this.options.joins.push({ sql, params });
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
orderBy(orderBy) {
|
|
60
|
+
this.options.orderBy = orderBy;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
toString() {
|
|
64
|
+
const { sql, params } = this.compile();
|
|
65
|
+
return this.platform.formatQuery(sql, params);
|
|
66
|
+
}
|
|
67
|
+
compile() {
|
|
68
|
+
if (!this.type) {
|
|
69
|
+
throw new Error('No query type provided');
|
|
70
|
+
}
|
|
71
|
+
this.parts.length = 0;
|
|
72
|
+
this.params.length = 0;
|
|
73
|
+
if (this.options.comment) {
|
|
74
|
+
this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
|
|
75
|
+
}
|
|
76
|
+
switch (this.type) {
|
|
77
|
+
case QueryType.SELECT:
|
|
78
|
+
case QueryType.COUNT:
|
|
79
|
+
this.compileSelect();
|
|
80
|
+
break;
|
|
81
|
+
case QueryType.INSERT:
|
|
82
|
+
this.compileInsert();
|
|
83
|
+
break;
|
|
84
|
+
case QueryType.UPDATE:
|
|
85
|
+
this.compileUpdate();
|
|
86
|
+
break;
|
|
87
|
+
case QueryType.DELETE:
|
|
88
|
+
this.compileDelete();
|
|
89
|
+
break;
|
|
90
|
+
case QueryType.TRUNCATE:
|
|
91
|
+
this.compileTruncate();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
this.addOnConflictClause();
|
|
95
|
+
if (this.options.returning && this.platform.usesReturningStatement()) {
|
|
96
|
+
const fields = this.options.returning.map(field => this.quote(field));
|
|
97
|
+
this.parts.push(`returning ${fields.join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
this.addLockClause();
|
|
100
|
+
return this.combineParts();
|
|
101
|
+
}
|
|
102
|
+
addLockClause() {
|
|
103
|
+
if (!this.options.lockMode) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if ([LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_PARTIAL_READ, LockMode.PESSIMISTIC_READ_OR_FAIL].includes(this.options.lockMode)) {
|
|
107
|
+
this.parts.push('for share');
|
|
108
|
+
}
|
|
109
|
+
if ([LockMode.PESSIMISTIC_WRITE, LockMode.PESSIMISTIC_PARTIAL_WRITE, LockMode.PESSIMISTIC_WRITE_OR_FAIL].includes(this.options.lockMode)) {
|
|
110
|
+
this.parts.push('for update');
|
|
111
|
+
}
|
|
112
|
+
if (this.options.lockTables?.length) {
|
|
113
|
+
const fields = this.options.lockTables.map(field => this.quote(field));
|
|
114
|
+
this.parts.push(`of ${fields.join(', ')}`);
|
|
115
|
+
}
|
|
116
|
+
if ([LockMode.PESSIMISTIC_PARTIAL_READ, LockMode.PESSIMISTIC_PARTIAL_WRITE].includes(this.options.lockMode)) {
|
|
117
|
+
this.parts.push('skip locked');
|
|
118
|
+
}
|
|
119
|
+
if ([LockMode.PESSIMISTIC_READ_OR_FAIL, LockMode.PESSIMISTIC_WRITE_OR_FAIL].includes(this.options.lockMode)) {
|
|
120
|
+
this.parts.push('nowait');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
addOnConflictClause() {
|
|
124
|
+
const clause = this.options.onConflict;
|
|
125
|
+
if (!clause) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.parts.push('on conflict');
|
|
129
|
+
if (clause.fields instanceof RawQueryFragment) {
|
|
130
|
+
this.parts.push(clause.fields.sql);
|
|
131
|
+
this.params.push(...clause.fields.params);
|
|
132
|
+
}
|
|
133
|
+
else if (clause.fields.length > 0) {
|
|
134
|
+
const fields = clause.fields.map(field => this.quote(field));
|
|
135
|
+
this.parts.push(`(${fields.join(', ')})`);
|
|
136
|
+
}
|
|
137
|
+
if (clause.ignore) {
|
|
138
|
+
this.parts.push('do nothing');
|
|
139
|
+
}
|
|
140
|
+
if (Utils.isObject(clause.merge)) {
|
|
141
|
+
this.parts.push('do update set');
|
|
142
|
+
const fields = Object.keys(clause.merge).map(field => {
|
|
143
|
+
this.params.push(clause.merge[field]);
|
|
144
|
+
return `${this.quote(field)} = ?`;
|
|
145
|
+
});
|
|
146
|
+
this.parts.push(fields.join(', '));
|
|
147
|
+
}
|
|
148
|
+
else if (clause.merge) {
|
|
149
|
+
this.parts.push('do update set');
|
|
150
|
+
if (clause.merge.length) {
|
|
151
|
+
const fields = clause.merge.map(field => `${this.quote(field)} = excluded.${this.quote(field)}`);
|
|
152
|
+
this.parts.push(fields.join(', '));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const dataAsArray = Utils.asArray(this.options.data);
|
|
156
|
+
const keys = Object.keys(dataAsArray[0]);
|
|
157
|
+
const fields = keys.map(field => `${this.quote(field)} = excluded.${this.quote(field)}`);
|
|
158
|
+
this.parts.push(fields.join(', '));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (clause.where) {
|
|
162
|
+
this.parts.push(`where ${clause.where.sql}`);
|
|
163
|
+
this.params.push(...clause.where.params);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
combineParts() {
|
|
167
|
+
let sql = this.parts.join(' ');
|
|
168
|
+
if (this.options.wrap) {
|
|
169
|
+
const [a, b] = this.options.wrap;
|
|
170
|
+
sql = `${a}${sql}${b}`;
|
|
171
|
+
}
|
|
172
|
+
return { sql, params: this.params };
|
|
173
|
+
}
|
|
174
|
+
limit(limit) {
|
|
175
|
+
this.options.limit = limit;
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
offset(offset) {
|
|
179
|
+
this.options.offset = offset;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
insert(data) {
|
|
183
|
+
this.type = QueryType.INSERT;
|
|
184
|
+
this.options.data = data;
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
update(data) {
|
|
188
|
+
this.type = QueryType.UPDATE;
|
|
189
|
+
this.options.data ??= {};
|
|
190
|
+
Object.assign(this.options.data, data);
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
delete() {
|
|
194
|
+
this.type = QueryType.DELETE;
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
truncate() {
|
|
198
|
+
this.type = QueryType.TRUNCATE;
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
distinct() {
|
|
202
|
+
this.options.distinct = true;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
distinctOn(fields) {
|
|
206
|
+
this.options.distinctOn = fields;
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
onConflict(options) {
|
|
210
|
+
this.options.onConflict = options;
|
|
211
|
+
return options;
|
|
212
|
+
}
|
|
213
|
+
returning(fields) {
|
|
214
|
+
this.options.returning = fields;
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
lockMode(lockMode, lockTables) {
|
|
218
|
+
this.options.lockMode = lockMode;
|
|
219
|
+
this.options.lockTables = lockTables;
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
comment(comment) {
|
|
223
|
+
this.options.comment ??= [];
|
|
224
|
+
this.options.comment.push(...Utils.asArray(comment));
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
hintComment(comment) {
|
|
228
|
+
this.options.hintComment ??= [];
|
|
229
|
+
this.options.hintComment.push(...Utils.asArray(comment));
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
setFlags(flags) {
|
|
233
|
+
this.options.flags = flags;
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
clear(clause) {
|
|
237
|
+
delete this.options[clause];
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
wrap(prefix, suffix) {
|
|
241
|
+
this.options.wrap = [prefix, suffix];
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
as(alias) {
|
|
245
|
+
this.wrap('(', `) as ${this.platform.quoteIdentifier(alias)}`);
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
toRaw() {
|
|
249
|
+
const { sql, params } = this.compile();
|
|
250
|
+
return raw(sql, params);
|
|
251
|
+
}
|
|
252
|
+
compileSelect() {
|
|
253
|
+
this.parts.push('select');
|
|
254
|
+
this.addHintComment();
|
|
255
|
+
this.parts.push(`${this.getFields()} from ${this.getTableName()}`);
|
|
256
|
+
if (this.options.joins) {
|
|
257
|
+
for (const join of this.options.joins) {
|
|
258
|
+
this.parts.push(join.sql);
|
|
259
|
+
this.params.push(...join.params);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (this.options.where?.sql.trim()) {
|
|
263
|
+
this.parts.push(`where ${this.options.where.sql}`);
|
|
264
|
+
this.options.where.params.forEach(p => this.params.push(p));
|
|
265
|
+
}
|
|
266
|
+
if (this.options.groupBy) {
|
|
267
|
+
const fields = this.options.groupBy.map(field => this.quote(field));
|
|
268
|
+
this.parts.push(`group by ${fields.join(', ')}`);
|
|
269
|
+
}
|
|
270
|
+
if (this.options.having) {
|
|
271
|
+
this.parts.push(`having ${this.options.having.sql}`);
|
|
272
|
+
this.params.push(...this.options.having.params);
|
|
273
|
+
}
|
|
274
|
+
if (this.options.orderBy) {
|
|
275
|
+
this.parts.push(`order by ${this.options.orderBy}`);
|
|
276
|
+
}
|
|
277
|
+
if (this.options.limit != null) {
|
|
278
|
+
this.parts.push(`limit ?`);
|
|
279
|
+
this.params.push(this.options.limit);
|
|
280
|
+
}
|
|
281
|
+
if (this.options.offset != null) {
|
|
282
|
+
this.parts.push(`offset ?`);
|
|
283
|
+
this.params.push(this.options.offset);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
getFields() {
|
|
287
|
+
if (!this.options.select || this.options.select.length === 0) {
|
|
288
|
+
throw new Error('No fields selected');
|
|
289
|
+
}
|
|
290
|
+
let fields = this.options.select.map(field => this.quote(field)).join(', ');
|
|
291
|
+
if (this.options.distinct) {
|
|
292
|
+
fields = `distinct ${fields}`;
|
|
293
|
+
}
|
|
294
|
+
else if (this.options.distinctOn) {
|
|
295
|
+
fields = `distinct on (${this.options.distinctOn.map(field => this.quote(field)).join(', ')}) ${fields}`;
|
|
296
|
+
}
|
|
297
|
+
if (this.type === QueryType.COUNT) {
|
|
298
|
+
fields = `count(${fields}) as ${this.quote('count')}`;
|
|
299
|
+
}
|
|
300
|
+
return fields;
|
|
301
|
+
}
|
|
302
|
+
compileInsert() {
|
|
303
|
+
if (!this.options.data) {
|
|
304
|
+
throw new Error('No data provided');
|
|
305
|
+
}
|
|
306
|
+
this.parts.push('insert');
|
|
307
|
+
this.addHintComment();
|
|
308
|
+
this.parts.push(`into ${this.getTableName()}`);
|
|
309
|
+
if (Object.keys(this.options.data).length === 0) {
|
|
310
|
+
this.addOutputClause('inserted');
|
|
311
|
+
this.parts.push('default values');
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const parts = this.processInsertData();
|
|
315
|
+
this.parts.push(parts.join(', '));
|
|
316
|
+
}
|
|
317
|
+
addOutputClause(type) {
|
|
318
|
+
if (this.options.returning && this.platform.usesOutputStatement()) {
|
|
319
|
+
const fields = this.options.returning.map(field => `${type}.${this.quote(field)}`);
|
|
320
|
+
this.parts.push(`output ${fields.join(', ')}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
processInsertData() {
|
|
324
|
+
const dataAsArray = Utils.asArray(this.options.data);
|
|
325
|
+
const keys = Object.keys(dataAsArray[0]);
|
|
326
|
+
const values = keys.map(() => '?');
|
|
327
|
+
const parts = [];
|
|
328
|
+
this.parts.push(`(${keys.map(key => this.quote(key)).join(', ')})`);
|
|
329
|
+
this.addOutputClause('inserted');
|
|
330
|
+
this.parts.push('values');
|
|
331
|
+
for (const data of dataAsArray) {
|
|
332
|
+
for (const key of keys) {
|
|
333
|
+
if (typeof data[key] === 'undefined') {
|
|
334
|
+
this.params.push(this.platform.usesDefaultKeyword() ? raw('default') : null);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
this.params.push(data[key]);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
parts.push(`(${values.join(', ')})`);
|
|
341
|
+
}
|
|
342
|
+
return parts;
|
|
343
|
+
}
|
|
344
|
+
compileUpdate() {
|
|
345
|
+
if (!this.options.data || Object.keys(this.options.data).length === 0) {
|
|
346
|
+
throw new Error('No data provided');
|
|
347
|
+
}
|
|
348
|
+
this.parts.push('update');
|
|
349
|
+
this.addHintComment();
|
|
350
|
+
this.parts.push(this.getTableName());
|
|
351
|
+
if (this.options.joins) {
|
|
352
|
+
for (const join of this.options.joins) {
|
|
353
|
+
this.parts.push(join.sql);
|
|
354
|
+
this.params.push(...join.params);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
this.parts.push('set');
|
|
358
|
+
if (this.options.data) {
|
|
359
|
+
const parts = [];
|
|
360
|
+
for (const key of Object.keys(this.options.data)) {
|
|
361
|
+
parts.push(`${this.quote(key)} = ?`);
|
|
362
|
+
this.params.push(this.options.data[key]);
|
|
363
|
+
}
|
|
364
|
+
this.parts.push(parts.join(', '));
|
|
365
|
+
}
|
|
366
|
+
this.addOutputClause('inserted');
|
|
367
|
+
if (this.options.where?.sql.trim()) {
|
|
368
|
+
this.parts.push(`where ${this.options.where.sql}`);
|
|
369
|
+
this.params.push(...this.options.where.params);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
compileDelete() {
|
|
373
|
+
this.parts.push('delete');
|
|
374
|
+
this.addHintComment();
|
|
375
|
+
this.parts.push(`from ${this.getTableName()}`);
|
|
376
|
+
this.addOutputClause('deleted');
|
|
377
|
+
if (this.options.where?.sql.trim()) {
|
|
378
|
+
this.parts.push(`where ${this.options.where.sql}`);
|
|
379
|
+
this.params.push(...this.options.where.params);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
compileTruncate() {
|
|
383
|
+
const sql = `truncate table ${this.getTableName()}`;
|
|
384
|
+
this.parts.push(sql);
|
|
385
|
+
}
|
|
386
|
+
addHintComment() {
|
|
387
|
+
if (this.options.hintComment) {
|
|
388
|
+
this.parts.push(`/*+ ${this.options.hintComment.join(' ')} */`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
getTableName() {
|
|
392
|
+
if (!this.options.tableName) {
|
|
393
|
+
throw new Error('No table name provided');
|
|
394
|
+
}
|
|
395
|
+
const indexHint = this.options.indexHint ? ' ' + this.options.indexHint : '';
|
|
396
|
+
if (this.options.tableName instanceof RawQueryFragment) {
|
|
397
|
+
this.params.push(...this.options.tableName.params);
|
|
398
|
+
return this.options.tableName.sql + indexHint;
|
|
399
|
+
}
|
|
400
|
+
return this.options.tableName + indexHint;
|
|
401
|
+
}
|
|
402
|
+
quote(id) {
|
|
403
|
+
if (id instanceof RawQueryFragment) {
|
|
404
|
+
return this.platform.formatQuery(id.sql, id.params);
|
|
405
|
+
}
|
|
406
|
+
if (id instanceof NativeQueryBuilder) {
|
|
407
|
+
const { sql, params } = id.compile();
|
|
408
|
+
return this.platform.formatQuery(sql, params);
|
|
409
|
+
}
|
|
410
|
+
if (id.endsWith('.*')) {
|
|
411
|
+
const schema = this.platform.quoteIdentifier(id.substring(0, id.indexOf('.')));
|
|
412
|
+
return schema + '.*';
|
|
413
|
+
}
|
|
414
|
+
if (id.toLowerCase().includes(' as ')) {
|
|
415
|
+
const parts = id.split(/ as /i);
|
|
416
|
+
const a = this.platform.quoteIdentifier(parts[0]);
|
|
417
|
+
const b = this.platform.quoteIdentifier(parts[1]);
|
|
418
|
+
return `${a} as ${b}`;
|
|
419
|
+
}
|
|
420
|
+
if (id === '*') {
|
|
421
|
+
return id;
|
|
422
|
+
}
|
|
423
|
+
return this.platform.quoteIdentifier(id);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CriteriaNode } from './CriteriaNode.js';
|
|
2
|
+
import type { ICriteriaNodeProcessOptions, IQueryBuilder } from '../typings.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
+
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
|
+
isStrict(): boolean;
|
|
9
|
+
unwrap(): any;
|
|
10
|
+
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
|
|
11
|
+
shouldInline(payload: any): boolean;
|
|
12
|
+
private getChildKey;
|
|
13
|
+
private inlineArrayChildPayload;
|
|
14
|
+
private inlineChildPayload;
|
|
15
|
+
private inlineCondition;
|
|
16
|
+
private shouldAutoJoin;
|
|
17
|
+
private autoJoin;
|
|
18
|
+
private isPrefixed;
|
|
19
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
|
|
2
|
+
import { CriteriaNode } from './CriteriaNode.js';
|
|
3
|
+
import { JoinType, QueryType } from './enums.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export class ObjectCriteriaNode extends CriteriaNode {
|
|
8
|
+
process(qb, options) {
|
|
9
|
+
const matchPopulateJoins = options?.matchPopulateJoins || (this.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind));
|
|
10
|
+
const nestedAlias = qb.getAliasForJoinPath(this.getPath(), { ...options, matchPopulateJoins });
|
|
11
|
+
const ownerAlias = options?.alias || qb.alias;
|
|
12
|
+
const keys = Object.keys(this.payload);
|
|
13
|
+
let alias = options?.alias;
|
|
14
|
+
if (nestedAlias) {
|
|
15
|
+
alias = nestedAlias;
|
|
16
|
+
}
|
|
17
|
+
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
18
|
+
if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
|
|
19
|
+
if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
|
|
20
|
+
// ignore collection operators when used on a non-relational property - this can happen when they get into
|
|
21
|
+
// populateWhere via `infer` on m:n properties with select-in strategy
|
|
22
|
+
if (this.parent?.parent) { // we validate only usage on top level
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`Collection operators can be used only inside a collection property context, but it was used for ${this.getPath()}.`);
|
|
26
|
+
}
|
|
27
|
+
const $and = [];
|
|
28
|
+
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
29
|
+
const parentMeta = this.metadata.find(this.parent.entityName);
|
|
30
|
+
const primaryKeys = parentMeta.primaryKeys.map(pk => {
|
|
31
|
+
return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
|
|
32
|
+
});
|
|
33
|
+
for (const key of keys) {
|
|
34
|
+
if (!['$some', '$none', '$every'].includes(key)) {
|
|
35
|
+
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
36
|
+
}
|
|
37
|
+
const payload = this.payload[key].unwrap();
|
|
38
|
+
const qb2 = qb.clone(true);
|
|
39
|
+
const sub = qb2
|
|
40
|
+
.from(parentMeta.className)
|
|
41
|
+
.innerJoin(this.key, qb2.getNextAlias(this.prop.type))
|
|
42
|
+
.select(parentMeta.primaryKeys);
|
|
43
|
+
if (key === '$every') {
|
|
44
|
+
sub.where({ $not: { [this.key]: payload } });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
sub.where({ [this.key]: payload });
|
|
48
|
+
}
|
|
49
|
+
const op = key === '$some' ? '$in' : '$nin';
|
|
50
|
+
$and.push({
|
|
51
|
+
[Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if ($and.length === 1) {
|
|
55
|
+
return $and[0];
|
|
56
|
+
}
|
|
57
|
+
return { $and };
|
|
58
|
+
}
|
|
59
|
+
alias = this.autoJoin(qb, ownerAlias, options);
|
|
60
|
+
}
|
|
61
|
+
if (this.prop && nestedAlias) {
|
|
62
|
+
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
63
|
+
// if the property is nullable and the filter is strict, we need to use left join, so we mimic the inner join behaviour
|
|
64
|
+
// with an exclusive condition on the join columns:
|
|
65
|
+
// - if the owning column is null, the row is missing, we don't apply the filter
|
|
66
|
+
// - if the target column is not null, the row is matched, we apply the filter
|
|
67
|
+
if (toOneProperty && this.prop.nullable && this.isStrict()) {
|
|
68
|
+
const key = this.prop.owner ? this.prop.name : this.prop.referencedPKs;
|
|
69
|
+
qb.andWhere({
|
|
70
|
+
$or: [
|
|
71
|
+
{ [ownerAlias + '.' + key]: null },
|
|
72
|
+
{ [nestedAlias + '.' + Utils.getPrimaryKeyHash(this.prop.referencedPKs)]: { $ne: null } },
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return keys.reduce((o, field) => {
|
|
78
|
+
const childNode = this.payload[field];
|
|
79
|
+
const payload = childNode.process(qb, { ...options, alias: this.prop ? alias : ownerAlias });
|
|
80
|
+
const operator = Utils.isOperator(field);
|
|
81
|
+
const isRawField = RawQueryFragment.isKnownFragment(field);
|
|
82
|
+
// we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
|
|
83
|
+
const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
|
|
84
|
+
// if key is missing, we are inside group operator and we need to prefix with alias
|
|
85
|
+
const primaryKey = this.key && this.metadata.find(this.entityName)?.primaryKeys.includes(field);
|
|
86
|
+
const isToOne = childNode.prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(childNode.prop.kind);
|
|
87
|
+
if (childNode.shouldInline(payload)) {
|
|
88
|
+
const childAlias = qb.getAliasForJoinPath(childNode.getPath(), { preferNoBranch: isToOne, ...options });
|
|
89
|
+
const a = qb.helper.isTableNameAliasRequired(qb.type) ? alias : undefined;
|
|
90
|
+
this.inlineChildPayload(o, payload, field, a, childAlias);
|
|
91
|
+
}
|
|
92
|
+
else if (childNode.shouldRename(payload)) {
|
|
93
|
+
this.inlineCondition(childNode.renameFieldToPK(qb, alias), o, payload);
|
|
94
|
+
}
|
|
95
|
+
else if (isRawField) {
|
|
96
|
+
const rawField = RawQueryFragment.getKnownFragment(field);
|
|
97
|
+
o[raw(rawField.sql.replaceAll(ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
|
|
98
|
+
}
|
|
99
|
+
else if (primaryKey || virtual || operator || field.includes('.') || ![QueryType.SELECT, QueryType.COUNT].includes(qb.type)) {
|
|
100
|
+
this.inlineCondition(field.replaceAll(ALIAS_REPLACEMENT, alias), o, payload);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
this.inlineCondition(`${alias}.${field}`, o, payload);
|
|
104
|
+
}
|
|
105
|
+
return o;
|
|
106
|
+
}, {});
|
|
107
|
+
}
|
|
108
|
+
isStrict() {
|
|
109
|
+
return this.strict || Object.keys(this.payload).some(key => {
|
|
110
|
+
return this.payload[key].isStrict();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
unwrap() {
|
|
114
|
+
return Object.keys(this.payload).reduce((o, field) => {
|
|
115
|
+
o[field] = this.payload[field].unwrap();
|
|
116
|
+
return o;
|
|
117
|
+
}, {});
|
|
118
|
+
}
|
|
119
|
+
willAutoJoin(qb, alias, options) {
|
|
120
|
+
const nestedAlias = qb.getAliasForJoinPath(this.getPath(), options);
|
|
121
|
+
const ownerAlias = alias || qb.alias;
|
|
122
|
+
const keys = Object.keys(this.payload);
|
|
123
|
+
if (nestedAlias) {
|
|
124
|
+
alias = nestedAlias;
|
|
125
|
+
}
|
|
126
|
+
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
127
|
+
return !keys.some(k => ['$some', '$none', '$every'].includes(k));
|
|
128
|
+
}
|
|
129
|
+
return keys.some(field => {
|
|
130
|
+
const childNode = this.payload[field];
|
|
131
|
+
return childNode.willAutoJoin(qb, this.prop ? alias : ownerAlias, options);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
shouldInline(payload) {
|
|
135
|
+
const customExpression = RawQueryFragment.isKnownFragment(this.key);
|
|
136
|
+
const scalar = Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression;
|
|
137
|
+
const operator = Utils.isObject(payload) && Object.keys(payload).every(k => Utils.isOperator(k, false));
|
|
138
|
+
return !!this.prop && this.prop.kind !== ReferenceKind.SCALAR && !scalar && !operator;
|
|
139
|
+
}
|
|
140
|
+
getChildKey(k, prop, childAlias, alias) {
|
|
141
|
+
const idx = prop.referencedPKs.indexOf(k);
|
|
142
|
+
return idx !== -1 && !childAlias && ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)
|
|
143
|
+
? this.aliased(prop.joinColumns[idx], alias)
|
|
144
|
+
: k;
|
|
145
|
+
}
|
|
146
|
+
inlineArrayChildPayload(obj, payload, k, prop, childAlias, alias) {
|
|
147
|
+
const key = this.getChildKey(k, prop, childAlias);
|
|
148
|
+
const value = payload.map((child) => Object.keys(child).reduce((inner, childKey) => {
|
|
149
|
+
const key = (this.isPrefixed(childKey) || Utils.isOperator(childKey)) ? childKey : this.aliased(childKey, childAlias);
|
|
150
|
+
inner[key] = child[childKey];
|
|
151
|
+
return inner;
|
|
152
|
+
}, {}));
|
|
153
|
+
this.inlineCondition(key, obj, value);
|
|
154
|
+
}
|
|
155
|
+
inlineChildPayload(o, payload, field, alias, childAlias) {
|
|
156
|
+
const prop = this.metadata.find(this.entityName).properties[field];
|
|
157
|
+
for (const k of Object.keys(payload)) {
|
|
158
|
+
if (Utils.isOperator(k, false)) {
|
|
159
|
+
const tmp = payload[k];
|
|
160
|
+
delete payload[k];
|
|
161
|
+
o[this.aliased(field, alias)] = { [k]: tmp, ...o[this.aliased(field, alias)] };
|
|
162
|
+
}
|
|
163
|
+
else if (k in GroupOperator && Array.isArray(payload[k])) {
|
|
164
|
+
this.inlineArrayChildPayload(o, payload[k], k, prop, childAlias, alias);
|
|
165
|
+
}
|
|
166
|
+
else if (this.isPrefixed(k) || Utils.isOperator(k) || !childAlias) {
|
|
167
|
+
const key = this.getChildKey(k, prop, childAlias, alias);
|
|
168
|
+
this.inlineCondition(key, o, payload[k]);
|
|
169
|
+
}
|
|
170
|
+
else if (RawQueryFragment.isKnownFragment(k)) {
|
|
171
|
+
o[k] = payload[k];
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
o[this.aliased(k, childAlias)] = payload[k];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
inlineCondition(key, o, value) {
|
|
179
|
+
if (!(key in o)) {
|
|
180
|
+
o[key] = value;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
/* v8 ignore next */
|
|
184
|
+
if (key === '$and') {
|
|
185
|
+
o.$and.push({ [key]: value });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const $and = o.$and ?? [];
|
|
189
|
+
$and.push({ [key]: o[key] }, { [key]: value });
|
|
190
|
+
delete o[key];
|
|
191
|
+
o.$and = $and;
|
|
192
|
+
}
|
|
193
|
+
shouldAutoJoin(qb, nestedAlias) {
|
|
194
|
+
if (!this.prop || !this.parent) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
const keys = Object.keys(this.payload);
|
|
198
|
+
if (keys.every(k => k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
if (keys.some(k => ['$some', '$none', '$every'].includes(k))) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
const meta = this.metadata.find(this.entityName);
|
|
205
|
+
const embeddable = this.prop.kind === ReferenceKind.EMBEDDED;
|
|
206
|
+
const knownKey = [ReferenceKind.SCALAR, ReferenceKind.MANY_TO_ONE, ReferenceKind.EMBEDDED].includes(this.prop.kind) || (this.prop.kind === ReferenceKind.ONE_TO_ONE && this.prop.owner);
|
|
207
|
+
const operatorKeys = knownKey && keys.every(key => Utils.isOperator(key, false));
|
|
208
|
+
const primaryKeys = knownKey && keys.every(key => {
|
|
209
|
+
if (!meta.primaryKeys.includes(key)) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
if (!Utils.isPlainObject(this.payload[key].payload) || ![ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[key].kind)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return Object.keys(this.payload[key].payload).every(k => meta.properties[key].targetMeta.primaryKeys.includes(k));
|
|
216
|
+
});
|
|
217
|
+
return !primaryKeys && !nestedAlias && !operatorKeys && !embeddable;
|
|
218
|
+
}
|
|
219
|
+
autoJoin(qb, alias, options) {
|
|
220
|
+
const nestedAlias = qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
|
|
221
|
+
const customExpression = RawQueryFragment.isKnownFragment(this.key);
|
|
222
|
+
const scalar = Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression;
|
|
223
|
+
const operator = Utils.isPlainObject(this.payload) && Object.keys(this.payload).every(k => Utils.isOperator(k, false));
|
|
224
|
+
const field = `${alias}.${this.prop.name}`;
|
|
225
|
+
const method = qb.hasFlag(QueryFlag.INFER_POPULATE) ? 'joinAndSelect' : 'join';
|
|
226
|
+
const path = this.getPath();
|
|
227
|
+
if (this.prop.kind === ReferenceKind.MANY_TO_MANY && (scalar || operator)) {
|
|
228
|
+
qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const prev = qb._fields?.slice();
|
|
232
|
+
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
233
|
+
const joinType = toOneProperty && !this.prop.nullable
|
|
234
|
+
? JoinType.innerJoin
|
|
235
|
+
: JoinType.leftJoin;
|
|
236
|
+
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
237
|
+
if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
|
|
238
|
+
qb._fields = prev;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (options?.type !== 'orderBy') {
|
|
242
|
+
qb.scheduleFilterCheck(path);
|
|
243
|
+
}
|
|
244
|
+
return nestedAlias;
|
|
245
|
+
}
|
|
246
|
+
isPrefixed(field) {
|
|
247
|
+
return !!field.match(/\w+\./);
|
|
248
|
+
}
|
|
249
|
+
}
|