@mikro-orm/knex 6.4.6-dev.9 → 7.0.0-dev.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.
Files changed (101) hide show
  1. package/AbstractSqlConnection.d.ts +15 -25
  2. package/AbstractSqlConnection.js +98 -126
  3. package/AbstractSqlDriver.d.ts +20 -13
  4. package/AbstractSqlDriver.js +73 -54
  5. package/AbstractSqlPlatform.d.ts +15 -3
  6. package/AbstractSqlPlatform.js +32 -11
  7. package/README.md +0 -2
  8. package/SqlEntityManager.d.ts +5 -6
  9. package/SqlEntityManager.js +5 -5
  10. package/SqlEntityRepository.d.ts +1 -6
  11. package/SqlEntityRepository.js +0 -6
  12. package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +12 -0
  13. package/dialects/mssql/MsSqlNativeQueryBuilder.js +161 -0
  14. package/dialects/mssql/index.d.ts +1 -1
  15. package/dialects/mssql/index.js +1 -1
  16. package/dialects/mysql/MySqlExceptionConverter.js +1 -0
  17. package/dialects/mysql/MySqlNativeQueryBuilder.d.ts +7 -0
  18. package/dialects/mysql/MySqlNativeQueryBuilder.js +81 -0
  19. package/dialects/mysql/MySqlPlatform.d.ts +5 -1
  20. package/dialects/mysql/MySqlPlatform.js +9 -1
  21. package/dialects/mysql/MySqlSchemaHelper.d.ts +6 -12
  22. package/dialects/mysql/MySqlSchemaHelper.js +42 -75
  23. package/dialects/mysql/index.d.ts +1 -3
  24. package/dialects/mysql/index.js +1 -3
  25. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.d.ts +5 -0
  26. package/dialects/postgresql/PostgreSqlNativeQueryBuilder.js +12 -0
  27. package/dialects/postgresql/index.d.ts +1 -1
  28. package/dialects/postgresql/index.js +1 -1
  29. package/dialects/sqlite/BaseSqliteConnection.d.ts +0 -5
  30. package/dialects/sqlite/BaseSqliteConnection.js +4 -42
  31. package/dialects/sqlite/BaseSqlitePlatform.d.ts +15 -3
  32. package/dialects/sqlite/BaseSqlitePlatform.js +20 -4
  33. package/dialects/sqlite/SqliteExceptionConverter.d.ts +9 -0
  34. package/dialects/sqlite/SqliteExceptionConverter.js +55 -0
  35. package/dialects/sqlite/SqliteNativeQueryBuilder.d.ts +6 -0
  36. package/dialects/sqlite/SqliteNativeQueryBuilder.js +15 -0
  37. package/dialects/sqlite/SqliteSchemaHelper.d.ts +38 -0
  38. package/dialects/sqlite/SqliteSchemaHelper.js +384 -0
  39. package/dialects/sqlite/index.d.ts +3 -5
  40. package/dialects/sqlite/index.js +3 -5
  41. package/index.d.ts +1 -2
  42. package/index.js +3 -5
  43. package/index.mjs +10 -13
  44. package/package.json +4 -18
  45. package/query/CriteriaNodeFactory.js +5 -5
  46. package/query/NativeQueryBuilder.d.ts +108 -0
  47. package/query/NativeQueryBuilder.js +429 -0
  48. package/query/ObjectCriteriaNode.js +3 -3
  49. package/query/QueryBuilder.d.ts +30 -34
  50. package/query/QueryBuilder.js +112 -123
  51. package/query/QueryBuilderHelper.d.ts +27 -23
  52. package/query/QueryBuilderHelper.js +174 -168
  53. package/query/ScalarCriteriaNode.js +4 -0
  54. package/query/index.d.ts +1 -0
  55. package/query/index.js +1 -0
  56. package/schema/DatabaseSchema.js +9 -6
  57. package/schema/DatabaseTable.d.ts +2 -1
  58. package/schema/DatabaseTable.js +9 -5
  59. package/schema/SchemaComparator.d.ts +1 -2
  60. package/schema/SchemaComparator.js +31 -18
  61. package/schema/SchemaHelper.d.ts +27 -33
  62. package/schema/SchemaHelper.js +294 -184
  63. package/schema/SqlSchemaGenerator.d.ts +3 -9
  64. package/schema/SqlSchemaGenerator.js +105 -229
  65. package/typings.d.ts +7 -17
  66. package/MonkeyPatchable.d.ts +0 -18
  67. package/MonkeyPatchable.js +0 -60
  68. package/dialects/mssql/MsSqlColumnCompiler.d.ts +0 -4
  69. package/dialects/mssql/MsSqlColumnCompiler.js +0 -10
  70. package/dialects/mssql/MsSqlKnexDialect.d.ts +0 -6
  71. package/dialects/mssql/MsSqlKnexDialect.js +0 -22
  72. package/dialects/mssql/MsSqlQueryCompiler.d.ts +0 -12
  73. package/dialects/mssql/MsSqlQueryCompiler.js +0 -94
  74. package/dialects/mssql/MsSqlTableCompiler.d.ts +0 -9
  75. package/dialects/mssql/MsSqlTableCompiler.js +0 -40
  76. package/dialects/mysql/MariaDbKnexDialect.d.ts +0 -6
  77. package/dialects/mysql/MariaDbKnexDialect.js +0 -16
  78. package/dialects/mysql/MySqlColumnCompiler.d.ts +0 -7
  79. package/dialects/mysql/MySqlColumnCompiler.js +0 -26
  80. package/dialects/mysql/MySqlConnection.d.ts +0 -8
  81. package/dialects/mysql/MySqlConnection.js +0 -43
  82. package/dialects/mysql/MySqlKnexDialect.d.ts +0 -5
  83. package/dialects/mysql/MySqlKnexDialect.js +0 -17
  84. package/dialects/mysql/MySqlQueryCompiler.d.ts +0 -5
  85. package/dialects/mysql/MySqlQueryCompiler.js +0 -23
  86. package/dialects/postgresql/PostgreSqlKnexDialect.d.ts +0 -7
  87. package/dialects/postgresql/PostgreSqlKnexDialect.js +0 -20
  88. package/dialects/postgresql/PostgreSqlQueryCompiler.d.ts +0 -4
  89. package/dialects/postgresql/PostgreSqlQueryCompiler.js +0 -13
  90. package/dialects/postgresql/PostgreSqlTableCompiler.d.ts +0 -11
  91. package/dialects/postgresql/PostgreSqlTableCompiler.js +0 -89
  92. package/dialects/sqlite/BaseSqliteSchemaHelper.d.ts +0 -28
  93. package/dialects/sqlite/BaseSqliteSchemaHelper.js +0 -200
  94. package/dialects/sqlite/BetterSqliteKnexDialect.d.ts +0 -5
  95. package/dialects/sqlite/BetterSqliteKnexDialect.js +0 -15
  96. package/dialects/sqlite/LibSqlKnexDialect.d.ts +0 -11
  97. package/dialects/sqlite/LibSqlKnexDialect.js +0 -85
  98. package/dialects/sqlite/SqliteKnexDialect.d.ts +0 -8
  99. package/dialects/sqlite/SqliteKnexDialect.js +0 -67
  100. package/dialects/sqlite/SqliteTableCompiler.d.ts +0 -6
  101. 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, knex, driver) {
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.Utils.isRawSql(field)) {
31
- return this.knex.raw(field.sql, field.params);
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 this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
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
- // sometimes knex is confusing the binding positions, we need to interpolate early
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 this.alias + '.' + prop.fieldNames[fkIdx];
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 this.knex.raw(this.prefix(field, isTableNameAliasRequired));
92
+ return (0, core_1.raw)(this.prefix(field, isTableNameAliasRequired));
91
93
  }
