@mikro-orm/knex 6.0.0-dev.5 → 6.0.0-dev.51
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 +3 -0
- package/AbstractSqlConnection.js +16 -8
- package/AbstractSqlDriver.d.ts +11 -11
- package/AbstractSqlDriver.js +107 -44
- package/AbstractSqlPlatform.d.ts +2 -4
- package/AbstractSqlPlatform.js +15 -21
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/README.md +63 -106
- package/SqlEntityManager.d.ts +0 -4
- package/SqlEntityManager.js +1 -9
- package/SqlEntityRepository.d.ts +0 -1
- package/SqlEntityRepository.js +0 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.mjs +175 -0
- package/package.json +5 -39
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +8 -8
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +11 -11
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +12 -8
- package/query/QueryBuilder.d.ts +17 -4
- package/query/QueryBuilder.js +188 -45
- package/query/QueryBuilderHelper.d.ts +3 -5
- package/query/QueryBuilderHelper.js +85 -71
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/schema/DatabaseSchema.js +3 -3
- package/schema/DatabaseTable.d.ts +9 -9
- package/schema/DatabaseTable.js +21 -22
- package/schema/SchemaComparator.d.ts +4 -4
- package/schema/SchemaComparator.js +4 -2
- package/schema/SchemaHelper.d.ts +11 -11
- package/schema/SchemaHelper.js +3 -3
- package/schema/{SchemaGenerator.d.ts → SqlSchemaGenerator.d.ts} +4 -8
- package/schema/{SchemaGenerator.js → SqlSchemaGenerator.js} +17 -24
- package/schema/index.d.ts +1 -1
- package/schema/index.js +1 -1
- package/typings.d.ts +14 -13
|
@@ -19,6 +19,9 @@ class QueryBuilderHelper {
|
|
|
19
19
|
this.metadata = this.driver.getMetadata();
|
|
20
20
|
}
|
|
21
21
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
22
|
+
if (core_1.Utils.isRawSql(field)) {
|
|
23
|
+
return this.knex.raw(field.sql, field.params);
|
|
24
|
+
}
|
|
22
25
|
if (typeof field !== 'string') {
|
|
23
26
|
return field;
|
|
24
27
|
}
|
|
@@ -36,12 +39,26 @@ class QueryBuilderHelper {
|
|
|
36
39
|
parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
|
|
37
40
|
}
|
|
38
41
|
}
|
|
42
|
+
// flatten the value if we see we are expanding nested composite key
|
|
43
|
+
// hackish, but cleaner solution would require quite a lot of refactoring
|
|
44
|
+
if (fields.length !== parts.length && Array.isArray(value)) {
|
|
45
|
+
value.forEach(row => {
|
|
46
|
+
if (Array.isArray(row)) {
|
|
47
|
+
const tmp = core_1.Utils.flatten(row);
|
|
48
|
+
row.length = 0;
|
|
49
|
+
row.push(...tmp);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
39
53
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
40
54
|
}
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
56
|
+
if (rawField) {
|
|
57
|
+
return this.knex.raw(rawField.sql, rawField.params);
|
|
58
|
+
}
|
|
43
59
|
const [a, f] = this.splitField(field);
|
|
44
60
|
const prop = this.getProperty(f, a);
|
|
61
|
+
let ret = field;
|
|
45
62
|
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
46
63
|
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
47
64
|
return this.alias + '.' + prop.fieldNames[0];
|
|
@@ -65,15 +82,12 @@ class QueryBuilderHelper {
|
|
|
65
82
|
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
|
|
66
83
|
}
|
|
67
84
|
// do not wrap custom expressions
|
|
68
|
-
if (!
|
|
85
|
+
if (!rawField) {
|
|
69
86
|
ret = this.prefix(field);
|
|
70
87
|
}
|
|
71
88
|
if (alias) {
|
|
72
89
|
ret += ' as ' + alias;
|
|
73
90
|
}
|
|
74
|
-
if (customExpression) {
|
|
75
|
-
return this.knex.raw(ret, value);
|
|
76
|
-
}
|
|
77
91
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
78
92
|
return ret;
|
|
79
93
|
}
|
|
@@ -212,20 +226,14 @@ class QueryBuilderHelper {
|
|
|
212
226
|
if (operator === '$exists') {
|
|
213
227
|
value = null;
|
|
214
228
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const { sql, bindings } = k.toSQL().toNative();
|
|
223
|
-
if (params2.length > 0) {
|
|
224
|
-
params.push(...bindings);
|
|
225
|
-
params.push(...params2);
|
|
226
|
-
return sql + ' = ?';
|
|
229
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
230
|
+
if (rawField) {
|
|
231
|
+
let sql = rawField.sql;
|
|
232
|
+
params.push(...rawField.params);
|
|
233
|
+
params.push(...core_1.Utils.asArray(value));
|
|
234
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
235
|
+
sql += ' = ?';
|
|
227
236
|
}
|
|
228
|
-
params.push(...bindings);
|
|
229
237
|
return sql;
|
|
230
238
|
}
|
|
231
239
|
const sql = this.mapper(key);
|
|
@@ -241,7 +249,7 @@ class QueryBuilderHelper {
|
|
|
241
249
|
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
242
250
|
}
|
|
243
251
|
mapJoinColumns(type, join) {
|
|
244
|
-
if (join.prop && join.prop.
|
|
252
|
+
if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
|
|
245
253
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
246
254
|
return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
|
|
247
255
|
});
|
|
@@ -254,7 +262,7 @@ class QueryBuilderHelper {
|
|
|
254
262
|
isOneToOneInverse(field) {
|
|
255
263
|
const meta = this.metadata.find(this.entityName);
|
|
256
264
|
const prop = meta.properties[field];
|
|
257
|
-
return prop && prop.
|
|
265
|
+
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
258
266
|
}
|
|
259
267
|
getTableName(entityName) {
|
|
260
268
|
const meta = this.metadata.find(entityName);
|
|
@@ -299,7 +307,7 @@ class QueryBuilderHelper {
|
|
|
299
307
|
let mergeParam = item.merge;
|
|
300
308
|
if (core_1.Utils.isObject(item.merge)) {
|
|
301
309
|
mergeParam = {};
|
|
302
|
-
|
|
310
|
+
core_1.Utils.keys(item.merge).forEach(key => {
|
|
303
311
|
const k = this.mapper(key, type);
|
|
304
312
|
mergeParam[k] = item.merge[key];
|
|
305
313
|
});
|
|
@@ -336,28 +344,20 @@ class QueryBuilderHelper {
|
|
|
336
344
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
337
345
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
338
346
|
}
|
|
339
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
340
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
341
|
-
}
|
|
342
347
|
const op = cond[key] === null ? 'is' : '=';
|
|
348
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
349
|
+
if (raw) {
|
|
350
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
351
|
+
if (value.length > 0) {
|
|
352
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
353
|
+
}
|
|
354
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
355
|
+
}
|
|
343
356
|
if (this.subQueries[key]) {
|
|
344
357
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
345
358
|
}
|
|
346
359
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
347
360
|
}
|
|
348
|
-
processCustomExpression(clause, m, key, cond, type) {
|
|
349
|
-
// unwind parameters when ? found in field name
|
|
350
|
-
const count = key.concat('?').match(/\?/g).length - 1;
|
|
351
|
-
const value = core_1.Utils.asArray(cond[key]);
|
|
352
|
-
const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
353
|
-
const params2 = value.slice(count);
|
|
354
|
-
const k = this.mapper(key, type, params1);
|
|
355
|
-
if (params2.length > 0) {
|
|
356
|
-
const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
|
|
357
|
-
return void clause[m](k, val);
|
|
358
|
-
}
|
|
359
|
-
clause[m](k);
|
|
360
|
-
}
|
|
361
361
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
362
362
|
// grouped condition for one field
|
|
363
363
|
let value = cond[key];
|
|
@@ -394,7 +394,8 @@ class QueryBuilderHelper {
|
|
|
394
394
|
}));
|
|
395
395
|
}
|
|
396
396
|
else {
|
|
397
|
-
|
|
397
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
398
|
+
qb[m](mappedKey, replacement, value[op]);
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
401
|
getOperatorReplacement(op, value) {
|
|
@@ -422,20 +423,21 @@ class QueryBuilderHelper {
|
|
|
422
423
|
}
|
|
423
424
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
424
425
|
const ret = [];
|
|
425
|
-
Object.keys(orderBy).forEach(
|
|
426
|
-
const direction = orderBy[
|
|
426
|
+
Object.keys(orderBy).forEach(key => {
|
|
427
|
+
const direction = orderBy[key];
|
|
427
428
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
428
|
-
|
|
429
|
-
|
|
429
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
430
|
+
if (raw) {
|
|
431
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
430
432
|
return;
|
|
431
433
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const prop = this.getProperty(
|
|
437
|
-
const noPrefix = (prop && prop.persist === false && !prop.formula) ||
|
|
438
|
-
const column = this.mapper(noPrefix ?
|
|
434
|
+
core_1.Utils.splitPrimaryKeys(key).forEach(f => {
|
|
435
|
+
// eslint-disable-next-line prefer-const
|
|
436
|
+
let [alias, field] = this.splitField(f, true);
|
|
437
|
+
alias = populate[alias] || alias;
|
|
438
|
+
const prop = this.getProperty(field, alias);
|
|
439
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
440
|
+
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
439
441
|
/* istanbul ignore next */
|
|
440
442
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
441
443
|
const customOrder = prop?.customOrder;
|
|
@@ -447,11 +449,23 @@ class QueryBuilderHelper {
|
|
|
447
449
|
});
|
|
448
450
|
return ret.join(', ');
|
|
449
451
|
}
|
|
450
|
-
finalize(type, qb, meta) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
452
|
+
finalize(type, qb, meta, data) {
|
|
453
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
457
|
+
const returningProps = meta.hydrateProps
|
|
458
|
+
.filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
|
|
459
|
+
.filter(prop => !(prop.name in data) || core_1.Utils.isRawSql(data[prop.name]));
|
|
460
|
+
if (returningProps.length > 0) {
|
|
461
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
465
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
466
|
+
if (returningProps.length > 0) {
|
|
467
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
468
|
+
}
|
|
455
469
|
}
|
|
456
470
|
}
|
|
457
471
|
splitField(field, greedyAlias = false) {
|
|
@@ -494,20 +508,20 @@ class QueryBuilderHelper {
|
|
|
494
508
|
}
|
|
495
509
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
496
510
|
}
|
|
497
|
-
static isCustomExpression(field, hasAlias = false) {
|
|
498
|
-
// if we do not have alias, we don't consider spaces as custom expressions
|
|
499
|
-
const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
|
|
500
|
-
return !!field.match(re);
|
|
501
|
-
}
|
|
502
511
|
prefix(field, always = false, quote = false) {
|
|
503
512
|
let ret;
|
|
504
513
|
if (!this.isPrefixed(field)) {
|
|
505
514
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
506
515
|
const fieldName = this.fieldName(field, this.alias, always);
|
|
507
|
-
if (
|
|
508
|
-
return fieldName;
|
|
516
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
517
|
+
return fieldName.sql;
|
|
518
|
+
}
|
|
519
|
+
if (this.isPrefixed(fieldName)) {
|
|
520
|
+
ret = fieldName;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
ret = alias + fieldName;
|
|
509
524
|
}
|
|
510
|
-
ret = alias + fieldName;
|
|
511
525
|
}
|
|
512
526
|
else {
|
|
513
527
|
const [a, ...rest] = field.split('.');
|
|
@@ -545,15 +559,15 @@ class QueryBuilderHelper {
|
|
|
545
559
|
}
|
|
546
560
|
if (prop.fieldNameRaw) {
|
|
547
561
|
if (!always) {
|
|
548
|
-
return prop.fieldNameRaw
|
|
549
|
-
.replace(
|
|
550
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
562
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
563
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
564
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
551
565
|
}
|
|
552
566
|
if (alias) {
|
|
553
|
-
return prop.fieldNameRaw.replace(
|
|
567
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
554
568
|
}
|
|
555
569
|
/* istanbul ignore next */
|
|
556
|
-
return prop.fieldNameRaw;
|
|
570
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
557
571
|
}
|
|
558
572
|
/* istanbul ignore next */
|
|
559
573
|
return prop.fieldNames?.[0] ?? field;
|
|
@@ -564,7 +578,7 @@ class QueryBuilderHelper {
|
|
|
564
578
|
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
565
579
|
if (alias && meta) {
|
|
566
580
|
const prop = meta.properties[alias];
|
|
567
|
-
if (prop?.
|
|
581
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
568
582
|
// we want to select the full object property so hydration works as expected
|
|
569
583
|
if (prop.object) {
|
|
570
584
|
return prop;
|
|
@@ -596,13 +610,13 @@ class QueryBuilderHelper {
|
|
|
596
610
|
return;
|
|
597
611
|
}
|
|
598
612
|
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
599
|
-
const copy = data[k];
|
|
613
|
+
const copy = core_1.Utils.flatten(data[k]);
|
|
600
614
|
delete data[k];
|
|
601
615
|
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
602
616
|
return;
|
|
603
617
|
}
|
|
604
618
|
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
605
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
|
|
619
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
|
|
606
620
|
}
|
|
607
621
|
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
608
622
|
const quoted = this.platform.quoteValue(data[k]);
|
|
@@ -3,7 +3,8 @@ import type { IQueryBuilder } from '../typings';
|
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
|
-
export declare class ScalarCriteriaNode extends CriteriaNode {
|
|
7
|
-
process
|
|
6
|
+
export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
+
process(qb: IQueryBuilder<T>, alias?: string): any;
|
|
8
|
+
willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
|
|
8
9
|
shouldJoin(): boolean;
|
|
9
10
|
}
|
|
@@ -13,23 +13,26 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
13
13
|
const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false`
|
|
14
14
|
const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
|
|
15
15
|
const field = `${alias}.${this.prop.name}`;
|
|
16
|
-
const type = this.prop.
|
|
16
|
+
const type = this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY ? 'pivotJoin' : 'leftJoin';
|
|
17
17
|
qb.join(field, nestedAlias, undefined, type, path);
|
|
18
18
|
// select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity
|
|
19
|
-
if (this.prop.
|
|
19
|
+
if (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !parentPath.includes('.')) {
|
|
20
20
|
qb.addSelect(field);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
return this.payload;
|
|
24
24
|
}
|
|
25
|
+
willAutoJoin(qb, alias) {
|
|
26
|
+
return this.shouldJoin();
|
|
27
|
+
}
|
|
25
28
|
shouldJoin() {
|
|
26
29
|
if (!this.parent || !this.prop) {
|
|
27
30
|
return false;
|
|
28
31
|
}
|
|
29
|
-
switch (this.prop.
|
|
30
|
-
case core_1.
|
|
31
|
-
case core_1.
|
|
32
|
-
case core_1.
|
|
32
|
+
switch (this.prop.kind) {
|
|
33
|
+
case core_1.ReferenceKind.ONE_TO_MANY: return true;
|
|
34
|
+
case core_1.ReferenceKind.MANY_TO_MANY: return true;
|
|
35
|
+
case core_1.ReferenceKind.ONE_TO_ONE: return !this.prop.owner;
|
|
33
36
|
default: return false; // SCALAR, MANY_TO_ONE
|
|
34
37
|
}
|
|
35
38
|
}
|
package/schema/DatabaseSchema.js
CHANGED
|
@@ -78,15 +78,15 @@ class DatabaseSchema {
|
|
|
78
78
|
if (prop.persist === false || !prop.fieldNames) {
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
|
-
if (meta.pivotTable || (core_1.
|
|
81
|
+
if (meta.pivotTable || (core_1.ReferenceKind.EMBEDDED && prop.object)) {
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
84
84
|
const getRootProperty = (prop) => prop.embedded ? getRootProperty(meta.properties[prop.embedded[0]]) : prop;
|
|
85
85
|
const rootProp = getRootProperty(prop);
|
|
86
|
-
if (rootProp.
|
|
86
|
+
if (rootProp.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
87
87
|
return prop === rootProp || !rootProp.object;
|
|
88
88
|
}
|
|
89
|
-
return [core_1.
|
|
89
|
+
return [core_1.ReferenceKind.SCALAR, core_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && prop.owner);
|
|
90
90
|
}
|
|
91
91
|
toJSON() {
|
|
92
92
|
const { platform, namespaces, ...rest } = this;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Dictionary, EntityMetadata, EntityProperty, NamingStrategy } from '@mikro-orm/core';
|
|
2
2
|
import type { SchemaHelper } from './SchemaHelper';
|
|
3
|
-
import type {
|
|
3
|
+
import type { CheckDef, Column, ForeignKey, IndexDef } from '../typings';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
5
5
|
/**
|
|
6
6
|
* @internal
|
|
@@ -17,9 +17,9 @@ export declare class DatabaseTable {
|
|
|
17
17
|
constructor(platform: AbstractSqlPlatform, name: string, schema?: string | undefined);
|
|
18
18
|
getColumns(): Column[];
|
|
19
19
|
getColumn(name: string): Column | undefined;
|
|
20
|
-
getIndexes():
|
|
21
|
-
getChecks():
|
|
22
|
-
init(cols: Column[], indexes:
|
|
20
|
+
getIndexes(): IndexDef[];
|
|
21
|
+
getChecks(): CheckDef[];
|
|
22
|
+
init(cols: Column[], indexes: IndexDef[] | undefined, checks: CheckDef<unknown>[] | undefined, pks: string[], fks?: Dictionary<ForeignKey>, enums?: Dictionary<string[]>): void;
|
|
23
23
|
addColumn(column: Column): void;
|
|
24
24
|
addColumnFromProperty(prop: EntityProperty, meta: EntityMetadata): void;
|
|
25
25
|
private getIndexName;
|
|
@@ -30,14 +30,14 @@ export declare class DatabaseTable {
|
|
|
30
30
|
getShortestName(): string;
|
|
31
31
|
getForeignKeys(): Dictionary<ForeignKey>;
|
|
32
32
|
hasColumn(columnName: string): boolean;
|
|
33
|
-
getIndex(indexName: string):
|
|
33
|
+
getIndex(indexName: string): IndexDef | undefined;
|
|
34
34
|
hasIndex(indexName: string): boolean;
|
|
35
|
-
getCheck(checkName: string):
|
|
35
|
+
getCheck(checkName: string): CheckDef<unknown> | undefined;
|
|
36
36
|
hasCheck(checkName: string): boolean;
|
|
37
|
-
getPrimaryKey():
|
|
37
|
+
getPrimaryKey(): IndexDef | undefined;
|
|
38
38
|
hasPrimaryKey(): boolean;
|
|
39
39
|
private getPropertyDeclaration;
|
|
40
|
-
private
|
|
40
|
+
private getReferenceKind;
|
|
41
41
|
private getPropertyName;
|
|
42
42
|
private getPropertyType;
|
|
43
43
|
private getPropertyDefaultValue;
|
|
@@ -48,6 +48,6 @@ export declare class DatabaseTable {
|
|
|
48
48
|
expression?: string;
|
|
49
49
|
options?: Dictionary;
|
|
50
50
|
}, type: 'index' | 'unique' | 'primary'): void;
|
|
51
|
-
addCheck(check:
|
|
51
|
+
addCheck(check: CheckDef): void;
|
|
52
52
|
toJSON(): Dictionary;
|
|
53
53
|
}
|
package/schema/DatabaseTable.js
CHANGED
|
@@ -51,7 +51,6 @@ class DatabaseTable {
|
|
|
51
51
|
}
|
|
52
52
|
addColumnFromProperty(prop, meta) {
|
|
53
53
|
prop.fieldNames.forEach((field, idx) => {
|
|
54
|
-
var _a;
|
|
55
54
|
const type = prop.enum ? 'enum' : prop.columnTypes[idx];
|
|
56
55
|
const mappedType = this.platform.getMappedType(type);
|
|
57
56
|
if (mappedType instanceof core_1.DecimalType) {
|
|
@@ -64,9 +63,9 @@ class DatabaseTable {
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
if (mappedType instanceof core_1.DateTimeType) {
|
|
67
|
-
prop.length
|
|
66
|
+
prop.length ??= this.platform.getDefaultDateTimeLength();
|
|
68
67
|
}
|
|
69
|
-
const primary = !meta.compositePK && !!prop.primary && prop.
|
|
68
|
+
const primary = !meta.compositePK && !!prop.primary && prop.kind === core_1.ReferenceKind.SCALAR && this.platform.isNumericColumn(mappedType);
|
|
70
69
|
this.columns[field] = {
|
|
71
70
|
name: prop.fieldNames[idx],
|
|
72
71
|
type: prop.columnTypes[idx],
|
|
@@ -84,11 +83,11 @@ class DatabaseTable {
|
|
|
84
83
|
extra: prop.extra,
|
|
85
84
|
ignoreSchemaChanges: prop.ignoreSchemaChanges,
|
|
86
85
|
};
|
|
87
|
-
|
|
86
|
+
this.columns[field].unsigned ||= this.columns[field].autoincrement;
|
|
88
87
|
const defaultValue = this.platform.getSchemaHelper().normalizeDefaultValue(prop.defaultRaw, prop.length);
|
|
89
88
|
this.columns[field].default = defaultValue;
|
|
90
89
|
});
|
|
91
|
-
if ([core_1.
|
|
90
|
+
if ([core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
92
91
|
const constraintName = this.getIndexName(true, prop.fieldNames, 'foreign');
|
|
93
92
|
let schema = prop.targetMeta.schema === '*' ? this.schema : prop.targetMeta.schema ?? this.schema;
|
|
94
93
|
if (prop.referencedTableName.includes('.')) {
|
|
@@ -102,11 +101,11 @@ class DatabaseTable {
|
|
|
102
101
|
referencedTableName: schema ? `${schema}.${prop.referencedTableName}` : prop.referencedTableName,
|
|
103
102
|
};
|
|
104
103
|
const cascade = prop.cascade.includes(core_1.Cascade.REMOVE) || prop.cascade.includes(core_1.Cascade.ALL);
|
|
105
|
-
if (prop.
|
|
106
|
-
this.foreignKeys[constraintName].deleteRule = prop.
|
|
104
|
+
if (prop.deleteRule || cascade || prop.nullable) {
|
|
105
|
+
this.foreignKeys[constraintName].deleteRule = prop.deleteRule || (cascade ? 'cascade' : 'set null');
|
|
107
106
|
}
|
|
108
|
-
if (prop.
|
|
109
|
-
this.foreignKeys[constraintName].updateRule = prop.
|
|
107
|
+
if (prop.updateRule || prop.cascade.includes(core_1.Cascade.PERSIST) || prop.cascade.includes(core_1.Cascade.ALL)) {
|
|
108
|
+
this.foreignKeys[constraintName].updateRule = prop.updateRule || 'cascade';
|
|
110
109
|
}
|
|
111
110
|
}
|
|
112
111
|
if (prop.index) {
|
|
@@ -164,8 +163,8 @@ class DatabaseTable {
|
|
|
164
163
|
}
|
|
165
164
|
const meta = schema.init().meta;
|
|
166
165
|
meta.relations
|
|
167
|
-
.filter(prop => prop.primary && prop.
|
|
168
|
-
.forEach(prop => prop.
|
|
166
|
+
.filter(prop => prop.primary && prop.kind === core_1.ReferenceKind.MANY_TO_ONE && !meta.compositePK)
|
|
167
|
+
.forEach(prop => prop.kind = core_1.ReferenceKind.ONE_TO_ONE);
|
|
169
168
|
return meta;
|
|
170
169
|
}
|
|
171
170
|
/**
|
|
@@ -206,20 +205,20 @@ class DatabaseTable {
|
|
|
206
205
|
const prop = this.getPropertyName(namingStrategy, column);
|
|
207
206
|
const index = compositeFkIndexes[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && !idx.unique && !idx.primary);
|
|
208
207
|
const unique = compositeFkUniques[prop] || this.indexes.find(idx => idx.columnNames[0] === column.name && !idx.composite && idx.unique && !idx.primary);
|
|
209
|
-
const
|
|
208
|
+
const kind = this.getReferenceKind(fk, unique);
|
|
210
209
|
const type = this.getPropertyType(namingStrategy, column, fk);
|
|
211
210
|
const fkOptions = {};
|
|
212
211
|
if (fk) {
|
|
213
212
|
fkOptions.fieldNames = fk.columnNames;
|
|
214
213
|
fkOptions.referencedTableName = fk.referencedTableName;
|
|
215
214
|
fkOptions.referencedColumnNames = fk.referencedColumnNames;
|
|
216
|
-
fkOptions.
|
|
217
|
-
fkOptions.
|
|
215
|
+
fkOptions.updateRule = fk.updateRule?.toLowerCase();
|
|
216
|
+
fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
|
|
218
217
|
}
|
|
219
218
|
return {
|
|
220
219
|
name: prop,
|
|
221
220
|
type,
|
|
222
|
-
|
|
221
|
+
kind,
|
|
223
222
|
columnType: column.type,
|
|
224
223
|
default: this.getPropertyDefaultValue(schemaHelper, column, type),
|
|
225
224
|
defaultRaw: this.getPropertyDefaultValue(schemaHelper, column, type, true),
|
|
@@ -234,14 +233,14 @@ class DatabaseTable {
|
|
|
234
233
|
...fkOptions,
|
|
235
234
|
};
|
|
236
235
|
}
|
|
237
|
-
|
|
236
|
+
getReferenceKind(fk, unique) {
|
|
238
237
|
if (fk && unique) {
|
|
239
|
-
return core_1.
|
|
238
|
+
return core_1.ReferenceKind.ONE_TO_ONE;
|
|
240
239
|
}
|
|
241
240
|
if (fk) {
|
|
242
|
-
return core_1.
|
|
241
|
+
return core_1.ReferenceKind.MANY_TO_ONE;
|
|
243
242
|
}
|
|
244
|
-
return core_1.
|
|
243
|
+
return core_1.ReferenceKind.SCALAR;
|
|
245
244
|
}
|
|
246
245
|
getPropertyName(namingStrategy, column) {
|
|
247
246
|
const fk = Object.values(this.foreignKeys).find(fk => fk.columnNames.includes(column.name));
|
|
@@ -288,7 +287,7 @@ class DatabaseTable {
|
|
|
288
287
|
return '' + val;
|
|
289
288
|
}
|
|
290
289
|
addIndex(meta, index, type) {
|
|
291
|
-
const properties = core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop => meta.properties[prop].fieldNames));
|
|
290
|
+
const properties = core_1.Utils.unique(core_1.Utils.flatten(core_1.Utils.asArray(index.properties).map(prop => meta.properties[prop].fieldNames)));
|
|
292
291
|
if (properties.length === 0 && !index.expression) {
|
|
293
292
|
return;
|
|
294
293
|
}
|
|
@@ -308,10 +307,10 @@ class DatabaseTable {
|
|
|
308
307
|
}
|
|
309
308
|
toJSON() {
|
|
310
309
|
const { platform, columns, ...rest } = this;
|
|
311
|
-
const columnsMapped =
|
|
310
|
+
const columnsMapped = core_1.Utils.keys(columns).reduce((o, col) => {
|
|
312
311
|
const { mappedType, ...restCol } = columns[col];
|
|
313
312
|
o[col] = restCol;
|
|
314
|
-
o[col].mappedType =
|
|
313
|
+
o[col].mappedType = core_1.Utils.keys(core_1.t).find(k => core_1.t[k] === mappedType.constructor);
|
|
315
314
|
return o;
|
|
316
315
|
}, {});
|
|
317
316
|
return { columns: columnsMapped, ...rest };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CheckDef, Column, ForeignKey, IndexDef, SchemaDifference, TableDifference } from '../typings';
|
|
2
2
|
import type { DatabaseSchema } from './DatabaseSchema';
|
|
3
3
|
import type { DatabaseTable } from './DatabaseTable';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
@@ -45,12 +45,12 @@ export declare class SchemaComparator {
|
|
|
45
45
|
* Finds the difference between the indexes index1 and index2.
|
|
46
46
|
* Compares index1 with index2 and returns index2 if there are any differences or false in case there are no differences.
|
|
47
47
|
*/
|
|
48
|
-
diffIndex(index1:
|
|
48
|
+
diffIndex(index1: IndexDef, index2: IndexDef): boolean;
|
|
49
49
|
/**
|
|
50
50
|
* Checks if the other index already fulfills all the indexing and constraint needs of the current one.
|
|
51
51
|
*/
|
|
52
|
-
isIndexFulfilledBy(index1:
|
|
53
|
-
diffCheck(check1:
|
|
52
|
+
isIndexFulfilledBy(index1: IndexDef, index2: IndexDef): boolean;
|
|
53
|
+
diffCheck(check1: CheckDef, check2: CheckDef): boolean;
|
|
54
54
|
hasSameDefaultValue(from: Column, to: Column): boolean;
|
|
55
55
|
private mapColumnToProperty;
|
|
56
56
|
private log;
|
|
@@ -425,8 +425,10 @@ class SchemaComparator {
|
|
|
425
425
|
return index1.primary === index2.primary && index1.unique === index2.unique;
|
|
426
426
|
}
|
|
427
427
|
diffCheck(check1, check2) {
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
// check constraint definition might be normalized by the driver,
|
|
429
|
+
// e.g. quotes might be added (https://github.com/mikro-orm/mikro-orm/issues/3827)
|
|
430
|
+
const simplify = (str) => str?.replace(/['"`()]/g, '').toLowerCase();
|
|
431
|
+
return simplify(check1.expression) !== simplify(check2.expression);
|
|
430
432
|
}
|
|
431
433
|
hasSameDefaultValue(from, to) {
|
|
432
434
|
if (from.default == null || from.default.toString().toLowerCase() === 'null' || from.default.toString().startsWith('nextval(')) {
|
package/schema/SchemaHelper.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Knex } from 'knex';
|
|
|
2
2
|
import type { Connection, Dictionary } from '@mikro-orm/core';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
5
|
-
import type {
|
|
5
|
+
import type { CheckDef, Column, IndexDef, Table, TableDifference } from '../typings';
|
|
6
6
|
import type { DatabaseTable } from './DatabaseTable';
|
|
7
7
|
import type { DatabaseSchema } from './DatabaseSchema';
|
|
8
8
|
export declare abstract class SchemaHelper {
|
|
@@ -14,28 +14,28 @@ export declare abstract class SchemaHelper {
|
|
|
14
14
|
getSchemaEnd(): string;
|
|
15
15
|
finalizeTable(table: Knex.TableBuilder, charset: string, collate?: string): void;
|
|
16
16
|
supportsSchemaConstraints(): boolean;
|
|
17
|
-
getPrimaryKeys(connection: AbstractSqlConnection, indexes:
|
|
17
|
+
getPrimaryKeys(connection: AbstractSqlConnection, indexes: IndexDef[] | undefined, tableName: string, schemaName?: string): Promise<string[]>;
|
|
18
18
|
getForeignKeys(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Dictionary>;
|
|
19
19
|
protected getTableKey(t: Table): string;
|
|
20
|
-
getEnumDefinitions(connection: AbstractSqlConnection, checks:
|
|
20
|
+
getEnumDefinitions(connection: AbstractSqlConnection, checks: CheckDef[], tableName: string, schemaName?: string): Promise<Dictionary<string[]>>;
|
|
21
21
|
loadInformationSchema(schema: DatabaseSchema, connection: AbstractSqlConnection, tables: Table[]): Promise<void>;
|
|
22
22
|
getListTablesSQL(schemaName?: string): string;
|
|
23
23
|
getRenameColumnSQL(tableName: string, oldColumnName: string, to: Column, schemaName?: string): string;
|
|
24
|
-
getCreateIndexSQL(tableName: string, index:
|
|
25
|
-
getDropIndexSQL(tableName: string, index:
|
|
26
|
-
getRenameIndexSQL(tableName: string, index:
|
|
24
|
+
getCreateIndexSQL(tableName: string, index: IndexDef): string;
|
|
25
|
+
getDropIndexSQL(tableName: string, index: IndexDef): string;
|
|
26
|
+
getRenameIndexSQL(tableName: string, index: IndexDef, oldIndexName: string): string;
|
|
27
27
|
hasNonDefaultPrimaryKeyName(table: DatabaseTable): boolean;
|
|
28
28
|
createTableColumn(table: Knex.TableBuilder, column: Column, fromTable: DatabaseTable, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
29
29
|
configureColumn(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
30
30
|
configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>): Knex.ColumnBuilder;
|
|
31
31
|
getPreAlterTable(tableDiff: TableDifference, safe: boolean): string;
|
|
32
|
-
getAlterColumnAutoincrement(tableName: string, column: Column): string;
|
|
33
|
-
getChangeColumnCommentSQL(tableName: string, to: Column): string;
|
|
32
|
+
getAlterColumnAutoincrement(tableName: string, column: Column, schemaName?: string): string;
|
|
33
|
+
getChangeColumnCommentSQL(tableName: string, to: Column, schemaName?: string): string;
|
|
34
34
|
getNamespaces(connection: AbstractSqlConnection): Promise<string[]>;
|
|
35
35
|
getColumns(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<Column[]>;
|
|
36
|
-
getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<
|
|
37
|
-
getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string, columns?: Column[]): Promise<
|
|
38
|
-
protected mapIndexes(indexes:
|
|
36
|
+
getIndexes(connection: AbstractSqlConnection, tableName: string, schemaName?: string): Promise<IndexDef[]>;
|
|
37
|
+
getChecks(connection: AbstractSqlConnection, tableName: string, schemaName?: string, columns?: Column[]): Promise<CheckDef[]>;
|
|
38
|
+
protected mapIndexes(indexes: IndexDef[]): Promise<IndexDef[]>;
|
|
39
39
|
getForeignKeysSQL(tableName: string, schemaName?: string): string;
|
|
40
40
|
mapForeignKeys(fks: any[], tableName: string, schemaName?: string): Dictionary;
|
|
41
41
|
normalizeDefaultValue(defaultValue: string, length?: number, defaultValues?: Dictionary<string[]>): string | number;
|
package/schema/SchemaHelper.js
CHANGED
|
@@ -122,17 +122,17 @@ class SchemaHelper {
|
|
|
122
122
|
core_1.Utils.runIfNotEmpty(() => col.defaultTo(column.default == null ? null : knex.raw(column.default)), guard('default'));
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
125
|
-
core_1.Utils.runIfNotEmpty(() => col.defaultTo(
|
|
125
|
+
core_1.Utils.runIfNotEmpty(() => col.defaultTo(knex.raw(column.default)), column.default != null && column.default !== 'null');
|
|
126
126
|
}
|
|
127
127
|
return col;
|
|
128
128
|
}
|
|
129
129
|
getPreAlterTable(tableDiff, safe) {
|
|
130
130
|
return '';
|
|
131
131
|
}
|
|
132
|
-
getAlterColumnAutoincrement(tableName, column) {
|
|
132
|
+
getAlterColumnAutoincrement(tableName, column, schemaName) {
|
|
133
133
|
return '';
|
|
134
134
|
}
|
|
135
|
-
getChangeColumnCommentSQL(tableName, to) {
|
|
135
|
+
getChangeColumnCommentSQL(tableName, to, schemaName) {
|
|
136
136
|
return '';
|
|
137
137
|
}
|
|
138
138
|
async getNamespaces(connection) {
|