@mikro-orm/knex 6.4.6-dev.9 → 7.0.0-dev.1
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 +15 -25
- package/AbstractSqlConnection.js +101 -126
- package/AbstractSqlDriver.d.ts +20 -13
- package/AbstractSqlDriver.js +73 -54
- package/AbstractSqlPlatform.d.ts +15 -3
- package/AbstractSqlPlatform.js +32 -11
- package/README.md +0 -2
- package/SqlEntityManager.d.ts +5 -6
- package/SqlEntityManager.js +5 -5
- package/SqlEntityRepository.d.ts +1 -6
- package/SqlEntityRepository.js +0 -6
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +161 -0
- package/dialects/mssql/index.d.ts +1 -1
- package/dialects/mssql/index.js +1 -1
- package/dialects/mysql/MySqlExceptionConverter.js +1 -0
- package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +7 -0
- package/dialects/mysql/MySqlNativeQueryBuilder.js +81 -0
- package/dialects/mysql/MySqlPlatform.d.ts +10 -2
- package/dialects/mysql/MySqlPlatform.js +23 -1
- package/dialects/mysql/MySqlSchemaHelper.d.ts +6 -12
- package/dialects/mysql/MySqlSchemaHelper.js +42 -75
- package/dialects/mysql/index.d.ts +1 -3
- package/dialects/mysql/index.js +1 -3
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +5 -0
- package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +12 -0
- package/dialects/postgresql/index.d.ts +1 -1
- package/dialects/postgresql/index.js +1 -1
- package/dialects/sqlite/BaseSqliteConnection.d.ts +0 -5
- package/dialects/sqlite/BaseSqliteConnection.js +4 -42
- package/dialects/sqlite/BaseSqlitePlatform.d.ts +15 -3
- package/dialects/sqlite/BaseSqlitePlatform.js +20 -4
- package/dialects/sqlite/SqliteExceptionConverter.d.ts +9 -0
- package/dialects/sqlite/SqliteExceptionConverter.js +55 -0
- package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +6 -0
- package/dialects/sqlite/SqliteNativeQueryBuilder.js +15 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +38 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +384 -0
- package/dialects/sqlite/index.d.ts +3 -5
- package/dialects/sqlite/index.js +3 -5
- package/index.d.ts +1 -2
- package/index.js +3 -5
- package/index.mjs +10 -13
- package/package.json +4 -18
- package/query/CriteriaNodeFactory.js +5 -5
- package/query/NativeQueryBuilder.d.ts +108 -0
- package/query/NativeQueryBuilder.js +429 -0
- package/query/ObjectCriteriaNode.js +3 -3
- package/query/QueryBuilder.d.ts +30 -34
- package/query/QueryBuilder.js +112 -123
- package/query/QueryBuilderHelper.d.ts +27 -23
- package/query/QueryBuilderHelper.js +174 -168
- package/query/ScalarCriteriaNode.js +4 -0
- package/query/index.d.ts +1 -0
- package/query/index.js +1 -0
- package/schema/DatabaseSchema.js +9 -6
- package/schema/DatabaseTable.d.ts +2 -1
- package/schema/DatabaseTable.js +9 -5
- package/schema/SchemaComparator.d.ts +1 -2
- package/schema/SchemaComparator.js +31 -18
- package/schema/SchemaHelper.d.ts +27 -33
- package/schema/SchemaHelper.js +294 -184
- package/schema/SqlSchemaGenerator.d.ts +3 -9
- package/schema/SqlSchemaGenerator.js +105 -229
- package/typings.d.ts +7 -17
- package/MonkeyPatchable.d.ts +0 -18
- package/MonkeyPatchable.js +0 -60
- package/dialects/mssql/MsSqlColumnCompiler.d.ts +0 -4
- package/dialects/mssql/MsSqlColumnCompiler.js +0 -10
- package/dialects/mssql/MsSqlKnexDialect.d.ts +0 -6
- package/dialects/mssql/MsSqlKnexDialect.js +0 -22
- package/dialects/mssql/MsSqlQueryCompiler.d.ts +0 -12
- package/dialects/mssql/MsSqlQueryCompiler.js +0 -94
- package/dialects/mssql/MsSqlTableCompiler.d.ts +0 -9
- package/dialects/mssql/MsSqlTableCompiler.js +0 -40
- package/dialects/mysql/MariaDbKnexDialect.d.ts +0 -6
- package/dialects/mysql/MariaDbKnexDialect.js +0 -16
- package/dialects/mysql/MySqlColumnCompiler.d.ts +0 -7
- package/dialects/mysql/MySqlColumnCompiler.js +0 -26
- package/dialects/mysql/MySqlConnection.d.ts +0 -8
- package/dialects/mysql/MySqlConnection.js +0 -43
- package/dialects/mysql/MySqlKnexDialect.d.ts +0 -5
- package/dialects/mysql/MySqlKnexDialect.js +0 -17
- package/dialects/mysql/MySqlQueryCompiler.d.ts +0 -5
- package/dialects/mysql/MySqlQueryCompiler.js +0 -23
- package/dialects/postgresql/PostgreSqlKnexDialect.d.ts +0 -7
- package/dialects/postgresql/PostgreSqlKnexDialect.js +0 -20
- package/dialects/postgresql/PostgreSqlQueryCompiler.d.ts +0 -4
- package/dialects/postgresql/PostgreSqlQueryCompiler.js +0 -13
- package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +0 -11
- package/dialects/postgresql/PostgreSqlTableCompiler.js +0 -89
- package/dialects/sqlite/BaseSqliteSchemaHelper.d.ts +0 -28
- package/dialects/sqlite/BaseSqliteSchemaHelper.js +0 -200
- package/dialects/sqlite/BetterSqliteKnexDialect.d.ts +0 -5
- package/dialects/sqlite/BetterSqliteKnexDialect.js +0 -15
- package/dialects/sqlite/LibSqlKnexDialect.d.ts +0 -11
- package/dialects/sqlite/LibSqlKnexDialect.js +0 -85
- package/dialects/sqlite/SqliteKnexDialect.d.ts +0 -8
- package/dialects/sqlite/SqliteKnexDialect.js +0 -67
- package/dialects/sqlite/SqliteTableCompiler.d.ts +0 -6
- package/dialects/sqlite/SqliteTableCompiler.js +0 -71
|
@@ -4,6 +4,7 @@ exports.QueryBuilderHelper = void 0;
|
|
|
4
4
|
const node_util_1 = require("node:util");
|
|
5
5
|
const core_1 = require("@mikro-orm/core");
|
|
6
6
|
const enums_1 = require("./enums");
|
|
7
|
+
const NativeQueryBuilder_1 = require("./NativeQueryBuilder");
|
|
7
8
|
/**
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
@@ -12,23 +13,21 @@ class QueryBuilderHelper {
|
|
|
12
13
|
alias;
|
|
13
14
|
aliasMap;
|
|
14
15
|
subQueries;
|
|
15
|
-
knex;
|
|
16
16
|
driver;
|
|
17
17
|
platform;
|
|
18
18
|
metadata;
|
|
19
|
-
constructor(entityName, alias, aliasMap, subQueries,
|
|
19
|
+
constructor(entityName, alias, aliasMap, subQueries, driver) {
|
|
20
20
|
this.entityName = entityName;
|
|
21
21
|
this.alias = alias;
|
|
22
22
|
this.aliasMap = aliasMap;
|
|
23
23
|
this.subQueries = subQueries;
|
|
24
|
-
this.knex = knex;
|
|
25
24
|
this.driver = driver;
|
|
26
25
|
this.platform = this.driver.getPlatform();
|
|
27
26
|
this.metadata = this.driver.getMetadata();
|
|
28
27
|
}
|
|
29
28
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
30
|
-
if (core_1.
|
|
31
|
-
return
|
|
29
|
+
if ((0, core_1.isRaw)(field)) {
|
|
30
|
+
return (0, core_1.raw)(field.sql, field.params);
|
|
32
31
|
}
|
|
33
32
|
/* istanbul ignore next */
|
|
34
33
|
if (typeof field !== 'string') {
|
|
@@ -69,13 +68,13 @@ class QueryBuilderHelper {
|
|
|
69
68
|
if (parts.length === 1) {
|
|
70
69
|
return parts[0];
|
|
71
70
|
}
|
|
72
|
-
return
|
|
71
|
+
return (0, core_1.raw)('(' + parts.map(part => this.platform.quoteIdentifier(part)).join(', ') + ')');
|
|
73
72
|
}
|
|
74
73
|
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
75
74
|
if (rawField) {
|
|
76
|
-
|
|
77
|
-
return this.knex.raw(this.platform.formatQuery(rawField.sql, rawField.params));
|
|
75
|
+
return rawField;
|
|
78
76
|
}
|
|
77
|
+
const aliasPrefix = isTableNameAliasRequired ? this.alias + '.' : '';
|
|
79
78
|
const [a, f] = this.splitField(field);
|
|
80
79
|
const prop = this.getProperty(f, a);
|
|
81
80
|
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
@@ -83,21 +82,24 @@ class QueryBuilderHelper {
|
|
|
83
82
|
let ret = field;
|
|
84
83
|
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
85
84
|
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
86
|
-
return
|
|
85
|
+
return aliasPrefix + prop.fieldNames[fkIdx];
|
|
86
|
+
}
|
|
87
|
+
if (prop?.embedded && a === prop.embedded[0]) {
|
|
88
|
+
return aliasPrefix + prop.fieldNames[fkIdx];
|
|
87
89
|
}
|
|
88
90
|
const noPrefix = prop && prop.persist === false;
|
|
89
91
|
if (prop?.fieldNameRaw) {
|
|
90
|
-
return
|
|
92
|
+
return (0, core_1.raw)(this.prefix(field, isTableNameAliasRequired));
|
|
91
93
|
}
|
|
92
94
|
if (prop?.formula) {
|
|
93
|
-
const alias2 = this.
|
|
94
|
-
const aliased = this.
|
|
95
|
+
const alias2 = this.platform.quoteIdentifier(a).toString();
|
|
96
|
+
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]).toString();
|
|
95
97
|
const as = alias === null ? '' : ` as ${aliased}`;
|
|
96
98
|
let value = prop.formula(alias2);
|
|
97
99
|
if (!this.isTableNameAliasRequired(type)) {
|
|
98
100
|
value = value.replaceAll(alias2 + '.', '');
|
|
99
101
|
}
|
|
100
|
-
return
|
|
102
|
+
return (0, core_1.raw)(`${value}${as}`);
|
|
101
103
|
}
|
|
102
104
|
if (prop?.hasConvertToJSValueSQL && type !== enums_1.QueryType.UPSERT) {
|
|
103
105
|
let valueSQL;
|
|
@@ -111,9 +113,9 @@ class QueryBuilderHelper {
|
|
|
111
113
|
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
112
114
|
}
|
|
113
115
|
if (alias === null) {
|
|
114
|
-
return
|
|
116
|
+
return (0, core_1.raw)(valueSQL);
|
|
115
117
|
}
|
|
116
|
-
return
|
|
118
|
+
return (0, core_1.raw)(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[fkIdx])}`);
|
|
117
119
|
}
|
|
118
120
|
// do not wrap custom expressions
|
|
119
121
|
if (!rawField) {
|
|
@@ -135,7 +137,7 @@ class QueryBuilderHelper {
|
|
|
135
137
|
data = this.driver.mapDataToFieldNames(data, true, meta?.properties, convertCustomTypes);
|
|
136
138
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
137
139
|
/* istanbul ignore next */
|
|
138
|
-
data[meta.getPrimaryProps()[0].fieldNames[0]] = this.platform.usesDefaultKeyword() ?
|
|
140
|
+
data[meta.getPrimaryProps()[0].fieldNames[0]] = this.platform.usesDefaultKeyword() ? (0, core_1.raw)('default') : undefined;
|
|
139
141
|
}
|
|
140
142
|
return data;
|
|
141
143
|
}
|
|
@@ -193,7 +195,7 @@ class QueryBuilderHelper {
|
|
|
193
195
|
return;
|
|
194
196
|
}
|
|
195
197
|
const { sql, params } = this.createJoinExpression(join, joins, schema);
|
|
196
|
-
qb.
|
|
198
|
+
qb.join(sql, params);
|
|
197
199
|
});
|
|
198
200
|
}
|
|
199
201
|
createJoinExpression(join, joins, schema) {
|
|
@@ -215,13 +217,13 @@ class QueryBuilderHelper {
|
|
|
215
217
|
if (join.prop.formula) {
|
|
216
218
|
const alias = this.platform.quoteIdentifier(join.ownerAlias);
|
|
217
219
|
const left = join.prop.formula(alias);
|
|
218
|
-
conditions.push(`${left} = ${this.
|
|
220
|
+
conditions.push(`${left} = ${this.platform.quoteIdentifier(right)}`);
|
|
219
221
|
return;
|
|
220
222
|
}
|
|
221
223
|
const left = join.prop.object && join.prop.fieldNameRaw
|
|
222
224
|
? join.prop.fieldNameRaw.replaceAll(core_1.ALIAS_REPLACEMENT, join.ownerAlias)
|
|
223
|
-
: this.
|
|
224
|
-
conditions.push(`${left} = ${this.
|
|
225
|
+
: this.platform.quoteIdentifier(`${join.ownerAlias}.${primaryKey}`);
|
|
226
|
+
conditions.push(`${left} = ${this.platform.quoteIdentifier(right)}`);
|
|
225
227
|
});
|
|
226
228
|
}
|
|
227
229
|
if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
@@ -231,7 +233,7 @@ class QueryBuilderHelper {
|
|
|
231
233
|
}
|
|
232
234
|
let sql = method + ' ';
|
|
233
235
|
if (join.nested) {
|
|
234
|
-
sql += `(${this.
|
|
236
|
+
sql += `(${this.platform.quoteIdentifier(table)} as ${this.platform.quoteIdentifier(join.alias)}`;
|
|
235
237
|
for (const nested of join.nested) {
|
|
236
238
|
const { sql: nestedSql, params: nestedParams } = this.createJoinExpression(nested, joins, schema);
|
|
237
239
|
sql += ' ' + nestedSql;
|
|
@@ -240,94 +242,24 @@ class QueryBuilderHelper {
|
|
|
240
242
|
sql += `)`;
|
|
241
243
|
}
|
|
242
244
|
else if (join.subquery) {
|
|
243
|
-
sql += `(${join.subquery}) as ${this.
|
|
245
|
+
sql += `(${join.subquery}) as ${this.platform.quoteIdentifier(join.alias)}`;
|
|
244
246
|
}
|
|
245
247
|
else {
|
|
246
|
-
sql += `${this.
|
|
248
|
+
sql += `${this.platform.quoteIdentifier(table)} as ${this.platform.quoteIdentifier(join.alias)}`;
|
|
247
249
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
250
|
+
const oldAlias = this.alias;
|
|
251
|
+
this.alias = join.alias;
|
|
252
|
+
const subquery = this._appendQueryCondition(enums_1.QueryType.SELECT, join.cond);
|
|
253
|
+
this.alias = oldAlias;
|
|
254
|
+
if (subquery.sql) {
|
|
255
|
+
conditions.push(subquery.sql);
|
|
256
|
+
params.push(...subquery.params);
|
|
256
257
|
}
|
|
257
258
|
if (conditions.length > 0) {
|
|
258
259
|
sql += ` on ${conditions.join(' and ')}`;
|
|
259
260
|
}
|
|
260
261
|
return { sql, params };
|
|
261
262
|
}
|
|
262
|
-
processJoinClause(key, value, alias, params, operator = '$eq') {
|
|
263
|
-
if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
|
|
264
|
-
const parts = value.map(sub => {
|
|
265
|
-
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], alias, params)));
|
|
266
|
-
});
|
|
267
|
-
return this.wrapQueryGroup(parts, key);
|
|
268
|
-
}
|
|
269
|
-
if (this.isSimpleRegExp(value)) {
|
|
270
|
-
params.push(this.getRegExpParam(value));
|
|
271
|
-
return `${this.knex.ref(this.mapper(key))} like ?`;
|
|
272
|
-
}
|
|
273
|
-
if (value instanceof RegExp) {
|
|
274
|
-
value = this.platform.getRegExpValue(value);
|
|
275
|
-
}
|
|
276
|
-
if (core_1.Utils.isOperator(key, false) && core_1.Utils.isPlainObject(value)) {
|
|
277
|
-
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], alias, params, key));
|
|
278
|
-
return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
|
|
279
|
-
}
|
|
280
|
-
if (core_1.Utils.isPlainObject(value) && Object.keys(value).every(k => core_1.Utils.isOperator(k, false))) {
|
|
281
|
-
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], alias, params, op));
|
|
282
|
-
return this.wrapQueryGroup(parts);
|
|
283
|
-
}
|
|
284
|
-
const [fromAlias, fromField] = this.splitField(key);
|
|
285
|
-
const prop = this.getProperty(fromField, fromAlias);
|
|
286
|
-
operator = operator === '$not' ? '$eq' : operator;
|
|
287
|
-
if (value === null) {
|
|
288
|
-
return `${this.knex.ref(this.mapper(key))} is ${operator === '$ne' ? 'not ' : ''}null`;
|
|
289
|
-
}
|
|
290
|
-
if (operator === '$fulltext' && prop) {
|
|
291
|
-
const query = this.knex.raw(this.platform.getFullTextWhereClause(prop), {
|
|
292
|
-
column: this.mapper(key),
|
|
293
|
-
query: this.knex.raw('?'),
|
|
294
|
-
}).toSQL().toNative();
|
|
295
|
-
params.push(value);
|
|
296
|
-
return query.sql;
|
|
297
|
-
}
|
|
298
|
-
const replacement = this.getOperatorReplacement(operator, { [operator]: value });
|
|
299
|
-
if (['$in', '$nin'].includes(operator) && Array.isArray(value)) {
|
|
300
|
-
params.push(...value);
|
|
301
|
-
return `${this.knex.ref(this.mapper(key))} ${replacement} (${value.map(() => '?').join(', ')})`;
|
|
302
|
-
}
|
|
303
|
-
if (operator === '$exists') {
|
|
304
|
-
value = null;
|
|
305
|
-
}
|
|
306
|
-
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
307
|
-
if (rawField) {
|
|
308
|
-
let sql = rawField.sql.replaceAll(core_1.ALIAS_REPLACEMENT, alias);
|
|
309
|
-
params.push(...rawField.params);
|
|
310
|
-
params.push(...core_1.Utils.asArray(value));
|
|
311
|
-
if (core_1.Utils.asArray(value).length > 0) {
|
|
312
|
-
sql += ' = ?';
|
|
313
|
-
}
|
|
314
|
-
return sql;
|
|
315
|
-
}
|
|
316
|
-
const sql = this.mapper(key);
|
|
317
|
-
if (value !== null) {
|
|
318
|
-
if (prop?.customType) {
|
|
319
|
-
value = prop.customType.convertToDatabaseValue(value, this.platform, { fromQuery: true, key, mode: 'query' });
|
|
320
|
-
}
|
|
321
|
-
params.push(value);
|
|
322
|
-
}
|
|
323
|
-
return `${this.knex.ref(sql)} ${replacement} ${value === null ? 'null' : '?'}`;
|
|
324
|
-
}
|
|
325
|
-
wrapQueryGroup(parts, operator = '$and') {
|
|
326
|
-
if (parts.length === 1) {
|
|
327
|
-
return parts[0];
|
|
328
|
-
}
|
|
329
|
-
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
330
|
-
}
|
|
331
263
|
mapJoinColumns(type, join) {
|
|
332
264
|
if (join.prop && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(join.prop.kind)) {
|
|
333
265
|
return join.prop.fieldNames.map((_fieldName, idx) => {
|
|
@@ -382,17 +314,8 @@ class QueryBuilderHelper {
|
|
|
382
314
|
}
|
|
383
315
|
appendOnConflictClause(type, onConflict, qb) {
|
|
384
316
|
onConflict.forEach(item => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
sub = qb.onConflict(this.knex.raw(item.fields.sql, item.fields.params));
|
|
388
|
-
}
|
|
389
|
-
else if (item.fields.length > 0) {
|
|
390
|
-
sub = qb.onConflict(item.fields);
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
sub = qb.onConflict();
|
|
394
|
-
}
|
|
395
|
-
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
317
|
+
const { fields, ignore } = item;
|
|
318
|
+
const sub = qb.onConflict({ fields, ignore });
|
|
396
319
|
core_1.Utils.runIfNotEmpty(() => {
|
|
397
320
|
let mergeParam = item.merge;
|
|
398
321
|
if (core_1.Utils.isObject(item.merge)) {
|
|
@@ -405,57 +328,89 @@ class QueryBuilderHelper {
|
|
|
405
328
|
if (Array.isArray(item.merge)) {
|
|
406
329
|
mergeParam = item.merge.map(key => this.mapper(key, type));
|
|
407
330
|
}
|
|
408
|
-
|
|
409
|
-
|
|
331
|
+
sub.merge = mergeParam ?? [];
|
|
332
|
+
if (item.where) {
|
|
333
|
+
sub.where = this._appendQueryCondition(type, item.where);
|
|
334
|
+
}
|
|
410
335
|
}, 'merge' in item);
|
|
411
336
|
});
|
|
412
337
|
}
|
|
413
338
|
appendQueryCondition(type, cond, qb, operator, method = 'where') {
|
|
414
|
-
const
|
|
415
|
-
|
|
339
|
+
const { sql, params } = this._appendQueryCondition(type, cond, operator);
|
|
340
|
+
qb[method](sql, params);
|
|
341
|
+
}
|
|
342
|
+
_appendQueryCondition(type, cond, operator) {
|
|
343
|
+
const parts = [];
|
|
344
|
+
const params = [];
|
|
345
|
+
for (const k of Object.keys(cond)) {
|
|
416
346
|
if (k === '$and' || k === '$or') {
|
|
417
347
|
if (operator) {
|
|
418
|
-
|
|
348
|
+
this.append(() => this.appendGroupCondition(type, k, cond[k]), parts, params, operator);
|
|
349
|
+
continue;
|
|
419
350
|
}
|
|
420
|
-
|
|
351
|
+
this.append(() => this.appendGroupCondition(type, k, cond[k]), parts, params);
|
|
352
|
+
continue;
|
|
421
353
|
}
|
|
422
354
|
if (k === '$not') {
|
|
423
|
-
const
|
|
424
|
-
|
|
355
|
+
const res = this._appendQueryCondition(type, cond[k]);
|
|
356
|
+
parts.push(`not (${res.sql})`);
|
|
357
|
+
params.push(...res.params);
|
|
358
|
+
continue;
|
|
425
359
|
}
|
|
426
|
-
this.appendQuerySubCondition(
|
|
427
|
-
}
|
|
360
|
+
this.append(() => this.appendQuerySubCondition(type, cond, k), parts, params);
|
|
361
|
+
}
|
|
362
|
+
return { sql: parts.join(' and '), params };
|
|
428
363
|
}
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
if (
|
|
432
|
-
|
|
364
|
+
append(cb, parts, params, operator) {
|
|
365
|
+
const res = cb();
|
|
366
|
+
if (['', '()'].includes(res.sql)) {
|
|
367
|
+
return;
|
|
433
368
|
}
|
|
369
|
+
parts.push(operator === '$or' ? `(${res.sql})` : res.sql);
|
|
370
|
+
params.push(...res.params);
|
|
371
|
+
}
|
|
372
|
+
appendQuerySubCondition(type, cond, key) {
|
|
373
|
+
const parts = [];
|
|
374
|
+
const params = [];
|
|
375
|
+
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
434
376
|
if (this.isSimpleRegExp(cond[key])) {
|
|
435
|
-
|
|
377
|
+
parts.push(`${this.platform.quoteIdentifier(this.mapper(key, type))} like ?`);
|
|
378
|
+
params.push(this.getRegExpParam(cond[key]));
|
|
379
|
+
return { sql: parts.join(' and '), params };
|
|
436
380
|
}
|
|
437
381
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
438
|
-
return this.processObjectSubCondition(cond, key,
|
|
382
|
+
return this.processObjectSubCondition(cond, key, type);
|
|
439
383
|
}
|
|
440
384
|
const op = cond[key] === null ? 'is' : '=';
|
|
441
385
|
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
442
386
|
if (raw) {
|
|
387
|
+
const sql = raw.sql.replaceAll(core_1.ALIAS_REPLACEMENT, this.alias);
|
|
443
388
|
const value = core_1.Utils.asArray(cond[key]);
|
|
389
|
+
params.push(...raw.params);
|
|
444
390
|
if (value.length > 0) {
|
|
445
|
-
|
|
391
|
+
const val = this.getValueReplacement(fields, value[0], params, key);
|
|
392
|
+
parts.push(`${sql} ${op} ${val}`);
|
|
393
|
+
return { sql: parts.join(' and '), params };
|
|
446
394
|
}
|
|
447
|
-
|
|
395
|
+
parts.push(sql);
|
|
396
|
+
return { sql: parts.join(' and '), params };
|
|
448
397
|
}
|
|
449
398
|
if (this.subQueries[key]) {
|
|
450
|
-
|
|
399
|
+
const val = this.getValueReplacement(fields, cond[key], params, key);
|
|
400
|
+
parts.push(`(${this.subQueries[key]}) ${op} ${val}`);
|
|
401
|
+
return { sql: parts.join(' and '), params };
|
|
451
402
|
}
|
|
452
|
-
|
|
403
|
+
const val = this.getValueReplacement(fields, cond[key], params, key);
|
|
404
|
+
parts.push(`${this.platform.quoteIdentifier(this.mapper(key, type, cond[key], null))} ${op} ${val}`);
|
|
405
|
+
return { sql: parts.join(' and '), params };
|
|
453
406
|
}
|
|
454
|
-
processObjectSubCondition(cond, key,
|
|
407
|
+
processObjectSubCondition(cond, key, type) {
|
|
408
|
+
const parts = [];
|
|
409
|
+
const params = [];
|
|
455
410
|
let value = cond[key];
|
|
456
411
|
const size = core_1.Utils.getObjectKeysSize(value);
|
|
457
412
|
if (core_1.Utils.isPlainObject(value) && size === 0) {
|
|
458
|
-
return;
|
|
413
|
+
return { sql: '', params };
|
|
459
414
|
}
|
|
460
415
|
// grouped condition for one field, e.g. `{ age: { $gte: 10, $lt: 50 } }`
|
|
461
416
|
if (size > 1) {
|
|
@@ -464,7 +419,10 @@ class QueryBuilderHelper {
|
|
|
464
419
|
key = rawField?.clone().toString() ?? key;
|
|
465
420
|
return ({ [key]: { [subKey]: subValue } });
|
|
466
421
|
});
|
|
467
|
-
|
|
422
|
+
for (const sub of subCondition) {
|
|
423
|
+
this.append(() => this._appendQueryCondition(type, sub, '$and'), parts, params);
|
|
424
|
+
}
|
|
425
|
+
return { sql: parts.join(' and '), params };
|
|
468
426
|
}
|
|
469
427
|
if (value instanceof RegExp) {
|
|
470
428
|
value = this.platform.getRegExpValue(value);
|
|
@@ -485,37 +443,83 @@ class QueryBuilderHelper {
|
|
|
485
443
|
const conds = value[op].map(() => {
|
|
486
444
|
return `(${mapped.map(field => `${this.platform.quoteIdentifier(field)} = ?`).join(' and ')})`;
|
|
487
445
|
});
|
|
488
|
-
|
|
446
|
+
parts.push(`(${conds.join(' or ')})`);
|
|
447
|
+
params.push(...core_1.Utils.flatten(value[op]));
|
|
448
|
+
return { sql: parts.join(' and '), params };
|
|
489
449
|
}
|
|
490
|
-
|
|
450
|
+
parts.push(...mapped.map(field => `${this.platform.quoteIdentifier(field)} = ?`));
|
|
451
|
+
params.push(...core_1.Utils.flatten(value[op]));
|
|
452
|
+
return { sql: parts.join(' and '), params };
|
|
491
453
|
}
|
|
492
454
|
if (singleTuple) {
|
|
493
455
|
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
494
|
-
|
|
456
|
+
const sql = `(${fields.map(() => '?').join(', ')})`;
|
|
457
|
+
value[op] = (0, core_1.raw)(sql, tmp);
|
|
495
458
|
}
|
|
496
459
|
}
|
|
497
|
-
if (value[op] instanceof core_1.RawQueryFragment) {
|
|
498
|
-
value[op] = this.knex.raw(value[op].sql, value[op].params);
|
|
499
|
-
}
|
|
500
460
|
if (this.subQueries[key]) {
|
|
501
|
-
|
|
461
|
+
const val = this.getValueReplacement(fields, value[op], params, op);
|
|
462
|
+
parts.push(`(${this.subQueries[key]}) ${replacement} ${val}`);
|
|
463
|
+
return { sql: parts.join(' and '), params };
|
|
502
464
|
}
|
|
465
|
+
const [a, f] = this.splitField(key);
|
|
466
|
+
const prop = this.getProperty(f, a);
|
|
503
467
|
if (op === '$fulltext') {
|
|
504
|
-
const [a, f] = this.splitField(key);
|
|
505
|
-
const prop = this.getProperty(f, a);
|
|
506
468
|
/* istanbul ignore next */
|
|
507
469
|
if (!prop) {
|
|
508
470
|
throw new Error(`Cannot use $fulltext operator on ${key}, property not found`);
|
|
509
471
|
}
|
|
510
|
-
|
|
472
|
+
const { sql, params: params2 } = (0, core_1.raw)(this.platform.getFullTextWhereClause(prop), {
|
|
511
473
|
column: this.mapper(key, type, undefined, null),
|
|
512
474
|
query: value[op],
|
|
513
|
-
})
|
|
475
|
+
});
|
|
476
|
+
parts.push(sql);
|
|
477
|
+
params.push(...params2);
|
|
478
|
+
}
|
|
479
|
+
else if (op === '$in' && Array.isArray(value[op]) && value[op].length === 0) {
|
|
480
|
+
parts.push(`1 = 0`);
|
|
481
|
+
}
|
|
482
|
+
else if (value[op] instanceof core_1.RawQueryFragment || value[op] instanceof NativeQueryBuilder_1.NativeQueryBuilder) {
|
|
483
|
+
const query = value[op] instanceof NativeQueryBuilder_1.NativeQueryBuilder ? value[op].toRaw() : value[op];
|
|
484
|
+
const mappedKey = this.mapper(key, type, query, null);
|
|
485
|
+
let sql = query.sql;
|
|
486
|
+
if (['$in', '$nin'].includes(op)) {
|
|
487
|
+
sql = `(${sql})`;
|
|
488
|
+
}
|
|
489
|
+
parts.push(`${this.platform.quoteIdentifier(mappedKey)} ${replacement} ${sql}`);
|
|
490
|
+
params.push(...query.params);
|
|
514
491
|
}
|
|
515
492
|
else {
|
|
516
493
|
const mappedKey = this.mapper(key, type, value[op], null);
|
|
517
|
-
|
|
494
|
+
const val = this.getValueReplacement(fields, value[op], params, op, prop);
|
|
495
|
+
parts.push(`${this.platform.quoteIdentifier(mappedKey)} ${replacement} ${val}`);
|
|
496
|
+
}
|
|
497
|
+
return { sql: parts.join(' and '), params };
|
|
498
|
+
}
|
|
499
|
+
getValueReplacement(fields, value, params, key, prop) {
|
|
500
|
+
if (Array.isArray(value)) {
|
|
501
|
+
if (fields.length > 1) {
|
|
502
|
+
const tmp = [];
|
|
503
|
+
for (const field of value) {
|
|
504
|
+
tmp.push(`(${field.map(() => '?').join(', ')})`);
|
|
505
|
+
params.push(...field);
|
|
506
|
+
}
|
|
507
|
+
return `(${tmp.join(', ')})`;
|
|
508
|
+
}
|
|
509
|
+
if (prop?.customType instanceof core_1.ArrayType) {
|
|
510
|
+
const item = prop.customType.convertToDatabaseValue(value, this.platform, { fromQuery: true, key, mode: 'query' });
|
|
511
|
+
params.push(item);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
params.push(...value);
|
|
515
|
+
}
|
|
516
|
+
return `(${value.map(() => '?').join(', ')})`;
|
|
518
517
|
}
|
|
518
|
+
if (value === null) {
|
|
519
|
+
return 'null';
|
|
520
|
+
}
|
|
521
|
+
params.push(value);
|
|
522
|
+
return '?';
|
|
519
523
|
}
|
|
520
524
|
getOperatorReplacement(op, value) {
|
|
521
525
|
let replacement = core_1.QueryOperator[op];
|
|
@@ -529,6 +533,9 @@ class QueryBuilderHelper {
|
|
|
529
533
|
if (op === '$re') {
|
|
530
534
|
replacement = this.platform.getRegExpOperator(value[op], value.$flags);
|
|
531
535
|
}
|
|
536
|
+
if (replacement.includes('?')) {
|
|
537
|
+
replacement = replacement.replaceAll('?', '\\?');
|
|
538
|
+
}
|
|
532
539
|
return replacement;
|
|
533
540
|
}
|
|
534
541
|
getQueryOrder(type, orderBy, populate) {
|
|
@@ -555,12 +562,12 @@ class QueryBuilderHelper {
|
|
|
555
562
|
const noPrefix = (prop && prop.persist === false && !prop.formula && !prop.embedded) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
556
563
|
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
557
564
|
/* istanbul ignore next */
|
|
558
|
-
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.
|
|
565
|
+
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.platform.quoteIdentifier(e)).join('.') : column;
|
|
559
566
|
const customOrder = prop?.customOrder;
|
|
560
567
|
let colPart = customOrder
|
|
561
568
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
562
569
|
: rawColumn;
|
|
563
|
-
if (core_1.
|
|
570
|
+
if ((0, core_1.isRaw)(colPart)) {
|
|
564
571
|
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
565
572
|
}
|
|
566
573
|
if (Array.isArray(order)) {
|
|
@@ -593,13 +600,13 @@ class QueryBuilderHelper {
|
|
|
593
600
|
return;
|
|
594
601
|
}
|
|
595
602
|
if (type === enums_1.QueryType.UPDATE) {
|
|
596
|
-
const returningProps = meta.hydrateProps.filter(prop => prop.fieldNames && core_1.
|
|
603
|
+
const returningProps = meta.hydrateProps.filter(prop => prop.fieldNames && (0, core_1.isRaw)(data[prop.fieldNames[0]]));
|
|
597
604
|
if (returningProps.length > 0) {
|
|
598
605
|
qb.returning(returningProps.flatMap(prop => {
|
|
599
606
|
if (prop.hasConvertToJSValueSQL) {
|
|
600
607
|
const aliased = this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
601
608
|
const sql = prop.customType.convertToJSValueSQL(aliased, this.platform) + ' as ' + this.platform.quoteIdentifier(prop.fieldNames[0]);
|
|
602
|
-
return [
|
|
609
|
+
return [(0, core_1.raw)(sql)];
|
|
603
610
|
}
|
|
604
611
|
return prop.fieldNames;
|
|
605
612
|
}));
|
|
@@ -629,14 +636,7 @@ class QueryBuilderHelper {
|
|
|
629
636
|
if (lockMode === core_1.LockMode.OPTIMISTIC && meta && !meta.versionProperty) {
|
|
630
637
|
throw core_1.OptimisticLockError.lockFailed(this.entityName);
|
|
631
638
|
}
|
|
632
|
-
|
|
633
|
-
case core_1.LockMode.PESSIMISTIC_READ: return void qb.forShare(...lockTables);
|
|
634
|
-
case core_1.LockMode.PESSIMISTIC_WRITE: return void qb.forUpdate(...lockTables);
|
|
635
|
-
case core_1.LockMode.PESSIMISTIC_PARTIAL_WRITE: return void qb.forUpdate(...lockTables).skipLocked();
|
|
636
|
-
case core_1.LockMode.PESSIMISTIC_WRITE_OR_FAIL: return void qb.forUpdate(...lockTables).noWait();
|
|
637
|
-
case core_1.LockMode.PESSIMISTIC_PARTIAL_READ: return void qb.forShare(...lockTables).skipLocked();
|
|
638
|
-
case core_1.LockMode.PESSIMISTIC_READ_OR_FAIL: return void qb.forShare(...lockTables).noWait();
|
|
639
|
-
}
|
|
639
|
+
qb.lockMode(lockMode, lockTables);
|
|
640
640
|
}
|
|
641
641
|
updateVersionProperty(qb, data) {
|
|
642
642
|
const meta = this.metadata.find(this.entityName);
|
|
@@ -648,7 +648,7 @@ class QueryBuilderHelper {
|
|
|
648
648
|
if (versionProperty.runtimeType === 'Date') {
|
|
649
649
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
650
650
|
}
|
|
651
|
-
qb.update(versionProperty.fieldNames[0],
|
|
651
|
+
qb.update({ [versionProperty.fieldNames[0]]: (0, core_1.raw)(sql) });
|
|
652
652
|
}
|
|
653
653
|
prefix(field, always = false, quote = false, idx) {
|
|
654
654
|
let ret;
|
|
@@ -674,21 +674,28 @@ class QueryBuilderHelper {
|
|
|
674
674
|
}
|
|
675
675
|
return ret;
|
|
676
676
|
}
|
|
677
|
-
appendGroupCondition(type,
|
|
677
|
+
appendGroupCondition(type, operator, subCondition) {
|
|
678
|
+
const parts = [];
|
|
679
|
+
const params = [];
|
|
678
680
|
// single sub-condition can be ignored to reduce nesting of parens
|
|
679
681
|
if (subCondition.length === 1 || operator === '$and') {
|
|
680
|
-
|
|
682
|
+
for (const sub of subCondition) {
|
|
683
|
+
this.append(() => this._appendQueryCondition(type, sub), parts, params);
|
|
684
|
+
}
|
|
685
|
+
return { sql: parts.join(' and '), params };
|
|
681
686
|
}
|
|
682
|
-
|
|
687
|
+
for (const sub of subCondition) {
|
|
683
688
|
// skip nesting parens if the value is simple = scalar or object without operators or with only single key, being the operator
|
|
684
689
|
const keys = Object.keys(sub);
|
|
685
690
|
const val = sub[keys[0]];
|
|
686
691
|
const simple = !core_1.Utils.isPlainObject(val) || core_1.Utils.getObjectKeysSize(val) === 1 || Object.keys(val).every(k => !core_1.Utils.isOperator(k));
|
|
687
692
|
if (keys.length === 1 && simple) {
|
|
688
|
-
|
|
693
|
+
this.append(() => this._appendQueryCondition(type, sub, operator), parts, params);
|
|
694
|
+
continue;
|
|
689
695
|
}
|
|
690
|
-
|
|
691
|
-
}
|
|
696
|
+
this.append(() => this._appendQueryCondition(type, sub), parts, params, operator);
|
|
697
|
+
}
|
|
698
|
+
return { sql: `(${parts.join(' or ')})`, params };
|
|
692
699
|
}
|
|
693
700
|
isPrefixed(field) {
|
|
694
701
|
return !!field.match(/[\w`"[\]]+\./);
|
|
@@ -738,12 +745,11 @@ class QueryBuilderHelper {
|
|
|
738
745
|
return undefined;
|
|
739
746
|
}
|
|
740
747
|
isTableNameAliasRequired(type) {
|
|
741
|
-
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type
|
|
748
|
+
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type);
|
|
742
749
|
}
|
|
743
|
-
// workaround for https://github.com/knex/knex/issues/5257
|
|
744
750
|
processOnConflictCondition(cond, schema) {
|
|
745
751
|
const meta = this.metadata.get(this.entityName);
|
|
746
|
-
const tableName =
|
|
752
|
+
const tableName = meta.tableName;
|
|
747
753
|
for (const key of Object.keys(cond)) {
|
|
748
754
|
const mapped = this.mapper(key, enums_1.QueryType.UPSERT);
|
|
749
755
|
core_1.Utils.renameKey(cond, key, tableName + '.' + mapped);
|
|
@@ -4,6 +4,7 @@ exports.ScalarCriteriaNode = void 0;
|
|
|
4
4
|
const core_1 = require("@mikro-orm/core");
|
|
5
5
|
const CriteriaNode_1 = require("./CriteriaNode");
|
|
6
6
|
const enums_1 = require("./enums");
|
|
7
|
+
const QueryBuilder_1 = require("./QueryBuilder");
|
|
7
8
|
/**
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
@@ -21,6 +22,9 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
21
22
|
qb.addSelect(field);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
if (this.payload instanceof QueryBuilder_1.QueryBuilder) {
|
|
26
|
+
return this.payload.getNativeQuery().toRaw();
|
|
27
|
+
}
|
|
24
28
|
if (this.payload && typeof this.payload === 'object') {
|
|
25
29
|
const keys = Object.keys(this.payload).filter(key => core_1.Utils.isArrayOperator(key) && Array.isArray(this.payload[key]));
|
|
26
30
|
for (const key of keys) {
|
package/query/index.d.ts
CHANGED
package/query/index.js
CHANGED
|
@@ -22,3 +22,4 @@ __exportStar(require("./ArrayCriteriaNode"), exports);
|
|
|
22
22
|
__exportStar(require("./ObjectCriteriaNode"), exports);
|
|
23
23
|
__exportStar(require("./ScalarCriteriaNode"), exports);
|
|
24
24
|
__exportStar(require("./CriteriaNodeFactory"), exports);
|
|
25
|
+
__exportStar(require("./NativeQueryBuilder"), exports);
|
package/schema/DatabaseSchema.js
CHANGED
|
@@ -99,21 +99,24 @@ class DatabaseSchema {
|
|
|
99
99
|
for (const meta of metadata) {
|
|
100
100
|
const table = schema.addTable(meta.collection, this.getSchemaName(meta, config, schemaName));
|
|
101
101
|
table.comment = meta.comment;
|
|
102
|
-
meta.props
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
for (const prop of meta.props) {
|
|
103
|
+
if (!this.shouldHaveColumn(meta, prop)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
table.addColumnFromProperty(prop, meta, config);
|
|
107
|
+
}
|
|
105
108
|
meta.indexes.forEach(index => table.addIndex(meta, index, 'index'));
|
|
106
109
|
meta.uniques.forEach(index => table.addIndex(meta, index, 'unique'));
|
|
107
110
|
table.addIndex(meta, { properties: meta.props.filter(prop => prop.primary).map(prop => prop.name) }, 'primary');
|
|
108
|
-
meta.checks
|
|
111
|
+
for (const check of meta.checks) {
|
|
109
112
|
const columnName = check.property ? meta.properties[check.property].fieldNames[0] : undefined;
|
|
110
113
|
table.addCheck({
|
|
111
114
|
name: check.name,
|
|
112
115
|
expression: check.expression,
|
|
113
|
-
definition: `check (
|
|
116
|
+
definition: `check (${check.expression})`,
|
|
114
117
|
columnName,
|
|
115
118
|
});
|
|
116
|
-
}
|
|
119
|
+
}
|
|
117
120
|
}
|
|
118
121
|
return schema;
|
|
119
122
|
}
|