92
94
  if (prop?.formula) {
93
- const alias2 = this.knex.ref(a).toString();
94
- const aliased = this.knex.ref(prop.fieldNames[0]).toString();
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 this.knex.raw(`${value}${as}`);
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 this.knex.raw(valueSQL);
116
+ return (0, core_1.raw)(valueSQL);
115
117
  }
116
- return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[fkIdx])}`);
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() ? this.knex.raw('default') : undefined;
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.joinRaw(sql, params);
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.knex.ref(right)}`);
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.knex.ref(`${join.ownerAlias}.${primaryKey}`);
224
- conditions.push(`${left} = ${this.knex.ref(right)}`);
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.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
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.knex.ref(join.alias)}`;
245
+ sql += `(${join.subquery}) as ${this.platform.quoteIdentifier(join.alias)}`;
244
246
  }
245
247
  else {
246
- sql += `${this.knex.ref(table)} as ${this.knex.ref(join.alias)}`;
248
+ sql += `${this.platform.quoteIdentifier(table)} as ${this.platform.quoteIdentifier(join.alias)}`;
247
249
  }
248
- for (const key of Object.keys(join.cond)) {
249
- const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
250
- const newKey = hasPrefix ? key : `${join.alias}.${key}`;
251
- const clause = this.processJoinClause(newKey, join.cond[key], join.alias, params);
252
- /* istanbul ignore else */
253
- if (clause !== '()') {
254
- conditions.push(clause);
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
- let sub;
386
- if (core_1.Utils.isRawSql(item.fields)) {
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
- const sub2 = sub.merge(mergeParam);
409
- core_1.Utils.runIfNotEmpty(() => this.appendQueryCondition(type, item.where, sub2), item.where);
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 m = operator === '$or' ? 'orWhere' : 'andWhere';
415
- Object.keys(cond).forEach(k => {
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
- return qb[m](inner => this.appendGroupCondition(type, inner, k, method, cond[k]));
348
+ this.append(() => this.appendGroupCondition(type, k, cond[k]), parts, params, operator);
349
+ continue;
419
350
  }
420
- return this.appendGroupCondition(type, qb, k, method, cond[k]);
351
+ this.append(() => this.appendGroupCondition(type, k, cond[k]), parts, params);
352
+ continue;
421
353
  }
422
354
  if (k === '$not') {
423
- const m = operator === '$or' ? 'orWhereNot' : 'whereNot';
424
- return qb[m](inner => this.appendQueryCondition(type, cond[k], inner));
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(qb, type, method, cond, k, operator);
427
- });
360
+ this.append(() => this.appendQuerySubCondition(type, cond, k), parts, params);
361
+ }
362
+ return { sql: parts.join(' and '), params };
428
363
  }
429
- appendQuerySubCondition(qb, type, method, cond, key, operator) {
430
- const m = operator === '$or' ? 'orWhere' : method;
431
- if (cond[key] instanceof core_1.RawQueryFragment) {
432
- cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
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
- return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
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, qb, method, m, type);
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
- return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
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
- return void qb[m](this.knex.raw(raw.sql, raw.params));
395
+ parts.push(sql);
396
+ return { sql: parts.join(' and '), params };
448
397
  }
449
398
  if (this.subQueries[key]) {
450
- return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
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
- qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
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, qb, method, m, type) {
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
- return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
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
- return void qb[m](this.knex.raw(`(${conds.join(' or ')})`, core_1.Utils.flatten(value[op])));
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
- return void qb[m](this.knex.raw(`${mapped.map(field => `${this.platform.quoteIdentifier(field)} = ?`).join(' and ')}`, core_1.Utils.flatten(value[op])));
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
- value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
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
- return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
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
- qb[m](this.knex.raw(this.platform.getFullTextWhereClause(prop), {
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
- qb[m](mappedKey, replacement, value[op]);
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.knex.ref(e)).join('.') : column;
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.Utils.isRawSql(colPart)) {
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.Utils.isRawSql(data[prop.fieldNames[0]]));
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 [this.knex.raw(sql)];
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
- switch (lockMode) {
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], this.knex.raw(sql));
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, qb, operator, method, subCondition) {
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
- return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, undefined, method));
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
- qb[method](outer => subCondition.forEach(sub => {
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
- return this.appendQueryCondition(type, sub, outer, operator);
693
+ this.append(() => this._appendQueryCondition(type, sub, operator), parts, params);
694
+ continue;
689
695
  }
690
- outer.orWhere(inner => this.appendQueryCondition(type, sub, inner));
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 ?? enums_1.QueryType.SELECT);
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 = this.driver.getTableName(meta, { schema }, false);
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
@@ -6,3 +6,4 @@ export * from './ArrayCriteriaNode';
6
6
  export * from './ObjectCriteriaNode';
7
7
  export * from './ScalarCriteriaNode';
8
8
  export * from './CriteriaNodeFactory';
9
+ export * from './NativeQueryBuilder';
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);
@@ -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
- .filter(prop => this.shouldHaveColumn(meta, prop))
104
- .forEach(prop => table.addColumnFromProperty(prop, meta, config));
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.forEach(check => {
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 ((${check.expression}))`,
116
+ definition: `check (${check.expression})`,
114
117
  columnName,
115
118
  });
116
- });
119
+ }
117
120
  }
118
121
  return schema;
119
122
  }