@mikro-orm/sql 7.0.17 → 7.0.18-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +1 -1
- package/AbstractSqlConnection.js +27 -6
- package/AbstractSqlDriver.d.ts +25 -1
- package/AbstractSqlDriver.js +356 -20
- package/AbstractSqlPlatform.d.ts +13 -2
- package/AbstractSqlPlatform.js +16 -3
- package/PivotCollectionPersister.d.ts +2 -2
- package/PivotCollectionPersister.js +19 -3
- package/README.md +2 -1
- package/SqlEntityManager.d.ts +46 -3
- package/SqlEntityManager.js +77 -7
- package/SqlMikroORM.d.ts +4 -4
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +4 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +1 -0
- package/dialects/mysql/BaseMySqlPlatform.js +3 -0
- package/dialects/mysql/MySqlNativeQueryBuilder.js +11 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +19 -3
- package/dialects/mysql/MySqlSchemaHelper.js +254 -21
- package/dialects/oracledb/OracleDialect.d.ts +1 -1
- package/dialects/oracledb/OracleDialect.js +2 -1
- package/dialects/postgresql/BasePostgreSqlEntityManager.d.ts +19 -0
- package/dialects/postgresql/BasePostgreSqlEntityManager.js +24 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +8 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +50 -0
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +38 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +341 -6
- package/dialects/postgresql/index.d.ts +2 -0
- package/dialects/postgresql/index.js +2 -0
- package/dialects/postgresql/typeOverrides.d.ts +14 -0
- package/dialects/postgresql/typeOverrides.js +12 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +7 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +131 -2
- package/package.json +2 -2
- package/query/NativeQueryBuilder.d.ts +6 -0
- package/query/NativeQueryBuilder.js +16 -1
- package/query/QueryBuilder.d.ts +83 -1
- package/query/QueryBuilder.js +181 -8
- package/schema/DatabaseSchema.d.ts +29 -2
- package/schema/DatabaseSchema.js +137 -0
- package/schema/DatabaseTable.d.ts +20 -1
- package/schema/DatabaseTable.js +62 -3
- package/schema/SchemaComparator.d.ts +19 -0
- package/schema/SchemaComparator.js +250 -1
- package/schema/SchemaHelper.d.ts +77 -1
- package/schema/SchemaHelper.js +279 -5
- package/schema/SqlSchemaGenerator.d.ts +2 -2
- package/schema/SqlSchemaGenerator.js +47 -10
- package/schema/partitioning.d.ts +13 -0
- package/schema/partitioning.js +326 -0
- package/typings.d.ts +69 -2
package/AbstractSqlDriver.js
CHANGED
|
@@ -3,6 +3,28 @@ import { QueryBuilder } from './query/QueryBuilder.js';
|
|
|
3
3
|
import { JoinType, QueryType } from './query/enums.js';
|
|
4
4
|
import { SqlEntityManager } from './SqlEntityManager.js';
|
|
5
5
|
import { PivotCollectionPersister } from './PivotCollectionPersister.js';
|
|
6
|
+
/** Extracts cancellation controls from any options bag that extends `AbortQueryOptions`. */
|
|
7
|
+
function pickAbortOptions(options) {
|
|
8
|
+
if (!options || (options.signal == null && options.inflightQueryAbortStrategy == null)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
signal: options.signal,
|
|
13
|
+
inflightQueryAbortStrategy: options.inflightQueryAbortStrategy,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Returns a `loggerContext` payload that carries the abort fields alongside any existing
|
|
18
|
+
* context. The connection layer strips them before logging — this avoids widening the public
|
|
19
|
+
* `Connection.execute()` signature.
|
|
20
|
+
*/
|
|
21
|
+
function withAbortContext(loggerContext, options) {
|
|
22
|
+
const abort = pickAbortOptions(options);
|
|
23
|
+
if (!abort) {
|
|
24
|
+
return loggerContext;
|
|
25
|
+
}
|
|
26
|
+
return { ...loggerContext, ...abort };
|
|
27
|
+
}
|
|
6
28
|
/** Base class for SQL database drivers, implementing find/insert/update/delete using QueryBuilder. */
|
|
7
29
|
export class AbstractSqlDriver extends DatabaseDriver {
|
|
8
30
|
[EntityManagerType];
|
|
@@ -34,6 +56,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
34
56
|
return { alias, name: meta.tableName, schema: effectiveSchema, qualifiedName, toString: () => alias };
|
|
35
57
|
}
|
|
36
58
|
validateSqlOptions(options) {
|
|
59
|
+
if (options.using && !options.indexHint) {
|
|
60
|
+
const names = Utils.asArray(options.using);
|
|
61
|
+
const hint = this.platform.formatIndexHint(names);
|
|
62
|
+
if (hint) {
|
|
63
|
+
options.indexHint = hint;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
37
66
|
if (options.collation != null && typeof options.collation !== 'string') {
|
|
38
67
|
throw new Error('Collation option for SQL drivers must be a string (collation name). Use a CollationOptions object only with MongoDB.');
|
|
39
68
|
}
|
|
@@ -53,6 +82,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
53
82
|
const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging, undefined, options.em)
|
|
54
83
|
.withSchema(schema)
|
|
55
84
|
.cache(false);
|
|
85
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
56
86
|
const fields = this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
|
|
57
87
|
const orderBy = this.buildOrderBy(qb, meta, populate, options);
|
|
58
88
|
const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
|
|
@@ -90,6 +120,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
90
120
|
if (options.em) {
|
|
91
121
|
await qb.applyJoinedFilters(options.em, options.filters);
|
|
92
122
|
}
|
|
123
|
+
if (options._partitionLimit) {
|
|
124
|
+
qb.setPartitionLimit(options._partitionLimit);
|
|
125
|
+
}
|
|
93
126
|
return qb;
|
|
94
127
|
}
|
|
95
128
|
async find(entityName, where, options = {}) {
|
|
@@ -206,7 +239,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
206
239
|
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
207
240
|
native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
|
|
208
241
|
const query = native.compile();
|
|
209
|
-
const res = await this.execute(query.sql, query.params, 'all', options.ctx);
|
|
242
|
+
const res = await this.execute(query.sql, query.params, 'all', options.ctx, withAbortContext(options.loggerContext, options));
|
|
210
243
|
if (type === QueryType.COUNT) {
|
|
211
244
|
return res[0].count;
|
|
212
245
|
}
|
|
@@ -223,7 +256,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
223
256
|
native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
|
|
224
257
|
const query = native.compile();
|
|
225
258
|
const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
|
|
226
|
-
const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
|
|
259
|
+
const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, withAbortContext(options.loggerContext, options), options.chunkSize);
|
|
227
260
|
for await (const row of res) {
|
|
228
261
|
yield this.mapResult(row, meta);
|
|
229
262
|
}
|
|
@@ -531,6 +564,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
531
564
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
532
565
|
const schema = this.getSchemaName(meta, options);
|
|
533
566
|
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
|
|
567
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
534
568
|
const populateWhere = this.buildPopulateWhere(meta, joinedProps, options);
|
|
535
569
|
if (meta && !Utils.isEmpty(populate)) {
|
|
536
570
|
this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
|
|
@@ -556,6 +590,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
556
590
|
const meta = this.metadata.get(entityName);
|
|
557
591
|
const collections = this.extractManyToMany(meta, data);
|
|
558
592
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
593
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
559
594
|
const res = await this.rethrow(qb.insert(data).execute('run', false));
|
|
560
595
|
res.row = res.row || {};
|
|
561
596
|
let pk;
|
|
@@ -576,6 +611,115 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
576
611
|
await this.processManyToMany(meta, pk, collections, false, options);
|
|
577
612
|
return res;
|
|
578
613
|
}
|
|
614
|
+
async nativeClone(entityName, where, overrides, options = {}) {
|
|
615
|
+
options.convertCustomTypes ??= true;
|
|
616
|
+
const meta = this.metadata.get(entityName);
|
|
617
|
+
if (meta.inheritanceType === 'tpt' || meta.tptParent) {
|
|
618
|
+
return this.nativeCloneTPT(meta, where, overrides, options);
|
|
619
|
+
}
|
|
620
|
+
return this.nativeCloneSimple(meta, where, overrides, options);
|
|
621
|
+
}
|
|
622
|
+
async nativeCloneSimple(meta, where, overrides, options = {}) {
|
|
623
|
+
const props = this.getCloneableProps(meta);
|
|
624
|
+
const mappedOverrides = this.mapCloneOverrides(overrides, meta, options);
|
|
625
|
+
const { selectFields, insertColumns } = this.buildCloneFields(props, mappedOverrides, meta);
|
|
626
|
+
const abort = pickAbortOptions(options);
|
|
627
|
+
const selectQb = this.createQueryBuilder(meta.class, options.ctx, 'read', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
628
|
+
selectQb.select(selectFields).where(where);
|
|
629
|
+
selectQb.setAbortOptions(abort);
|
|
630
|
+
const insertQb = this.createQueryBuilder(meta.class, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
631
|
+
insertQb.setAbortOptions(abort);
|
|
632
|
+
return this.rethrow(insertQb
|
|
633
|
+
.insertFrom(selectQb, { columns: insertColumns })
|
|
634
|
+
.execute('run', false));
|
|
635
|
+
}
|
|
636
|
+
async nativeCloneTPT(leafMeta, where, overrides, options = {}) {
|
|
637
|
+
const hierarchy = [];
|
|
638
|
+
let current = leafMeta;
|
|
639
|
+
while (current) {
|
|
640
|
+
hierarchy.unshift(current);
|
|
641
|
+
current = current.tptParent;
|
|
642
|
+
}
|
|
643
|
+
const rootMeta = hierarchy[0];
|
|
644
|
+
let newPk;
|
|
645
|
+
let rootResult;
|
|
646
|
+
for (const tableMeta of hierarchy) {
|
|
647
|
+
const props = this.getCloneableProps(tableMeta, true);
|
|
648
|
+
const mappedOverrides = this.mapCloneOverrides(overrides, tableMeta, options);
|
|
649
|
+
const { selectFields, insertColumns } = this.buildCloneFields(props, mappedOverrides, tableMeta);
|
|
650
|
+
// For child tables, prepend the new PK value
|
|
651
|
+
if (tableMeta !== rootMeta && newPk != null) {
|
|
652
|
+
for (const pkName of tableMeta.primaryKeys) {
|
|
653
|
+
const prop = tableMeta.properties[pkName];
|
|
654
|
+
for (const fieldName of prop.fieldNames) {
|
|
655
|
+
insertColumns.unshift(fieldName);
|
|
656
|
+
selectFields.unshift(raw('? as ??', [newPk, fieldName]));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const sourceWhere = tableMeta === rootMeta ? where : (Utils.extractPK(where, tableMeta) ?? where);
|
|
661
|
+
const abort = pickAbortOptions(options);
|
|
662
|
+
const selectQb = this.createQueryBuilder(tableMeta.class, options.ctx, 'read', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(tableMeta, options));
|
|
663
|
+
selectQb.select(selectFields).where(sourceWhere);
|
|
664
|
+
selectQb.setAbortOptions(abort);
|
|
665
|
+
const insertQb = this.createQueryBuilder(tableMeta.class, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(tableMeta, options));
|
|
666
|
+
insertQb.setAbortOptions(abort);
|
|
667
|
+
const res = await this.rethrow(insertQb
|
|
668
|
+
.insertFrom(selectQb, { columns: insertColumns })
|
|
669
|
+
.execute('run', false));
|
|
670
|
+
if (tableMeta === rootMeta) {
|
|
671
|
+
rootResult = res;
|
|
672
|
+
newPk = res.insertId ?? res.row?.[rootMeta.primaryKeys[0]];
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return rootResult;
|
|
676
|
+
}
|
|
677
|
+
mapCloneOverrides(overrides, meta, options) {
|
|
678
|
+
if (!overrides) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
681
|
+
return super.mapDataToFieldNames(overrides, true, meta.properties, options.convertCustomTypes);
|
|
682
|
+
}
|
|
683
|
+
buildCloneFields(props, mappedOverrides, meta) {
|
|
684
|
+
const selectFields = [];
|
|
685
|
+
const insertColumns = [];
|
|
686
|
+
for (const prop of props) {
|
|
687
|
+
for (const fieldName of prop.fieldNames) {
|
|
688
|
+
insertColumns.push(fieldName);
|
|
689
|
+
if (mappedOverrides && fieldName in mappedOverrides) {
|
|
690
|
+
selectFields.push(raw('? as ??', [mappedOverrides[fieldName], fieldName]));
|
|
691
|
+
}
|
|
692
|
+
else if (meta.versionProperty === prop.name) {
|
|
693
|
+
const initial = prop.runtimeType === 'Date' ? new Date() : 1;
|
|
694
|
+
selectFields.push(raw('? as ??', [initial, fieldName]));
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
selectFields.push(fieldName);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return { selectFields, insertColumns };
|
|
702
|
+
}
|
|
703
|
+
getCloneableProps(meta, ownProps) {
|
|
704
|
+
return (ownProps ? (meta.ownProps ?? meta.props) : meta.props).filter(prop => {
|
|
705
|
+
if (prop.persist === false) {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
if (prop.primary) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
if (!prop.fieldNames?.length) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.object) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
return true;
|
|
721
|
+
});
|
|
722
|
+
}
|
|
579
723
|
async nativeInsertMany(entityName, data, options = {}, transform) {
|
|
580
724
|
options.processCollections ??= true;
|
|
581
725
|
options.convertCustomTypes ??= true;
|
|
@@ -727,7 +871,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
727
871
|
if (transform) {
|
|
728
872
|
sql = transform(sql);
|
|
729
873
|
}
|
|
730
|
-
const res = await this.execute(sql, params, 'run', options.ctx, options.loggerContext);
|
|
874
|
+
const res = await this.execute(sql, params, 'run', options.ctx, withAbortContext(options.loggerContext, options));
|
|
731
875
|
let pk;
|
|
732
876
|
/* v8 ignore next */
|
|
733
877
|
if (pks.length > 1) {
|
|
@@ -760,6 +904,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
760
904
|
}
|
|
761
905
|
if (Utils.hasObjectKeys(data)) {
|
|
762
906
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
907
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
763
908
|
if (options.upsert) {
|
|
764
909
|
/* v8 ignore next */
|
|
765
910
|
const uniqueFields = options.onConflictFields ??
|
|
@@ -805,6 +950,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
805
950
|
? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key))
|
|
806
951
|
: meta.primaryKeys);
|
|
807
952
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
953
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
808
954
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
809
955
|
qb.insert(data)
|
|
810
956
|
.onConflict(uniqueFields)
|
|
@@ -950,7 +1096,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
950
1096
|
if (transform) {
|
|
951
1097
|
sql = transform(sql, params);
|
|
952
1098
|
}
|
|
953
|
-
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
|
|
1099
|
+
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, withAbortContext(options.loggerContext, options)));
|
|
954
1100
|
for (let i = 0; i < collections.length; i++) {
|
|
955
1101
|
await this.processManyToMany(meta, where[i], collections[i], false, options);
|
|
956
1102
|
}
|
|
@@ -968,6 +1114,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
968
1114
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext)
|
|
969
1115
|
.delete(where)
|
|
970
1116
|
.withSchema(this.getSchemaName(meta, options));
|
|
1117
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
971
1118
|
return this.rethrow(qb.execute('run', false));
|
|
972
1119
|
}
|
|
973
1120
|
/**
|
|
@@ -998,8 +1145,29 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
998
1145
|
const pks = wrapped.getPrimaryKeys(true);
|
|
999
1146
|
const snap = coll.getSnapshot();
|
|
1000
1147
|
const includes = (arr, item) => !!arr.find(i => this.comparePrimaryKeyArrays(i, item));
|
|
1001
|
-
|
|
1002
|
-
|
|
1148
|
+
// For union-target polymorphic M:N, prepend the per-row discriminator value so the pivot
|
|
1149
|
+
// persister can write it alongside the FK id. Memoized per sync-run because a collection can
|
|
1150
|
+
// hold hundreds of items of the same few types, and findDiscriminatorValue walks the prototype
|
|
1151
|
+
// chain + re-scans Object.entries each call.
|
|
1152
|
+
const isUnionTargetMN = QueryHelper.isUnionTargetPolymorphic(coll.property);
|
|
1153
|
+
const classToDisc = new Map();
|
|
1154
|
+
const toDiff = (item) => {
|
|
1155
|
+
const keys = helper(item).getPrimaryKeys(true);
|
|
1156
|
+
if (!isUnionTargetMN) {
|
|
1157
|
+
return keys;
|
|
1158
|
+
}
|
|
1159
|
+
let disc = classToDisc.get(item.constructor);
|
|
1160
|
+
if (!classToDisc.has(item.constructor)) {
|
|
1161
|
+
disc = QueryHelper.findDiscriminatorValue(coll.property.discriminatorMap, item.constructor);
|
|
1162
|
+
if (disc === undefined) {
|
|
1163
|
+
throw new Error(`Cannot resolve discriminator value for ${item.constructor.name} in ${coll.property.name}; the class is not part of the union target list.`);
|
|
1164
|
+
}
|
|
1165
|
+
classToDisc.set(item.constructor, disc);
|
|
1166
|
+
}
|
|
1167
|
+
return [disc, ...keys];
|
|
1168
|
+
};
|
|
1169
|
+
const snapshot = snap ? snap.map(toDiff) : [];
|
|
1170
|
+
const current = coll.getItems(false).map(toDiff);
|
|
1003
1171
|
const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
|
|
1004
1172
|
const insertDiff = current.filter(item => !includes(snapshot, item));
|
|
1005
1173
|
const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
|
|
@@ -1017,6 +1185,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1017
1185
|
if (coll.property.kind === ReferenceKind.ONE_TO_MANY) {
|
|
1018
1186
|
const cols = coll.property.referencedColumnNames;
|
|
1019
1187
|
const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
|
|
1188
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
1020
1189
|
if (coll.getSnapshot() === undefined) {
|
|
1021
1190
|
if (coll.property.orphanRemoval) {
|
|
1022
1191
|
const query = qb
|
|
@@ -1058,7 +1227,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1058
1227
|
schema = this.config.get('schema');
|
|
1059
1228
|
}
|
|
1060
1229
|
const tableName = `${schema ?? '_'}.${pivotMeta.tableName}`;
|
|
1061
|
-
const persister = (groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext));
|
|
1230
|
+
const persister = (groups[tableName] ??= new PivotCollectionPersister(pivotMeta, this, options?.ctx, schema, options?.loggerContext, pickAbortOptions(options)));
|
|
1062
1231
|
persister.enqueueUpdate(coll.property, insertDiff, deleteDiff, pks, coll.isInitialized());
|
|
1063
1232
|
}
|
|
1064
1233
|
for (const persister of Utils.values(groups)) {
|
|
@@ -1071,21 +1240,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1071
1240
|
return {};
|
|
1072
1241
|
}
|
|
1073
1242
|
const pivotMeta = this.metadata.get(prop.pivotEntity);
|
|
1243
|
+
if (prop.discriminatorColumn && QueryHelper.isUnionTargetPolymorphic(prop)) {
|
|
1244
|
+
return this.loadFromUnionTargetPolymorphicPivotTable(prop, owners, where, orderBy, ctx, options, pivotJoin);
|
|
1245
|
+
}
|
|
1074
1246
|
if (prop.polymorphic && prop.discriminatorColumn && prop.discriminatorValue) {
|
|
1075
1247
|
return this.loadFromPolymorphicPivotTable(prop, owners, where, orderBy, ctx, options, pivotJoin);
|
|
1076
1248
|
}
|
|
1077
1249
|
const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
|
|
1078
1250
|
const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
|
|
1079
1251
|
const ownerMeta = pivotProp2.targetMeta;
|
|
1080
|
-
|
|
1081
|
-
// convert owner PKs to DB format for the query and convert result FKs back to
|
|
1082
|
-
// JS format for consistent key hashing in buildPivotResultMap.
|
|
1083
|
-
const pkProp = ownerMeta.properties[ownerMeta.primaryKeys[0]];
|
|
1084
|
-
const needsConversion = pkProp?.customType?.ensureComparable(ownerMeta, pkProp) && !ownerMeta.compositePK;
|
|
1085
|
-
let ownerPks = ownerMeta.compositePK ? owners : owners.map(o => o[0]);
|
|
1086
|
-
if (needsConversion) {
|
|
1087
|
-
ownerPks = ownerPks.map(v => pkProp.customType.convertToDatabaseValue(v, this.platform, { mode: 'query' }));
|
|
1088
|
-
}
|
|
1252
|
+
const { ownerPks, needsConversion, pkProp } = this.convertOwnerPksForPivotQuery(owners, ownerMeta);
|
|
1089
1253
|
const cond = {
|
|
1090
1254
|
[pivotProp2.name]: { $in: ownerPks },
|
|
1091
1255
|
};
|
|
@@ -1100,7 +1264,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1100
1264
|
const fields = pivotJoin
|
|
1101
1265
|
? [pivotProp1.name, pivotProp2.name]
|
|
1102
1266
|
: [pivotProp1.name, pivotProp2.name, ...childFields];
|
|
1103
|
-
const
|
|
1267
|
+
const pivotFindOptions = {
|
|
1104
1268
|
ctx,
|
|
1105
1269
|
...options,
|
|
1106
1270
|
fields,
|
|
@@ -1116,10 +1280,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1116
1280
|
},
|
|
1117
1281
|
],
|
|
1118
1282
|
populateWhere: undefined,
|
|
1119
|
-
// @ts-ignore
|
|
1120
1283
|
_populateWhere: 'infer',
|
|
1121
1284
|
populateFilter: this.wrapPopulateFilter(options, pivotProp2.name),
|
|
1122
|
-
}
|
|
1285
|
+
};
|
|
1286
|
+
if (pivotFindOptions._partitionLimit) {
|
|
1287
|
+
pivotFindOptions._partitionLimit.partitionBy = pivotProp2.name;
|
|
1288
|
+
}
|
|
1289
|
+
const res = await this.find(pivotMeta.class, where, pivotFindOptions);
|
|
1123
1290
|
// Convert result FK values back to JS format so key hashing
|
|
1124
1291
|
// in buildPivotResultMap is consistent with the owner keys.
|
|
1125
1292
|
if (needsConversion) {
|
|
@@ -1239,6 +1406,102 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1239
1406
|
});
|
|
1240
1407
|
return this.buildPivotResultMap(owners, res, tagProp.name, ownerRelationName);
|
|
1241
1408
|
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Load a union-target polymorphic M:N pivot (e.g. Post.attachments -> Image | Video).
|
|
1411
|
+
* Each pivot row's discriminator column selects which target table to hydrate.
|
|
1412
|
+
*/
|
|
1413
|
+
async loadFromUnionTargetPolymorphicPivotTable(prop, owners, where = {}, orderBy, ctx, options = {}, pivotJoin) {
|
|
1414
|
+
// :ref hints cannot be honored for union-target — EntityLoader.getReference needs a concrete
|
|
1415
|
+
// class per item, but the ref-mode map only carries flat PK values and would hydrate every
|
|
1416
|
+
// row as the first polymorph target. Fail loudly instead of silently corrupting the collection.
|
|
1417
|
+
if (pivotJoin) {
|
|
1418
|
+
throw new Error(`The ':ref' populate hint is not supported for union-target polymorphic M:N on ${prop.name}. Use a regular populate hint instead.`);
|
|
1419
|
+
}
|
|
1420
|
+
const pivotMeta = this.metadata.get(prop.pivotEntity);
|
|
1421
|
+
const targets = prop.polymorphTargets;
|
|
1422
|
+
const ownerProp = pivotMeta.relations.find(r => r.persist !== false && !r.polymorphic);
|
|
1423
|
+
const discriminatorColumn = prop.discriminatorColumn;
|
|
1424
|
+
const ownerMeta = ownerProp.targetMeta;
|
|
1425
|
+
const { ownerPks, needsConversion, pkProp } = this.convertOwnerPksForPivotQuery(owners, ownerMeta);
|
|
1426
|
+
const pivotRows = (await this.find(pivotMeta.class, { [ownerProp.name]: { $in: ownerPks } }, {
|
|
1427
|
+
ctx,
|
|
1428
|
+
orderBy: this.getPivotOrderBy(prop, ownerProp, orderBy, options?.orderBy),
|
|
1429
|
+
fields: [ownerProp.name, discriminatorColumn, prop.discriminator],
|
|
1430
|
+
populateWhere: undefined,
|
|
1431
|
+
// @ts-ignore
|
|
1432
|
+
_populateWhere: 'infer',
|
|
1433
|
+
}));
|
|
1434
|
+
/* v8 ignore next 7 - custom-type PK conversion, tested via loadFromPivotTable path */
|
|
1435
|
+
if (needsConversion) {
|
|
1436
|
+
for (const item of pivotRows) {
|
|
1437
|
+
const fk = item[ownerProp.name];
|
|
1438
|
+
if (fk != null) {
|
|
1439
|
+
item[ownerProp.name] = pkProp.customType.convertToJSValue(fk, this.platform);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
const classMeta = new Map(targets.map(t => [t.class, t]));
|
|
1444
|
+
const rowsByTarget = new Map();
|
|
1445
|
+
for (const row of pivotRows) {
|
|
1446
|
+
const discValue = row[discriminatorColumn];
|
|
1447
|
+
const targetClass = prop.discriminatorMap[discValue];
|
|
1448
|
+
const targetMeta = classMeta.get(targetClass);
|
|
1449
|
+
/* v8 ignore next 3 - defensive: unknown discriminator value */
|
|
1450
|
+
if (!targetMeta) {
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
const list = rowsByTarget.get(targetMeta) ?? [];
|
|
1454
|
+
list.push(row);
|
|
1455
|
+
rowsByTarget.set(targetMeta, list);
|
|
1456
|
+
}
|
|
1457
|
+
// Strip the outer find's orderBy/fields/exclude before bulk-loading targets by PK — those apply
|
|
1458
|
+
// to the owner query, not each polymorph target (Image and Video wouldn't share an orderBy field).
|
|
1459
|
+
// populateFilter is a filter on the populated collection; since union-target splits the pivot
|
|
1460
|
+
// and target queries, we merge it into the target-level `where` instead of wrapping it on the
|
|
1461
|
+
// pivot query (where joins to target tables aren't available).
|
|
1462
|
+
// Hoisted above the loop since `options` doesn't change per target.
|
|
1463
|
+
const { orderBy: _o, fields: _f, exclude: _e, populateFilter, ...childOptions } = options;
|
|
1464
|
+
const populate = options.populate ?? [];
|
|
1465
|
+
const orphanedRows = new Set();
|
|
1466
|
+
for (const [targetMeta, rows] of rowsByTarget) {
|
|
1467
|
+
const targetIds = rows.map(r => r[prop.discriminator]);
|
|
1468
|
+
// Union-target pivot stores one scalar FK per row; composite-PK targets are rejected at
|
|
1469
|
+
// metadata validation time, so a single primary key column is guaranteed here.
|
|
1470
|
+
const pkCol = targetMeta.primaryKeys[0];
|
|
1471
|
+
let cond = { [pkCol]: { $in: targetIds } };
|
|
1472
|
+
if (!Utils.isEmpty(where)) {
|
|
1473
|
+
cond = { $and: [cond, where] };
|
|
1474
|
+
}
|
|
1475
|
+
if (!Utils.isEmpty(populateFilter)) {
|
|
1476
|
+
cond = { $and: [cond, populateFilter] };
|
|
1477
|
+
}
|
|
1478
|
+
const results = (await this.find(targetMeta.class, cond, {
|
|
1479
|
+
ctx,
|
|
1480
|
+
...childOptions,
|
|
1481
|
+
populate: populate,
|
|
1482
|
+
}));
|
|
1483
|
+
const byPk = new Map();
|
|
1484
|
+
for (const row of results) {
|
|
1485
|
+
Object.defineProperty(row, 'constructor', {
|
|
1486
|
+
value: targetMeta.class,
|
|
1487
|
+
enumerable: false,
|
|
1488
|
+
configurable: true,
|
|
1489
|
+
});
|
|
1490
|
+
byPk.set(Utils.getPrimaryKeyHash([row[pkCol]]), row);
|
|
1491
|
+
}
|
|
1492
|
+
for (const row of rows) {
|
|
1493
|
+
const pkHash = Utils.getPrimaryKeyHash([row[prop.discriminator]]);
|
|
1494
|
+
const entity = byPk.get(pkHash);
|
|
1495
|
+
if (entity == null) {
|
|
1496
|
+
orphanedRows.add(row);
|
|
1497
|
+
continue;
|
|
1498
|
+
}
|
|
1499
|
+
row[prop.discriminator] = entity;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
const result = orphanedRows.size > 0 ? pivotRows.filter(r => !orphanedRows.has(r)) : pivotRows;
|
|
1503
|
+
return this.buildPivotResultMap(owners, result, ownerProp.name, prop.discriminator);
|
|
1504
|
+
}
|
|
1242
1505
|
/**
|
|
1243
1506
|
* Build a map from owner PKs to their related entities from pivot table results.
|
|
1244
1507
|
*/
|
|
@@ -1263,6 +1526,21 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1263
1526
|
}
|
|
1264
1527
|
return undefined;
|
|
1265
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* The pivot query builder doesn't convert custom types — manually convert owner PKs to the DB
|
|
1531
|
+
* representation. Returns `needsConversion` + `pkProp` so the caller can convert result FKs back
|
|
1532
|
+
* to JS format for consistent key hashing in `buildPivotResultMap`.
|
|
1533
|
+
*/
|
|
1534
|
+
convertOwnerPksForPivotQuery(owners, ownerMeta) {
|
|
1535
|
+
const pkProp = ownerMeta.properties[ownerMeta.primaryKeys[0]];
|
|
1536
|
+
const needsConversion = !!pkProp?.customType?.ensureComparable(ownerMeta, pkProp) && !ownerMeta.compositePK;
|
|
1537
|
+
let ownerPks = ownerMeta.compositePK ? owners : owners.map(o => o[0]);
|
|
1538
|
+
/* v8 ignore next 4 - custom-type PK conversion, tested via loadFromPivotTable path */
|
|
1539
|
+
if (needsConversion) {
|
|
1540
|
+
ownerPks = ownerPks.map(v => pkProp.customType.convertToDatabaseValue(v, this.platform, { mode: 'query' }));
|
|
1541
|
+
}
|
|
1542
|
+
return { ownerPks, needsConversion, pkProp };
|
|
1543
|
+
}
|
|
1266
1544
|
getPivotOrderBy(prop, pivotProp, orderBy, parentOrderBy) {
|
|
1267
1545
|
if (!Utils.isEmpty(orderBy) || RawQueryFragment.hasObjectFragments(orderBy)) {
|
|
1268
1546
|
return Utils.asArray(orderBy).map(o => ({ [pivotProp.name]: o }));
|
|
@@ -1329,6 +1607,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1329
1607
|
if (ref && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind)) {
|
|
1330
1608
|
return true;
|
|
1331
1609
|
}
|
|
1610
|
+
// Union-target polymorphic M:N cannot be loaded via a single JOIN because rows span multiple
|
|
1611
|
+
// target tables; fall through to SELECT_IN which dispatches through `loadFromPivotTable`.
|
|
1612
|
+
// Polymorphic M:1 (to-one with target_type discriminator) is handled via LEFT JOINs elsewhere.
|
|
1613
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && QueryHelper.isUnionTargetPolymorphic(prop) && prop.owner) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1332
1616
|
// skip redundant joins for 1:1 owner population hints when using `mapToPk`
|
|
1333
1617
|
if (prop.kind === ReferenceKind.ONE_TO_ONE && prop.mapToPk && prop.owner) {
|
|
1334
1618
|
return false;
|
|
@@ -1713,6 +1997,57 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1713
1997
|
}
|
|
1714
1998
|
return qb;
|
|
1715
1999
|
}
|
|
2000
|
+
/**
|
|
2001
|
+
* Renders a `FilterQuery` predicate into a SQL fragment (without the `WHERE` keyword and
|
|
2002
|
+
* without table-alias prefixes) suitable for inlining into a partial-index DDL statement.
|
|
2003
|
+
* Used by `DatabaseTable.addIndex` when the user passes an object `where` on `@Index` /
|
|
2004
|
+
* `@Unique`. Strings are returned unchanged.
|
|
2005
|
+
*/
|
|
2006
|
+
renderPartialIndexWhere(entityName, where) {
|
|
2007
|
+
if (typeof where === 'string') {
|
|
2008
|
+
return where;
|
|
2009
|
+
}
|
|
2010
|
+
const name = Utils.className(entityName);
|
|
2011
|
+
if (where == null || (Utils.isPlainObject(where) && Object.keys(where).length === 0)) {
|
|
2012
|
+
throw new Error(`Cannot render partial-index predicate for entity '${name}': \`where\` is empty.`);
|
|
2013
|
+
}
|
|
2014
|
+
const alias = '__p';
|
|
2015
|
+
const qb = this.createQueryBuilder(entityName, undefined, undefined, undefined, undefined, alias);
|
|
2016
|
+
qb.where(where);
|
|
2017
|
+
const sql = qb.getFormattedQuery();
|
|
2018
|
+
// Relation traversal produces join clauses whose aliased identifiers can't be inlined
|
|
2019
|
+
// into a CREATE INDEX ... WHERE clause — reject with a clear error rather than emitting broken DDL.
|
|
2020
|
+
if (/\bjoin\b/i.test(sql.split(/\bwhere\b/i)[0])) {
|
|
2021
|
+
throw new Error(`Cannot render partial-index predicate for entity '${name}': \`where\` may not traverse relations.`);
|
|
2022
|
+
}
|
|
2023
|
+
// Anchor at end-of-string only — the synthetic QB has no top-level order by / limit /
|
|
2024
|
+
// group by / having / offset, so any such keyword inside the captured predicate is
|
|
2025
|
+
// inside a subquery and must not terminate the match.
|
|
2026
|
+
const match = /\bwhere\s+([\s\S]+)$/i.exec(sql);
|
|
2027
|
+
if (!match) {
|
|
2028
|
+
throw new Error(`Failed to render partial-index predicate for entity '${name}': ${sql}`);
|
|
2029
|
+
}
|
|
2030
|
+
const quote = (s) => this.platform.quoteIdentifier(s);
|
|
2031
|
+
const aliasPrefix = new RegExp(`${quote(alias).replace(/[[\]]/g, '\\$&')}\\.`, 'g');
|
|
2032
|
+
const stripped = match[1].replace(aliasPrefix, '').trim();
|
|
2033
|
+
// Any qualified column reference remaining after the alias strip points at another table or
|
|
2034
|
+
// subquery and can't be inlined into a CREATE INDEX ... WHERE predicate. Covers both
|
|
2035
|
+
// QB-generated sub-aliases (quoted, e.g. `"e0"."col"`) and raw fragments with bare refs
|
|
2036
|
+
// (e.g. `raw('other_table.col = 1')`). String literals are erased first so dots inside
|
|
2037
|
+
// them (e.g. JSON path operands like `'$.path'`) don't trip the guard.
|
|
2038
|
+
// Both patterns use a `(?!\s*\()` lookahead so schema-qualified function calls
|
|
2039
|
+
// (`pg_catalog.lower(name)`, `"public".my_func(col)`) are accepted — only `<id>.<id>` not
|
|
2040
|
+
// followed by `(` is treated as a cross-table column reference.
|
|
2041
|
+
const withoutStrings = stripped.replace(/'(?:[^']|'')*'/g, "''");
|
|
2042
|
+
const quotedIdent = String.raw `(?:"(?:[^"]|"")+"|\`(?:[^\`]|\`\`)+\`|\[(?:[^\]]|\]\])+\])`;
|
|
2043
|
+
const anyIdent = `(?:${quotedIdent}|[A-Za-z_]\\w*)`;
|
|
2044
|
+
const quotedCrossRef = new RegExp(`${quotedIdent}\\s*\\.\\s*${anyIdent}(?!\\s*\\()`);
|
|
2045
|
+
const bareCrossRef = /\b[A-Za-z_]\w*\s*\.\s*[A-Za-z_]\w*\b(?!\s*\()/;
|
|
2046
|
+
if (quotedCrossRef.test(withoutStrings) || bareCrossRef.test(withoutStrings)) {
|
|
2047
|
+
throw new Error(`Cannot render partial-index predicate for entity '${name}': \`where\` references another table or subquery which cannot be inlined into a CREATE INDEX ... WHERE clause.`);
|
|
2048
|
+
}
|
|
2049
|
+
return stripped;
|
|
2050
|
+
}
|
|
1716
2051
|
resolveConnectionType(args) {
|
|
1717
2052
|
if (args.ctx) {
|
|
1718
2053
|
return 'write';
|
|
@@ -1739,7 +2074,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1739
2074
|
for (const prop of meta.relations) {
|
|
1740
2075
|
if (collections[prop.name]) {
|
|
1741
2076
|
const pivotMeta = this.metadata.get(prop.pivotEntity);
|
|
1742
|
-
const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema, options?.loggerContext);
|
|
2077
|
+
const persister = new PivotCollectionPersister(pivotMeta, this, options?.ctx, options?.schema, options?.loggerContext, pickAbortOptions(options));
|
|
1743
2078
|
persister.enqueueUpdate(prop, collections[prop.name], clear, pks);
|
|
1744
2079
|
await this.rethrow(persister.execute());
|
|
1745
2080
|
}
|
|
@@ -1748,6 +2083,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1748
2083
|
async lockPessimistic(entity, options) {
|
|
1749
2084
|
const meta = helper(entity).__meta;
|
|
1750
2085
|
const qb = this.createQueryBuilder(meta.class, options.ctx, undefined, undefined, options.logging).withSchema(options.schema ?? meta.schema);
|
|
2086
|
+
qb.setAbortOptions(pickAbortOptions(options));
|
|
1751
2087
|
const cond = Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
|
|
1752
2088
|
qb.select(raw('1'))
|
|
1753
2089
|
.where(cond)
|
package/AbstractSqlPlatform.d.ts
CHANGED
|
@@ -51,8 +51,19 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
51
51
|
* @internal
|
|
52
52
|
*/
|
|
53
53
|
quoteCollation(collation: string): string;
|
|
54
|
-
/**
|
|
55
|
-
|
|
54
|
+
/**
|
|
55
|
+
* PG ICU locale names include hyphens (`en-US-x-icu`) and libc locales include dots (`en_US.utf8`),
|
|
56
|
+
* so word-chars alone would reject valid real-world collations.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
validateCollationName(collation: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Whether collation names compare case-insensitively in this dialect. MySQL/MariaDB, MSSQL, and
|
|
62
|
+
* SQLite use case-insensitive collation identifiers; PostgreSQL stores them as case-sensitive
|
|
63
|
+
* names in `pg_collation` (e.g. `en-US-x-icu` is distinct from `EN-US-X-ICU`).
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
caseInsensitiveCollationNames(): boolean;
|
|
56
67
|
/** @internal */
|
|
57
68
|
validateJsonPropertyName(name: string): void;
|
|
58
69
|
/**
|
package/AbstractSqlPlatform.js
CHANGED
|
@@ -128,12 +128,25 @@ export class AbstractSqlPlatform extends Platform {
|
|
|
128
128
|
this.validateCollationName(collation);
|
|
129
129
|
return this.quoteIdentifier(collation);
|
|
130
130
|
}
|
|
131
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* PG ICU locale names include hyphens (`en-US-x-icu`) and libc locales include dots (`en_US.utf8`),
|
|
133
|
+
* so word-chars alone would reject valid real-world collations.
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
132
136
|
validateCollationName(collation) {
|
|
133
|
-
if (!/^[\w]+$/.test(collation)) {
|
|
134
|
-
throw new Error(`Invalid collation name: '${collation}'. Collation names must contain only word characters.`);
|
|
137
|
+
if (!/^[\w\-.]+$/.test(collation)) {
|
|
138
|
+
throw new Error(`Invalid collation name: '${collation}'. Collation names must contain only word characters, hyphens, and dots.`);
|
|
135
139
|
}
|
|
136
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Whether collation names compare case-insensitively in this dialect. MySQL/MariaDB, MSSQL, and
|
|
143
|
+
* SQLite use case-insensitive collation identifiers; PostgreSQL stores them as case-sensitive
|
|
144
|
+
* names in `pg_collation` (e.g. `en-US-x-icu` is distinct from `EN-US-X-ICU`).
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
caseInsensitiveCollationNames() {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
137
150
|
/** @internal */
|
|
138
151
|
validateJsonPropertyName(name) {
|
|
139
152
|
if (!AbstractSqlPlatform.#JSON_PROPERTY_NAME_RE.test(name)) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Dictionary, type EntityMetadata, type EntityProperty, type Primary, type Transaction } from '@mikro-orm/core';
|
|
1
|
+
import { type AbortQueryOptions, type Dictionary, type EntityMetadata, type EntityProperty, type Primary, type Transaction } from '@mikro-orm/core';
|
|
2
2
|
import { type AbstractSqlDriver } from './AbstractSqlDriver.js';
|
|
3
3
|
export declare class PivotCollectionPersister<Entity extends object> {
|
|
4
4
|
#private;
|
|
5
|
-
constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction, schema?: string, loggerContext?: Dictionary);
|
|
5
|
+
constructor(meta: EntityMetadata<Entity>, driver: AbstractSqlDriver, ctx?: Transaction, schema?: string, loggerContext?: Dictionary, abort?: AbortQueryOptions);
|
|
6
6
|
enqueueUpdate(prop: EntityProperty<Entity>, insertDiff: Primary<Entity>[][], deleteDiff: Primary<Entity>[][] | boolean, pks: Primary<Entity>[], isInitialized?: boolean): void;
|
|
7
7
|
private enqueueInsert;
|
|
8
8
|
private enqueueUpsert;
|