@mikro-orm/sql 7.0.0-dev.300 → 7.0.0-dev.301
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.js +2 -1
- package/AbstractSqlDriver.js +135 -39
- package/AbstractSqlPlatform.js +2 -3
- package/PivotCollectionPersister.js +2 -2
- package/SqlEntityManager.d.ts +1 -1
- package/SqlEntityManager.js +5 -5
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +3 -4
- package/dialects/mysql/BaseMySqlPlatform.js +1 -2
- package/dialects/mysql/MySqlSchemaHelper.js +21 -10
- package/dialects/postgresql/BasePostgreSqlPlatform.js +38 -30
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +55 -45
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.js +4 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +11 -9
- package/package.json +29 -29
- package/plugin/transformer.js +17 -16
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +5 -1
- package/query/NativeQueryBuilder.js +1 -1
- package/query/ObjectCriteriaNode.js +67 -43
- package/query/QueryBuilder.d.ts +25 -12
- package/query/QueryBuilder.js +105 -49
- package/query/QueryBuilderHelper.js +41 -14
- package/query/ScalarCriteriaNode.js +13 -6
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.js +21 -15
- package/schema/DatabaseTable.js +81 -51
- package/schema/SchemaComparator.js +54 -30
- package/schema/SchemaHelper.js +28 -8
- package/schema/SqlSchemaGenerator.js +13 -7
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +1 -1
package/AbstractSqlConnection.js
CHANGED
|
@@ -181,7 +181,8 @@ export class AbstractSqlConnection extends Connection {
|
|
|
181
181
|
try {
|
|
182
182
|
const res = (ctx ?? this.getClient()).getExecutor().stream(compiled, 1);
|
|
183
183
|
this.logQuery(sql, {
|
|
184
|
-
sql,
|
|
184
|
+
sql,
|
|
185
|
+
params,
|
|
185
186
|
...loggerContext,
|
|
186
187
|
affected: Utils.isPlainObject(res) ? res.affectedRows : undefined,
|
|
187
188
|
});
|
package/AbstractSqlDriver.js
CHANGED
|
@@ -37,7 +37,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
37
37
|
throw new Error('Collation option for SQL drivers must be a string (collation name). Use a CollationOptions object only with MongoDB.');
|
|
38
38
|
}
|
|
39
39
|
if (options.indexHint != null && typeof options.indexHint !== 'string') {
|
|
40
|
-
throw new Error(
|
|
40
|
+
throw new Error("indexHint for SQL drivers must be a string (e.g. 'force index(my_index)'). Use an object only with MongoDB.");
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
createEntityManager(useContext) {
|
|
@@ -300,7 +300,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
300
300
|
}
|
|
301
301
|
// Polymorphic to-one: iterate targets, find the matching one, build entity from its columns.
|
|
302
302
|
// Skip :ref hints — no JOINs were created, so the FK reference is already set by the result mapper.
|
|
303
|
-
if (prop.polymorphic &&
|
|
303
|
+
if (prop.polymorphic &&
|
|
304
|
+
prop.polymorphTargets?.length &&
|
|
305
|
+
!ref &&
|
|
306
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
304
307
|
const basePath = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
|
|
305
308
|
const pathPrefix = !parentJoinPath ? '[populate]' : '';
|
|
306
309
|
let matched = false;
|
|
@@ -309,16 +312,22 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
309
312
|
const relationAlias = qb.getAliasForJoinPath(targetPath, { matchPopulateJoins: true });
|
|
310
313
|
const meta2 = targetMeta;
|
|
311
314
|
const targetProps = meta2.props.filter(p => this.platform.shouldHaveColumn(p, hint.children || []));
|
|
312
|
-
const hasPK = meta2
|
|
315
|
+
const hasPK = meta2
|
|
316
|
+
.getPrimaryProps()
|
|
317
|
+
.every(pk => pk.fieldNames.every(name => root[`${relationAlias}__${name}`] != null));
|
|
313
318
|
if (hasPK && !matched) {
|
|
314
319
|
matched = true;
|
|
315
|
-
|
|
320
|
+
const relationPojo = {};
|
|
316
321
|
const tz = this.platform.getTimezone();
|
|
317
322
|
for (const p of targetProps) {
|
|
318
323
|
this.mapJoinedProp(relationPojo, p, relationAlias, root, tz, meta2);
|
|
319
324
|
}
|
|
320
325
|
// Inject the entity class constructor so that the factory creates the correct type
|
|
321
|
-
Object.defineProperty(relationPojo, 'constructor', {
|
|
326
|
+
Object.defineProperty(relationPojo, 'constructor', {
|
|
327
|
+
value: meta2.class,
|
|
328
|
+
enumerable: false,
|
|
329
|
+
configurable: true,
|
|
330
|
+
});
|
|
322
331
|
result[prop.name] = relationPojo;
|
|
323
332
|
const populateChildren = hint.children || [];
|
|
324
333
|
this.mapJoinedProps(relationPojo, meta2, populateChildren, qb, root, map, targetPath);
|
|
@@ -369,7 +378,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
369
378
|
return;
|
|
370
379
|
}
|
|
371
380
|
const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
|
|
372
|
-
const targetProps = mapToPk
|
|
381
|
+
const targetProps = mapToPk
|
|
382
|
+
? meta2.getPrimaryProps()
|
|
383
|
+
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
|
|
373
384
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
374
385
|
// and therefore we don't return any record (since all values would be null)
|
|
375
386
|
const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
|
|
@@ -462,7 +473,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
462
473
|
else if (prop.runtimeType === 'Date') {
|
|
463
474
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
464
475
|
const value = root[alias];
|
|
465
|
-
if (tz &&
|
|
476
|
+
if (tz &&
|
|
477
|
+
tz !== 'local' &&
|
|
478
|
+
typeof value === 'string' &&
|
|
479
|
+
!value.includes('+') &&
|
|
480
|
+
value.lastIndexOf('-') < 11 &&
|
|
481
|
+
!value.endsWith('Z')) {
|
|
466
482
|
relationPojo[prop.name] = this.platform.parseDate(value + tz);
|
|
467
483
|
}
|
|
468
484
|
else if (['string', 'number'].includes(typeof value)) {
|
|
@@ -478,10 +494,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
478
494
|
if (prop.kind === ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
|
|
479
495
|
const item = parseJsonSafe(relationPojo[prop.name]);
|
|
480
496
|
if (Array.isArray(item)) {
|
|
481
|
-
relationPojo[prop.name] = item.map(row =>
|
|
497
|
+
relationPojo[prop.name] = item.map(row => row == null ? row : this.comparator.mapResult(prop.targetMeta, row));
|
|
482
498
|
}
|
|
483
499
|
else {
|
|
484
|
-
relationPojo[prop.name] =
|
|
500
|
+
relationPojo[prop.name] =
|
|
501
|
+
item == null ? item : this.comparator.mapResult(prop.targetMeta, item);
|
|
485
502
|
}
|
|
486
503
|
}
|
|
487
504
|
}
|
|
@@ -563,13 +580,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
563
580
|
}
|
|
564
581
|
const tableName = this.getTableName(meta, options);
|
|
565
582
|
let sql = `insert into ${tableName} `;
|
|
566
|
-
sql +=
|
|
583
|
+
sql +=
|
|
584
|
+
fields.length > 0
|
|
585
|
+
? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')'
|
|
586
|
+
: `(${this.platform.quoteIdentifier(pks[0])})`;
|
|
567
587
|
if (this.platform.usesOutputStatement()) {
|
|
568
588
|
const returningProps = this.getTableProps(meta)
|
|
569
589
|
.filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
|
|
570
590
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
571
591
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
572
|
-
sql +=
|
|
592
|
+
sql +=
|
|
593
|
+
returningFields.length > 0
|
|
594
|
+
? ` output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`
|
|
595
|
+
: '';
|
|
573
596
|
}
|
|
574
597
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
575
598
|
sql += ' values ';
|
|
@@ -656,7 +679,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
656
679
|
else {
|
|
657
680
|
const field = prop.fieldNames[0];
|
|
658
681
|
if (!duplicates.includes(field) || !usedDups.includes(field)) {
|
|
659
|
-
if (prop.customType &&
|
|
682
|
+
if (prop.customType &&
|
|
683
|
+
!prop.object &&
|
|
684
|
+
'convertToDatabaseValueSQL' in prop.customType &&
|
|
685
|
+
row[prop.name] != null &&
|
|
686
|
+
!isRaw(row[prop.name])) {
|
|
660
687
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
661
688
|
}
|
|
662
689
|
else {
|
|
@@ -677,7 +704,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
677
704
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
678
705
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
679
706
|
/* v8 ignore next */
|
|
680
|
-
sql +=
|
|
707
|
+
sql +=
|
|
708
|
+
returningFields.length > 0
|
|
709
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
710
|
+
: '';
|
|
681
711
|
}
|
|
682
712
|
if (transform) {
|
|
683
713
|
sql = transform(sql);
|
|
@@ -711,13 +741,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
711
741
|
where = { [meta.primaryKeys[0] ?? pks[0]]: where };
|
|
712
742
|
}
|
|
713
743
|
if (!options.upsert && options.unionWhere?.length) {
|
|
714
|
-
where = await this.applyUnionWhere(meta, where, options, true);
|
|
744
|
+
where = (await this.applyUnionWhere(meta, where, options, true));
|
|
715
745
|
}
|
|
716
746
|
if (Utils.hasObjectKeys(data)) {
|
|
717
747
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
718
748
|
if (options.upsert) {
|
|
719
749
|
/* v8 ignore next */
|
|
720
|
-
const uniqueFields = options.onConflictFields ??
|
|
750
|
+
const uniqueFields = options.onConflictFields ??
|
|
751
|
+
(Utils.isPlainObject(where) ? Utils.keys(where) : meta.primaryKeys);
|
|
721
752
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
722
753
|
qb.insert(data)
|
|
723
754
|
.onConflict(uniqueFields)
|
|
@@ -737,7 +768,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
737
768
|
qb.update(data).where(where);
|
|
738
769
|
// reload generated columns and version fields
|
|
739
770
|
const returning = [];
|
|
740
|
-
meta.props
|
|
771
|
+
meta.props
|
|
772
|
+
.filter(prop => (prop.generated && !prop.primary) || prop.version)
|
|
773
|
+
.forEach(prop => returning.push(prop.name));
|
|
741
774
|
qb.returning(returning);
|
|
742
775
|
}
|
|
743
776
|
res = await this.rethrow(qb.execute('run', false));
|
|
@@ -753,7 +786,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
753
786
|
const meta = this.metadata.get(entityName);
|
|
754
787
|
if (options.upsert) {
|
|
755
788
|
const uniqueFields = options.onConflictFields ??
|
|
756
|
-
(Utils.isPlainObject(where[0])
|
|
789
|
+
(Utils.isPlainObject(where[0])
|
|
790
|
+
? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key))
|
|
791
|
+
: meta.primaryKeys);
|
|
757
792
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
758
793
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
759
794
|
qb.insert(data)
|
|
@@ -824,7 +859,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
824
859
|
if (key in data[idx]) {
|
|
825
860
|
const pks = Utils.getOrderedPrimaryKeys(cond, meta);
|
|
826
861
|
sql += ` when (${pkCond}) then `;
|
|
827
|
-
if (prop.customType &&
|
|
862
|
+
if (prop.customType &&
|
|
863
|
+
!prop.object &&
|
|
864
|
+
'convertToDatabaseValueSQL' in prop.customType &&
|
|
865
|
+
data[idx][prop.name] != null &&
|
|
866
|
+
!isRaw(data[idx][key])) {
|
|
828
867
|
sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
|
|
829
868
|
}
|
|
830
869
|
else {
|
|
@@ -853,7 +892,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
853
892
|
sql = sql.substring(0, sql.length - 2) + ' where ';
|
|
854
893
|
const pkProps = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
|
|
855
894
|
const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
856
|
-
sql +=
|
|
895
|
+
sql +=
|
|
896
|
+
pks.length > 1
|
|
897
|
+
? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})`
|
|
898
|
+
: this.platform.quoteIdentifier(pks[0]);
|
|
857
899
|
const conds = where.map(cond => {
|
|
858
900
|
if (Utils.isPlainObject(cond) && Utils.getObjectKeysSize(cond) === 1) {
|
|
859
901
|
cond = Object.values(cond)[0];
|
|
@@ -876,7 +918,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
876
918
|
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
877
919
|
const returningFields = Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
878
920
|
/* v8 ignore next */
|
|
879
|
-
sql +=
|
|
921
|
+
sql +=
|
|
922
|
+
returningFields.length > 0
|
|
923
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
924
|
+
: '';
|
|
880
925
|
}
|
|
881
926
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
|
|
882
927
|
for (let i = 0; i < collections.length; i++) {
|
|
@@ -947,7 +992,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
947
992
|
const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
|
|
948
993
|
if (coll.getSnapshot() === undefined) {
|
|
949
994
|
if (coll.property.orphanRemoval) {
|
|
950
|
-
const query = qb
|
|
995
|
+
const query = qb
|
|
996
|
+
.delete({ [coll.property.mappedBy]: pks })
|
|
997
|
+
.andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
|
|
951
998
|
await this.rethrow(query.execute());
|
|
952
999
|
continue;
|
|
953
1000
|
}
|
|
@@ -959,7 +1006,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
959
1006
|
continue;
|
|
960
1007
|
}
|
|
961
1008
|
/* v8 ignore next */
|
|
962
|
-
const query = qb
|
|
1009
|
+
const query = qb
|
|
1010
|
+
.update({ [coll.property.mappedBy]: pks })
|
|
1011
|
+
.where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
|
|
963
1012
|
await this.rethrow(query.execute());
|
|
964
1013
|
continue;
|
|
965
1014
|
}
|
|
@@ -972,7 +1021,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
972
1021
|
else {
|
|
973
1022
|
const targetMeta = coll.property.targetMeta;
|
|
974
1023
|
const targetSchema = (coll[0] ?? snap?.[0]) && helper(coll[0] ?? snap?.[0]).getSchema();
|
|
975
|
-
schema =
|
|
1024
|
+
schema =
|
|
1025
|
+
targetMeta.schema === '*'
|
|
1026
|
+
? (options?.schema ?? targetSchema ?? this.config.get('schema'))
|
|
1027
|
+
: targetMeta.schema;
|
|
976
1028
|
}
|
|
977
1029
|
}
|
|
978
1030
|
else if (schema == null) {
|
|
@@ -1009,7 +1061,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1009
1061
|
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
|
|
1010
1062
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
1011
1063
|
const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
1012
|
-
const fields = pivotJoin
|
|
1064
|
+
const fields = pivotJoin
|
|
1065
|
+
? [pivotProp1.name, pivotProp2.name]
|
|
1066
|
+
: [pivotProp1.name, pivotProp2.name, ...childFields];
|
|
1013
1067
|
const res = await this.find(pivotMeta.class, where, {
|
|
1014
1068
|
ctx,
|
|
1015
1069
|
...options,
|
|
@@ -1062,7 +1116,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1062
1116
|
const populateField = pivotJoin ? `${inverseProp.name}:ref` : inverseProp.name;
|
|
1063
1117
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1064
1118
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${inverseProp.name}.${f}`) : [];
|
|
1065
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1119
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1120
|
+
? options.exclude.map(f => `${inverseProp.name}.${f}`)
|
|
1121
|
+
: [];
|
|
1066
1122
|
const fields = pivotJoin
|
|
1067
1123
|
? [inverseProp.name, prop.discriminator, prop.discriminatorColumn]
|
|
1068
1124
|
: [inverseProp.name, prop.discriminator, prop.discriminatorColumn, ...childFields];
|
|
@@ -1072,7 +1128,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1072
1128
|
fields,
|
|
1073
1129
|
exclude: childExclude,
|
|
1074
1130
|
orderBy: this.getPivotOrderBy(prop, inverseProp, orderBy, options?.orderBy),
|
|
1075
|
-
populate: [
|
|
1131
|
+
populate: [
|
|
1132
|
+
{
|
|
1133
|
+
field: populateField,
|
|
1134
|
+
strategy: LoadStrategy.JOINED,
|
|
1135
|
+
joinType: JoinType.innerJoin,
|
|
1136
|
+
children: populate,
|
|
1137
|
+
dataOnly: inverseProp.mapToPk && !pivotJoin,
|
|
1138
|
+
},
|
|
1139
|
+
],
|
|
1076
1140
|
populateWhere: undefined,
|
|
1077
1141
|
// @ts-ignore
|
|
1078
1142
|
_populateWhere: 'infer',
|
|
@@ -1104,7 +1168,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1104
1168
|
const populateField = ownerRelationName;
|
|
1105
1169
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1106
1170
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${ownerRelationName}.${f}`) : [];
|
|
1107
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1171
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1172
|
+
? options.exclude.map(f => `${ownerRelationName}.${f}`)
|
|
1173
|
+
: [];
|
|
1108
1174
|
const fields = [ownerRelationName, tagProp.name, prop.discriminatorColumn, ...childFields];
|
|
1109
1175
|
const res = await this.find(pivotMeta.class, cond, {
|
|
1110
1176
|
ctx,
|
|
@@ -1112,7 +1178,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1112
1178
|
fields,
|
|
1113
1179
|
exclude: childExclude,
|
|
1114
1180
|
orderBy: this.getPivotOrderBy(prop, ownerProp, orderBy, options?.orderBy),
|
|
1115
|
-
populate: [
|
|
1181
|
+
populate: [
|
|
1182
|
+
{
|
|
1183
|
+
field: populateField,
|
|
1184
|
+
strategy: LoadStrategy.JOINED,
|
|
1185
|
+
joinType: JoinType.innerJoin,
|
|
1186
|
+
children: populate,
|
|
1187
|
+
},
|
|
1188
|
+
],
|
|
1116
1189
|
populateWhere: undefined,
|
|
1117
1190
|
// @ts-ignore
|
|
1118
1191
|
_populateWhere: 'infer',
|
|
@@ -1191,7 +1264,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1191
1264
|
}
|
|
1192
1265
|
const relationsToPopulate = populate.map(({ field }) => field.split(':')[0]);
|
|
1193
1266
|
const toPopulate = meta.relations
|
|
1194
|
-
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1267
|
+
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1268
|
+
!prop.owner &&
|
|
1269
|
+
!prop.lazy &&
|
|
1270
|
+
!relationsToPopulate.includes(prop.name))
|
|
1195
1271
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
1196
1272
|
.map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
|
|
1197
1273
|
return [...populate, ...toPopulate];
|
|
@@ -1307,8 +1383,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1307
1383
|
const prop = meta.properties[propName];
|
|
1308
1384
|
// Polymorphic to-one: create a LEFT JOIN per target type
|
|
1309
1385
|
// Skip :ref hints — polymorphic to-one already has FK + discriminator in the row
|
|
1310
|
-
if (prop.polymorphic &&
|
|
1311
|
-
|
|
1386
|
+
if (prop.polymorphic &&
|
|
1387
|
+
prop.polymorphTargets?.length &&
|
|
1388
|
+
!ref &&
|
|
1389
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1390
|
+
const basePath = options.parentJoinPath
|
|
1391
|
+
? `${options.parentJoinPath}.${prop.name}`
|
|
1392
|
+
: `${meta.name}.${prop.name}`;
|
|
1312
1393
|
const pathPrefix = !options.parentJoinPath && populateWhereAll && !basePath.startsWith('[populate]') ? '[populate]' : '';
|
|
1313
1394
|
for (const targetMeta of prop.polymorphTargets) {
|
|
1314
1395
|
const tableAlias = qb.getNextAlias(targetMeta.className);
|
|
@@ -1326,7 +1407,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1326
1407
|
continue;
|
|
1327
1408
|
}
|
|
1328
1409
|
// ignore ref joins of known FKs unless it's a filter hint
|
|
1329
|
-
if (ref &&
|
|
1410
|
+
if (ref &&
|
|
1411
|
+
!hint.filter &&
|
|
1412
|
+
(prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
|
|
1330
1413
|
continue;
|
|
1331
1414
|
}
|
|
1332
1415
|
const meta2 = prop.targetMeta;
|
|
@@ -1372,7 +1455,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1372
1455
|
childExplicitFields.push(f.substring(prop.name.length + 1));
|
|
1373
1456
|
}
|
|
1374
1457
|
});
|
|
1375
|
-
const childExclude = options.exclude
|
|
1458
|
+
const childExclude = options.exclude
|
|
1459
|
+
? Utils.extractChildElements(options.exclude, prop.name)
|
|
1460
|
+
: options.exclude;
|
|
1376
1461
|
if (!ref && (!prop.mapToPk || hint.dataOnly)) {
|
|
1377
1462
|
fields.push(...this.getFieldsForJoinedLoad(qb, meta2, {
|
|
1378
1463
|
...options,
|
|
@@ -1383,7 +1468,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1383
1468
|
parentJoinPath: path,
|
|
1384
1469
|
}));
|
|
1385
1470
|
}
|
|
1386
|
-
else if (hint.filter ||
|
|
1471
|
+
else if (hint.filter ||
|
|
1472
|
+
(prop.mapToPk && !hint.dataOnly) ||
|
|
1473
|
+
(ref && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind))) {
|
|
1387
1474
|
fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
|
|
1388
1475
|
}
|
|
1389
1476
|
}
|
|
@@ -1485,7 +1572,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1485
1572
|
if (childAlias) {
|
|
1486
1573
|
// Map fields using same filtering as joined loading, plus skip PKs
|
|
1487
1574
|
for (const prop of currentMeta.ownProps.filter(p => !p.primary && this.platform.shouldHaveColumn(p, []))) {
|
|
1488
|
-
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1575
|
+
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1576
|
+
deleteFromRoot: true,
|
|
1577
|
+
});
|
|
1489
1578
|
}
|
|
1490
1579
|
}
|
|
1491
1580
|
currentMeta = currentMeta.tptParent;
|
|
@@ -1534,7 +1623,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1534
1623
|
/** @internal */
|
|
1535
1624
|
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
|
|
1536
1625
|
// do not compute the connectionType if EM is provided as it will be computed from it in the QB later on
|
|
1537
|
-
const connectionType = em
|
|
1626
|
+
const connectionType = em
|
|
1627
|
+
? preferredConnectionType
|
|
1628
|
+
: this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
1538
1629
|
const qb = new QueryBuilder(entityName, this.metadata, this, ctx, alias, connectionType, em, loggerContext);
|
|
1539
1630
|
if (!convertCustomTypes) {
|
|
1540
1631
|
qb.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
@@ -1681,7 +1772,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1681
1772
|
let path = parentPath;
|
|
1682
1773
|
const meta2 = prop.targetMeta;
|
|
1683
1774
|
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
1684
|
-
(![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
1775
|
+
(![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
1776
|
+
!prop.owner ||
|
|
1777
|
+
Utils.isPlainObject(childOrder))) {
|
|
1685
1778
|
path += `.${field}`;
|
|
1686
1779
|
}
|
|
1687
1780
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
|
|
@@ -1692,7 +1785,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1692
1785
|
if (!join) {
|
|
1693
1786
|
continue;
|
|
1694
1787
|
}
|
|
1695
|
-
if (join &&
|
|
1788
|
+
if (join &&
|
|
1789
|
+
![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) &&
|
|
1790
|
+
typeof childOrder === 'object') {
|
|
1696
1791
|
const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
|
|
1697
1792
|
orderBy.push(...children);
|
|
1698
1793
|
continue;
|
|
@@ -1823,7 +1918,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1823
1918
|
if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
|
|
1824
1919
|
ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
|
|
1825
1920
|
}
|
|
1826
|
-
if (meta.root.inheritanceType === 'sti' &&
|
|
1921
|
+
if (meta.root.inheritanceType === 'sti' &&
|
|
1922
|
+
!options.fields.includes(`${qb.alias}.${meta.root.discriminatorColumn}`)) {
|
|
1827
1923
|
ret.push(meta.root.discriminatorColumn);
|
|
1828
1924
|
}
|
|
1829
1925
|
}
|
package/AbstractSqlPlatform.js
CHANGED
|
@@ -64,15 +64,14 @@ export class AbstractSqlPlatform extends Platform {
|
|
|
64
64
|
}
|
|
65
65
|
getSearchJsonPropertyKey(path, type, aliased, value) {
|
|
66
66
|
const [a, ...b] = path;
|
|
67
|
-
const quoteKey = (key) => key.match(/^[a-z]\w*$/i) ? key : `"${key}"
|
|
67
|
+
const quoteKey = (key) => (key.match(/^[a-z]\w*$/i) ? key : `"${key}"`);
|
|
68
68
|
if (aliased) {
|
|
69
69
|
return raw(alias => `json_extract(${this.quoteIdentifier(`${alias}.${a}`)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
70
70
|
}
|
|
71
71
|
return raw(`json_extract(${this.quoteIdentifier(a)}, '$.${b.map(quoteKey).join('.')}')`);
|
|
72
72
|
}
|
|
73
73
|
getJsonIndexDefinition(index) {
|
|
74
|
-
return index.columnNames
|
|
75
|
-
.map(column => {
|
|
74
|
+
return index.columnNames.map(column => {
|
|
76
75
|
if (!column.includes('.')) {
|
|
77
76
|
return column;
|
|
78
77
|
}
|
|
@@ -12,7 +12,7 @@ class InsertStatement {
|
|
|
12
12
|
}
|
|
13
13
|
getData() {
|
|
14
14
|
const data = {};
|
|
15
|
-
this.keys.forEach((key, idx) => data[key] = this.data[idx]);
|
|
15
|
+
this.keys.forEach((key, idx) => (data[key] = this.data[idx]));
|
|
16
16
|
return data;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -28,7 +28,7 @@ class DeleteStatement {
|
|
|
28
28
|
}
|
|
29
29
|
getCondition() {
|
|
30
30
|
const cond = {};
|
|
31
|
-
this.keys.forEach((key, idx) => cond[key] = this.cond[idx]);
|
|
31
|
+
this.keys.forEach((key, idx) => (cond[key] = this.cond[idx]));
|
|
32
32
|
return cond;
|
|
33
33
|
}
|
|
34
34
|
}
|
package/SqlEntityManager.d.ts
CHANGED
|
@@ -29,6 +29,6 @@ export declare class SqlEntityManager<Driver extends AbstractSqlDriver = Abstrac
|
|
|
29
29
|
getRepository<T extends object, U extends EntityRepository<T> = SqlEntityRepository<T>>(entityName: EntityName<T>): GetRepository<T, U>;
|
|
30
30
|
protected applyDiscriminatorCondition<Entity extends object>(entityName: EntityName<Entity>, where: FilterQuery<Entity>): FilterQuery<Entity>;
|
|
31
31
|
}
|
|
32
|
-
type EntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ?
|
|
32
|
+
type EntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? Extract<NonNullable<TEntityManager['~entities']>[number], EntitySchemaWithMeta> : never;
|
|
33
33
|
type AllEntitiesFromManager<TEntityManager extends EntityManager<any>> = NonNullable<TEntityManager['~entities']> extends any[] ? NonNullable<TEntityManager['~entities']>[number] : never;
|
|
34
34
|
export {};
|
package/SqlEntityManager.js
CHANGED
|
@@ -22,11 +22,11 @@ export class SqlEntityManager extends EntityManager {
|
|
|
22
22
|
*/
|
|
23
23
|
getKysely(options = {}) {
|
|
24
24
|
let kysely = this.getConnection(options.type).getClient();
|
|
25
|
-
if (options.columnNamingStrategy != null
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
if (options.columnNamingStrategy != null ||
|
|
26
|
+
options.tableNamingStrategy != null ||
|
|
27
|
+
options.processOnCreateHooks != null ||
|
|
28
|
+
options.processOnUpdateHooks != null ||
|
|
29
|
+
options.convertValues != null) {
|
|
30
30
|
kysely = kysely.withPlugin(new MikroKyselyPlugin(this, options));
|
|
31
31
|
}
|
|
32
32
|
return kysely;
|
|
@@ -78,9 +78,7 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
|
|
|
78
78
|
return { prefix: '', suffix: '' };
|
|
79
79
|
}
|
|
80
80
|
const returningFields = this.options.returning;
|
|
81
|
-
const selections = returningFields
|
|
82
|
-
.map(field => `[t].${this.platform.quoteIdentifier(field)}`)
|
|
83
|
-
.join(',');
|
|
81
|
+
const selections = returningFields.map(field => `[t].${this.platform.quoteIdentifier(field)}`).join(',');
|
|
84
82
|
return {
|
|
85
83
|
prefix: `select top(0) ${selections} into #out from ${this.getTableName()} as t left join ${this.getTableName()} on 0 = 1;`,
|
|
86
84
|
suffix: `select ${selections} from #out as t; drop table #out`,
|
|
@@ -182,7 +180,8 @@ export class MsSqlNativeQueryBuilder extends NativeQueryBuilder {
|
|
|
182
180
|
}
|
|
183
181
|
}
|
|
184
182
|
addLockClause() {
|
|
185
|
-
if (!this.options.lockMode ||
|
|
183
|
+
if (!this.options.lockMode ||
|
|
184
|
+
![LockMode.PESSIMISTIC_READ, LockMode.PESSIMISTIC_WRITE].includes(this.options.lockMode)) {
|
|
186
185
|
return;
|
|
187
186
|
}
|
|
188
187
|
const map = {
|
|
@@ -44,8 +44,7 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
44
44
|
return JSON.stringify(value);
|
|
45
45
|
}
|
|
46
46
|
getJsonIndexDefinition(index) {
|
|
47
|
-
return index.columnNames
|
|
48
|
-
.map(column => {
|
|
47
|
+
return index.columnNames.map(column => {
|
|
49
48
|
if (!column.includes('.')) {
|
|
50
49
|
return column;
|
|
51
50
|
}
|
|
@@ -89,11 +89,13 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
89
89
|
};
|
|
90
90
|
// Capture column options (prefix length, sort order)
|
|
91
91
|
if (index.sub_part != null || index.sort_order === 'D') {
|
|
92
|
-
indexDef.columns = [
|
|
92
|
+
indexDef.columns = [
|
|
93
|
+
{
|
|
93
94
|
name: index.column_name,
|
|
94
95
|
...(index.sub_part != null && { length: index.sub_part }),
|
|
95
96
|
...(index.sort_order === 'D' && { sort: 'DESC' }),
|
|
96
|
-
}
|
|
97
|
+
},
|
|
98
|
+
];
|
|
97
99
|
}
|
|
98
100
|
// Capture index type for fulltext and spatial indexes
|
|
99
101
|
if (index.index_type === 'FULLTEXT') {
|
|
@@ -149,7 +151,8 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
149
151
|
*/
|
|
150
152
|
getIndexColumns(index) {
|
|
151
153
|
if (index.columns?.length) {
|
|
152
|
-
return index.columns
|
|
154
|
+
return index.columns
|
|
155
|
+
.map(col => {
|
|
153
156
|
const quotedName = this.quote(col.name);
|
|
154
157
|
// MySQL supports collation via expression: (column_name COLLATE collation_name)
|
|
155
158
|
// When collation is specified, wrap in parentheses as an expression
|
|
@@ -173,7 +176,8 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
173
176
|
colDef += ` ${col.sort}`;
|
|
174
177
|
}
|
|
175
178
|
return colDef;
|
|
176
|
-
})
|
|
179
|
+
})
|
|
180
|
+
.join(', ');
|
|
177
181
|
}
|
|
178
182
|
return index.columnNames.map(c => this.quote(c)).join(', ');
|
|
179
183
|
}
|
|
@@ -205,20 +209,24 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
205
209
|
from information_schema.columns where table_schema = database() and table_name in (${tables.map(t => this.platform.quoteValue(t.table_name))})
|
|
206
210
|
order by ordinal_position`;
|
|
207
211
|
const allColumns = await connection.execute(sql);
|
|
208
|
-
const str = (val) => val != null ? '' + val : val;
|
|
212
|
+
const str = (val) => (val != null ? '' + val : val);
|
|
209
213
|
const extra = (val) => val.replace(/auto_increment|default_generated|(stored|virtual) generated/i, '').trim() || undefined;
|
|
210
214
|
const ret = {};
|
|
211
215
|
for (const col of allColumns) {
|
|
212
216
|
const mappedType = this.platform.getMappedType(col.column_type);
|
|
213
|
-
const defaultValue = str(this.normalizeDefaultValue(
|
|
217
|
+
const defaultValue = str(this.normalizeDefaultValue(mappedType.compareAsType() === 'boolean' && ['0', '1'].includes(col.column_default)
|
|
214
218
|
? ['false', 'true'][+col.column_default]
|
|
215
219
|
: col.column_default, col.length));
|
|
216
220
|
const key = this.getTableKey(col);
|
|
217
|
-
const generated = col.generation_expression
|
|
221
|
+
const generated = col.generation_expression
|
|
222
|
+
? `(${col.generation_expression.replaceAll(`\\'`, `'`)}) ${col.extra.match(/stored generated/i) ? 'stored' : 'virtual'}`
|
|
223
|
+
: undefined;
|
|
218
224
|
ret[key] ??= [];
|
|
219
225
|
ret[key].push({
|
|
220
226
|
name: col.column_name,
|
|
221
|
-
type: this.platform.isNumericColumn(mappedType)
|
|
227
|
+
type: this.platform.isNumericColumn(mappedType)
|
|
228
|
+
? col.column_type.replace(/ unsigned$/, '').replace(/\(\d+\)$/, '')
|
|
229
|
+
: col.column_type,
|
|
222
230
|
mappedType,
|
|
223
231
|
unsigned: col.column_type.endsWith(' unsigned'),
|
|
224
232
|
length: col.length,
|
|
@@ -328,7 +336,10 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
328
336
|
const enums = await connection.execute(sql);
|
|
329
337
|
return enums.reduce((o, item) => {
|
|
330
338
|
o[item.table_name] ??= {};
|
|
331
|
-
o[item.table_name][item.column_name] = item.column_type
|
|
339
|
+
o[item.table_name][item.column_name] = item.column_type
|
|
340
|
+
.match(/enum\((.*)\)/)[1]
|
|
341
|
+
.split(',')
|
|
342
|
+
.map((item) => item.match(/'(.*)'/)[1]);
|
|
332
343
|
return o;
|
|
333
344
|
}, {});
|
|
334
345
|
}
|
|
@@ -338,7 +349,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
338
349
|
}
|
|
339
350
|
const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
|
|
340
351
|
const res = await connection.execute(sql);
|
|
341
|
-
return this._cache.supportsCheckConstraints = res.length > 0;
|
|
352
|
+
return (this._cache.supportsCheckConstraints = res.length > 0);
|
|
342
353
|
}
|
|
343
354
|
getChecksSQL(tables) {
|
|
344
355
|
return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
|