@mikro-orm/sql 7.0.0-rc.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +5 -4
- package/AbstractSqlConnection.js +20 -6
- package/AbstractSqlDriver.d.ts +19 -13
- package/AbstractSqlDriver.js +225 -47
- package/AbstractSqlPlatform.d.ts +35 -0
- package/AbstractSqlPlatform.js +51 -5
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -4
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +18 -2
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +25 -14
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +49 -37
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +75 -59
- package/dialects/sqlite/BaseSqliteConnection.js +2 -2
- package/dialects/sqlite/NodeSqliteDialect.js +3 -1
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +7 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +23 -17
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +30 -30
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +91 -82
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +20 -4
- package/query/NativeQueryBuilder.d.ts +28 -3
- package/query/NativeQueryBuilder.js +65 -3
- package/query/ObjectCriteriaNode.js +75 -31
- package/query/QueryBuilder.d.ts +199 -100
- package/query/QueryBuilder.js +544 -358
- package/query/QueryBuilderHelper.d.ts +18 -14
- package/query/QueryBuilderHelper.js +364 -147
- package/query/ScalarCriteriaNode.js +17 -8
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +68 -45
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +191 -107
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +76 -50
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +30 -9
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +26 -12
- package/typings.d.ts +10 -5
- package/tsconfig.build.tsbuildinfo +0 -1
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) {
|
|
@@ -60,7 +60,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
60
60
|
this.validateSqlOptions(options);
|
|
61
61
|
const { first, last, before, after } = options;
|
|
62
62
|
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
63
|
-
qb.
|
|
63
|
+
qb.state.resolvedPopulateWhere = options._populateWhere;
|
|
64
64
|
qb.select(fields)
|
|
65
65
|
// only add populateWhere if we are populate-joining, as this will be used to add `on` conditions
|
|
66
66
|
.populate(populate, joinedProps.length > 0 ? populateWhere : undefined, joinedProps.length > 0 ? options.populateFilter : undefined)
|
|
@@ -95,6 +95,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
95
95
|
if (meta.virtual) {
|
|
96
96
|
return this.findVirtual(entityName, where, options);
|
|
97
97
|
}
|
|
98
|
+
if (options.unionWhere?.length) {
|
|
99
|
+
where = await this.applyUnionWhere(meta, where, options);
|
|
100
|
+
}
|
|
98
101
|
const qb = await this.createQueryBuilderFromOptions(meta, where, options);
|
|
99
102
|
const result = await this.rethrow(qb.execute('all'));
|
|
100
103
|
if (options.last && !options.first) {
|
|
@@ -197,7 +200,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
197
200
|
if (type === QueryType.COUNT) {
|
|
198
201
|
native.clear('select').clear('limit').clear('offset').count();
|
|
199
202
|
}
|
|
200
|
-
|
|
203
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
204
|
+
native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
|
|
201
205
|
const query = native.compile();
|
|
202
206
|
const res = await this.execute(query.sql, query.params, 'all', options.ctx);
|
|
203
207
|
if (type === QueryType.COUNT) {
|
|
@@ -212,7 +216,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
212
216
|
const qb = await this.createQueryBuilderFromOptions(meta, where, this.forceBalancedStrategy(options));
|
|
213
217
|
qb.unsetFlag(QueryFlag.DISABLE_PAGINATE);
|
|
214
218
|
const native = qb.getNativeQuery(false);
|
|
215
|
-
|
|
219
|
+
const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
|
|
220
|
+
native.from(raw(`(${expression})${asKeyword}${this.platform.quoteIdentifier(qb.alias)}`));
|
|
216
221
|
const query = native.compile();
|
|
217
222
|
const connectionType = this.resolveConnectionType({ ctx: options.ctx, connectionType: options.connectionType });
|
|
218
223
|
const res = this.getConnection(connectionType).stream(query.sql, query.params, options.ctx, options.loggerContext);
|
|
@@ -265,7 +270,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
265
270
|
* and need to be renamed back to `column_name` for the result mapper to work.
|
|
266
271
|
*/
|
|
267
272
|
mapTPTColumns(result, meta, qb) {
|
|
268
|
-
const tptAliases = qb.
|
|
273
|
+
const tptAliases = qb.state.tptAlias;
|
|
269
274
|
// Walk up the TPT hierarchy
|
|
270
275
|
let parentMeta = meta.tptParent;
|
|
271
276
|
while (parentMeta) {
|
|
@@ -297,7 +302,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
297
302
|
}
|
|
298
303
|
// Polymorphic to-one: iterate targets, find the matching one, build entity from its columns.
|
|
299
304
|
// Skip :ref hints — no JOINs were created, so the FK reference is already set by the result mapper.
|
|
300
|
-
if (prop.polymorphic &&
|
|
305
|
+
if (prop.polymorphic &&
|
|
306
|
+
prop.polymorphTargets?.length &&
|
|
307
|
+
!ref &&
|
|
308
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
301
309
|
const basePath = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
|
|
302
310
|
const pathPrefix = !parentJoinPath ? '[populate]' : '';
|
|
303
311
|
let matched = false;
|
|
@@ -306,16 +314,22 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
306
314
|
const relationAlias = qb.getAliasForJoinPath(targetPath, { matchPopulateJoins: true });
|
|
307
315
|
const meta2 = targetMeta;
|
|
308
316
|
const targetProps = meta2.props.filter(p => this.platform.shouldHaveColumn(p, hint.children || []));
|
|
309
|
-
const hasPK = meta2
|
|
317
|
+
const hasPK = meta2
|
|
318
|
+
.getPrimaryProps()
|
|
319
|
+
.every(pk => pk.fieldNames.every(name => root[`${relationAlias}__${name}`] != null));
|
|
310
320
|
if (hasPK && !matched) {
|
|
311
321
|
matched = true;
|
|
312
|
-
|
|
322
|
+
const relationPojo = {};
|
|
313
323
|
const tz = this.platform.getTimezone();
|
|
314
324
|
for (const p of targetProps) {
|
|
315
325
|
this.mapJoinedProp(relationPojo, p, relationAlias, root, tz, meta2);
|
|
316
326
|
}
|
|
317
327
|
// Inject the entity class constructor so that the factory creates the correct type
|
|
318
|
-
Object.defineProperty(relationPojo, 'constructor', {
|
|
328
|
+
Object.defineProperty(relationPojo, 'constructor', {
|
|
329
|
+
value: meta2.class,
|
|
330
|
+
enumerable: false,
|
|
331
|
+
configurable: true,
|
|
332
|
+
});
|
|
319
333
|
result[prop.name] = relationPojo;
|
|
320
334
|
const populateChildren = hint.children || [];
|
|
321
335
|
this.mapJoinedProps(relationPojo, meta2, populateChildren, qb, root, map, targetPath);
|
|
@@ -366,7 +380,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
366
380
|
return;
|
|
367
381
|
}
|
|
368
382
|
const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
|
|
369
|
-
const targetProps = mapToPk
|
|
383
|
+
const targetProps = mapToPk
|
|
384
|
+
? meta2.getPrimaryProps()
|
|
385
|
+
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
|
|
370
386
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
371
387
|
// and therefore we don't return any record (since all values would be null)
|
|
372
388
|
const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
|
|
@@ -459,7 +475,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
459
475
|
else if (prop.runtimeType === 'Date') {
|
|
460
476
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
461
477
|
const value = root[alias];
|
|
462
|
-
if (tz &&
|
|
478
|
+
if (tz &&
|
|
479
|
+
tz !== 'local' &&
|
|
480
|
+
typeof value === 'string' &&
|
|
481
|
+
!value.includes('+') &&
|
|
482
|
+
value.lastIndexOf('-') < 11 &&
|
|
483
|
+
!value.endsWith('Z')) {
|
|
463
484
|
relationPojo[prop.name] = this.platform.parseDate(value + tz);
|
|
464
485
|
}
|
|
465
486
|
else if (['string', 'number'].includes(typeof value)) {
|
|
@@ -475,10 +496,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
475
496
|
if (prop.kind === ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
|
|
476
497
|
const item = parseJsonSafe(relationPojo[prop.name]);
|
|
477
498
|
if (Array.isArray(item)) {
|
|
478
|
-
relationPojo[prop.name] = item.map(row =>
|
|
499
|
+
relationPojo[prop.name] = item.map(row => row == null ? row : this.comparator.mapResult(prop.targetMeta, row));
|
|
479
500
|
}
|
|
480
501
|
else {
|
|
481
|
-
relationPojo[prop.name] =
|
|
502
|
+
relationPojo[prop.name] =
|
|
503
|
+
item == null ? item : this.comparator.mapResult(prop.targetMeta, item);
|
|
482
504
|
}
|
|
483
505
|
}
|
|
484
506
|
}
|
|
@@ -493,6 +515,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
493
515
|
if (meta.virtual) {
|
|
494
516
|
return this.countVirtual(entityName, where, options);
|
|
495
517
|
}
|
|
518
|
+
if (options.unionWhere?.length) {
|
|
519
|
+
where = await this.applyUnionWhere(meta, where, options);
|
|
520
|
+
}
|
|
496
521
|
options = { populate: [], ...options };
|
|
497
522
|
const populate = options.populate;
|
|
498
523
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
@@ -503,7 +528,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
503
528
|
this.buildFields(meta, populate, joinedProps, qb, qb.alias, options, schema);
|
|
504
529
|
}
|
|
505
530
|
this.validateSqlOptions(options);
|
|
506
|
-
qb.
|
|
531
|
+
qb.state.resolvedPopulateWhere = options._populateWhere;
|
|
507
532
|
qb.indexHint(options.indexHint)
|
|
508
533
|
.collation(options.collation)
|
|
509
534
|
.comment(options.comments)
|
|
@@ -533,7 +558,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
533
558
|
else {
|
|
534
559
|
/* v8 ignore next */
|
|
535
560
|
res.insertId = data[meta.primaryKeys[0]] ?? res.insertId ?? res.row[meta.primaryKeys[0]];
|
|
536
|
-
|
|
561
|
+
if (options.convertCustomTypes && meta?.getPrimaryProp().customType) {
|
|
562
|
+
pk = [meta.getPrimaryProp().customType.convertToDatabaseValue(res.insertId, this.platform)];
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
pk = [res.insertId];
|
|
566
|
+
}
|
|
537
567
|
}
|
|
538
568
|
await this.processManyToMany(meta, pk, collections, false, options);
|
|
539
569
|
return res;
|
|
@@ -557,13 +587,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
557
587
|
}
|
|
558
588
|
const tableName = this.getTableName(meta, options);
|
|
559
589
|
let sql = `insert into ${tableName} `;
|
|
560
|
-
sql +=
|
|
590
|
+
sql +=
|
|
591
|
+
fields.length > 0
|
|
592
|
+
? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')'
|
|
593
|
+
: `(${this.platform.quoteIdentifier(pks[0])})`;
|
|
561
594
|
if (this.platform.usesOutputStatement()) {
|
|
562
595
|
const returningProps = this.getTableProps(meta)
|
|
563
596
|
.filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
|
|
564
597
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
565
598
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
566
|
-
sql +=
|
|
599
|
+
sql +=
|
|
600
|
+
returningFields.length > 0
|
|
601
|
+
? ` output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`
|
|
602
|
+
: '';
|
|
567
603
|
}
|
|
568
604
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
569
605
|
sql += ' values ';
|
|
@@ -650,7 +686,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
650
686
|
else {
|
|
651
687
|
const field = prop.fieldNames[0];
|
|
652
688
|
if (!duplicates.includes(field) || !usedDups.includes(field)) {
|
|
653
|
-
if (prop.customType &&
|
|
689
|
+
if (prop.customType &&
|
|
690
|
+
!prop.object &&
|
|
691
|
+
'convertToDatabaseValueSQL' in prop.customType &&
|
|
692
|
+
row[prop.name] != null &&
|
|
693
|
+
!isRaw(row[prop.name])) {
|
|
654
694
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
655
695
|
}
|
|
656
696
|
else {
|
|
@@ -671,7 +711,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
671
711
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
672
712
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
673
713
|
/* v8 ignore next */
|
|
674
|
-
sql +=
|
|
714
|
+
sql +=
|
|
715
|
+
returningFields.length > 0
|
|
716
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
717
|
+
: '';
|
|
675
718
|
}
|
|
676
719
|
if (transform) {
|
|
677
720
|
sql = transform(sql);
|
|
@@ -704,11 +747,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
704
747
|
/* v8 ignore next */
|
|
705
748
|
where = { [meta.primaryKeys[0] ?? pks[0]]: where };
|
|
706
749
|
}
|
|
750
|
+
if (!options.upsert && options.unionWhere?.length) {
|
|
751
|
+
where = (await this.applyUnionWhere(meta, where, options, true));
|
|
752
|
+
}
|
|
707
753
|
if (Utils.hasObjectKeys(data)) {
|
|
708
754
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
709
755
|
if (options.upsert) {
|
|
710
756
|
/* v8 ignore next */
|
|
711
|
-
const uniqueFields = options.onConflictFields ??
|
|
757
|
+
const uniqueFields = options.onConflictFields ??
|
|
758
|
+
(Utils.isPlainObject(where) ? Utils.keys(where) : meta.primaryKeys);
|
|
712
759
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
713
760
|
qb.insert(data)
|
|
714
761
|
.onConflict(uniqueFields)
|
|
@@ -728,7 +775,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
728
775
|
qb.update(data).where(where);
|
|
729
776
|
// reload generated columns and version fields
|
|
730
777
|
const returning = [];
|
|
731
|
-
meta.props
|
|
778
|
+
meta.props
|
|
779
|
+
.filter(prop => (prop.generated && !prop.primary) || prop.version)
|
|
780
|
+
.forEach(prop => returning.push(prop.name));
|
|
732
781
|
qb.returning(returning);
|
|
733
782
|
}
|
|
734
783
|
res = await this.rethrow(qb.execute('run', false));
|
|
@@ -738,13 +787,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
738
787
|
await this.processManyToMany(meta, pk, collections, true, options);
|
|
739
788
|
return res;
|
|
740
789
|
}
|
|
741
|
-
async nativeUpdateMany(entityName, where, data, options = {}) {
|
|
790
|
+
async nativeUpdateMany(entityName, where, data, options = {}, transform) {
|
|
742
791
|
options.processCollections ??= true;
|
|
743
792
|
options.convertCustomTypes ??= true;
|
|
744
793
|
const meta = this.metadata.get(entityName);
|
|
745
794
|
if (options.upsert) {
|
|
746
795
|
const uniqueFields = options.onConflictFields ??
|
|
747
|
-
(Utils.isPlainObject(where[0])
|
|
796
|
+
(Utils.isPlainObject(where[0])
|
|
797
|
+
? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key))
|
|
798
|
+
: meta.primaryKeys);
|
|
748
799
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
749
800
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
750
801
|
qb.insert(data)
|
|
@@ -815,7 +866,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
815
866
|
if (key in data[idx]) {
|
|
816
867
|
const pks = Utils.getOrderedPrimaryKeys(cond, meta);
|
|
817
868
|
sql += ` when (${pkCond}) then `;
|
|
818
|
-
if (prop.customType &&
|
|
869
|
+
if (prop.customType &&
|
|
870
|
+
!prop.object &&
|
|
871
|
+
'convertToDatabaseValueSQL' in prop.customType &&
|
|
872
|
+
data[idx][prop.name] != null &&
|
|
873
|
+
!isRaw(data[idx][key])) {
|
|
819
874
|
sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
|
|
820
875
|
}
|
|
821
876
|
else {
|
|
@@ -844,7 +899,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
844
899
|
sql = sql.substring(0, sql.length - 2) + ' where ';
|
|
845
900
|
const pkProps = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
|
|
846
901
|
const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
847
|
-
sql +=
|
|
902
|
+
sql +=
|
|
903
|
+
pks.length > 1
|
|
904
|
+
? `(${pks.map(pk => this.platform.quoteIdentifier(pk)).join(', ')})`
|
|
905
|
+
: this.platform.quoteIdentifier(pks[0]);
|
|
848
906
|
const conds = where.map(cond => {
|
|
849
907
|
if (Utils.isPlainObject(cond) && Utils.getObjectKeysSize(cond) === 1) {
|
|
850
908
|
cond = Object.values(cond)[0];
|
|
@@ -867,7 +925,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
867
925
|
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
868
926
|
const returningFields = Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
869
927
|
/* v8 ignore next */
|
|
870
|
-
sql +=
|
|
928
|
+
sql +=
|
|
929
|
+
returningFields.length > 0
|
|
930
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
931
|
+
: '';
|
|
932
|
+
}
|
|
933
|
+
if (transform) {
|
|
934
|
+
sql = transform(sql, params);
|
|
871
935
|
}
|
|
872
936
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
|
|
873
937
|
for (let i = 0; i < collections.length; i++) {
|
|
@@ -881,6 +945,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
881
945
|
if (Utils.isPrimaryKey(where) && pks.length === 1) {
|
|
882
946
|
where = { [pks[0]]: where };
|
|
883
947
|
}
|
|
948
|
+
if (options.unionWhere?.length) {
|
|
949
|
+
where = await this.applyUnionWhere(meta, where, options, true);
|
|
950
|
+
}
|
|
884
951
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext)
|
|
885
952
|
.delete(where)
|
|
886
953
|
.withSchema(this.getSchemaName(meta, options));
|
|
@@ -935,7 +1002,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
935
1002
|
const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
|
|
936
1003
|
if (coll.getSnapshot() === undefined) {
|
|
937
1004
|
if (coll.property.orphanRemoval) {
|
|
938
|
-
const query = qb
|
|
1005
|
+
const query = qb
|
|
1006
|
+
.delete({ [coll.property.mappedBy]: pks })
|
|
1007
|
+
.andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
|
|
939
1008
|
await this.rethrow(query.execute());
|
|
940
1009
|
continue;
|
|
941
1010
|
}
|
|
@@ -947,7 +1016,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
947
1016
|
continue;
|
|
948
1017
|
}
|
|
949
1018
|
/* v8 ignore next */
|
|
950
|
-
const query = qb
|
|
1019
|
+
const query = qb
|
|
1020
|
+
.update({ [coll.property.mappedBy]: pks })
|
|
1021
|
+
.where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
|
|
951
1022
|
await this.rethrow(query.execute());
|
|
952
1023
|
continue;
|
|
953
1024
|
}
|
|
@@ -960,7 +1031,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
960
1031
|
else {
|
|
961
1032
|
const targetMeta = coll.property.targetMeta;
|
|
962
1033
|
const targetSchema = (coll[0] ?? snap?.[0]) && helper(coll[0] ?? snap?.[0]).getSchema();
|
|
963
|
-
schema =
|
|
1034
|
+
schema =
|
|
1035
|
+
targetMeta.schema === '*'
|
|
1036
|
+
? (options?.schema ?? targetSchema ?? this.config.get('schema'))
|
|
1037
|
+
: targetMeta.schema;
|
|
964
1038
|
}
|
|
965
1039
|
}
|
|
966
1040
|
else if (schema == null) {
|
|
@@ -986,8 +1060,17 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
986
1060
|
const pivotProp1 = pivotMeta.relations[prop.owner ? 1 : 0];
|
|
987
1061
|
const pivotProp2 = pivotMeta.relations[prop.owner ? 0 : 1];
|
|
988
1062
|
const ownerMeta = pivotProp2.targetMeta;
|
|
1063
|
+
// The pivot query builder doesn't convert custom types, so we need to manually
|
|
1064
|
+
// convert owner PKs to DB format for the query and convert result FKs back to
|
|
1065
|
+
// JS format for consistent key hashing in buildPivotResultMap.
|
|
1066
|
+
const pkProp = ownerMeta.properties[ownerMeta.primaryKeys[0]];
|
|
1067
|
+
const needsConversion = pkProp?.customType?.ensureComparable(ownerMeta, pkProp) && !ownerMeta.compositePK;
|
|
1068
|
+
let ownerPks = ownerMeta.compositePK ? owners : owners.map(o => o[0]);
|
|
1069
|
+
if (needsConversion) {
|
|
1070
|
+
ownerPks = ownerPks.map(v => pkProp.customType.convertToDatabaseValue(v, this.platform, { mode: 'query' }));
|
|
1071
|
+
}
|
|
989
1072
|
const cond = {
|
|
990
|
-
[pivotProp2.name]: { $in:
|
|
1073
|
+
[pivotProp2.name]: { $in: ownerPks },
|
|
991
1074
|
};
|
|
992
1075
|
if (!Utils.isEmpty(where)) {
|
|
993
1076
|
cond[pivotProp1.name] = { ...where };
|
|
@@ -997,7 +1080,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
997
1080
|
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
|
|
998
1081
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
999
1082
|
const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
1000
|
-
const fields = pivotJoin
|
|
1083
|
+
const fields = pivotJoin
|
|
1084
|
+
? [pivotProp1.name, pivotProp2.name]
|
|
1085
|
+
: [pivotProp1.name, pivotProp2.name, ...childFields];
|
|
1001
1086
|
const res = await this.find(pivotMeta.class, where, {
|
|
1002
1087
|
ctx,
|
|
1003
1088
|
...options,
|
|
@@ -1018,6 +1103,16 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1018
1103
|
_populateWhere: 'infer',
|
|
1019
1104
|
populateFilter: this.wrapPopulateFilter(options, pivotProp2.name),
|
|
1020
1105
|
});
|
|
1106
|
+
// Convert result FK values back to JS format so key hashing
|
|
1107
|
+
// in buildPivotResultMap is consistent with the owner keys.
|
|
1108
|
+
if (needsConversion) {
|
|
1109
|
+
for (const item of res) {
|
|
1110
|
+
const fk = item[pivotProp2.name];
|
|
1111
|
+
if (fk != null) {
|
|
1112
|
+
item[pivotProp2.name] = pkProp.customType.convertToJSValue(fk, this.platform);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1021
1116
|
return this.buildPivotResultMap(owners, res, pivotProp2.name, pivotProp1.name);
|
|
1022
1117
|
}
|
|
1023
1118
|
/**
|
|
@@ -1050,7 +1145,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1050
1145
|
const populateField = pivotJoin ? `${inverseProp.name}:ref` : inverseProp.name;
|
|
1051
1146
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1052
1147
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${inverseProp.name}.${f}`) : [];
|
|
1053
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1148
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1149
|
+
? options.exclude.map(f => `${inverseProp.name}.${f}`)
|
|
1150
|
+
: [];
|
|
1054
1151
|
const fields = pivotJoin
|
|
1055
1152
|
? [inverseProp.name, prop.discriminator, prop.discriminatorColumn]
|
|
1056
1153
|
: [inverseProp.name, prop.discriminator, prop.discriminatorColumn, ...childFields];
|
|
@@ -1060,7 +1157,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1060
1157
|
fields,
|
|
1061
1158
|
exclude: childExclude,
|
|
1062
1159
|
orderBy: this.getPivotOrderBy(prop, inverseProp, orderBy, options?.orderBy),
|
|
1063
|
-
populate: [
|
|
1160
|
+
populate: [
|
|
1161
|
+
{
|
|
1162
|
+
field: populateField,
|
|
1163
|
+
strategy: LoadStrategy.JOINED,
|
|
1164
|
+
joinType: JoinType.innerJoin,
|
|
1165
|
+
children: populate,
|
|
1166
|
+
dataOnly: inverseProp.mapToPk && !pivotJoin,
|
|
1167
|
+
},
|
|
1168
|
+
],
|
|
1064
1169
|
populateWhere: undefined,
|
|
1065
1170
|
// @ts-ignore
|
|
1066
1171
|
_populateWhere: 'infer',
|
|
@@ -1092,7 +1197,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1092
1197
|
const populateField = ownerRelationName;
|
|
1093
1198
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1094
1199
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${ownerRelationName}.${f}`) : [];
|
|
1095
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1200
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1201
|
+
? options.exclude.map(f => `${ownerRelationName}.${f}`)
|
|
1202
|
+
: [];
|
|
1096
1203
|
const fields = [ownerRelationName, tagProp.name, prop.discriminatorColumn, ...childFields];
|
|
1097
1204
|
const res = await this.find(pivotMeta.class, cond, {
|
|
1098
1205
|
ctx,
|
|
@@ -1100,7 +1207,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1100
1207
|
fields,
|
|
1101
1208
|
exclude: childExclude,
|
|
1102
1209
|
orderBy: this.getPivotOrderBy(prop, ownerProp, orderBy, options?.orderBy),
|
|
1103
|
-
populate: [
|
|
1210
|
+
populate: [
|
|
1211
|
+
{
|
|
1212
|
+
field: populateField,
|
|
1213
|
+
strategy: LoadStrategy.JOINED,
|
|
1214
|
+
joinType: JoinType.innerJoin,
|
|
1215
|
+
children: populate,
|
|
1216
|
+
},
|
|
1217
|
+
],
|
|
1104
1218
|
populateWhere: undefined,
|
|
1105
1219
|
// @ts-ignore
|
|
1106
1220
|
_populateWhere: 'infer',
|
|
@@ -1179,7 +1293,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1179
1293
|
}
|
|
1180
1294
|
const relationsToPopulate = populate.map(({ field }) => field.split(':')[0]);
|
|
1181
1295
|
const toPopulate = meta.relations
|
|
1182
|
-
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1296
|
+
.filter(prop => prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
1297
|
+
!prop.owner &&
|
|
1298
|
+
!prop.lazy &&
|
|
1299
|
+
!relationsToPopulate.includes(prop.name))
|
|
1183
1300
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
1184
1301
|
.map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
|
|
1185
1302
|
return [...populate, ...toPopulate];
|
|
@@ -1295,8 +1412,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1295
1412
|
const prop = meta.properties[propName];
|
|
1296
1413
|
// Polymorphic to-one: create a LEFT JOIN per target type
|
|
1297
1414
|
// Skip :ref hints — polymorphic to-one already has FK + discriminator in the row
|
|
1298
|
-
if (prop.polymorphic &&
|
|
1299
|
-
|
|
1415
|
+
if (prop.polymorphic &&
|
|
1416
|
+
prop.polymorphTargets?.length &&
|
|
1417
|
+
!ref &&
|
|
1418
|
+
[ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1419
|
+
const basePath = options.parentJoinPath
|
|
1420
|
+
? `${options.parentJoinPath}.${prop.name}`
|
|
1421
|
+
: `${meta.name}.${prop.name}`;
|
|
1300
1422
|
const pathPrefix = !options.parentJoinPath && populateWhereAll && !basePath.startsWith('[populate]') ? '[populate]' : '';
|
|
1301
1423
|
for (const targetMeta of prop.polymorphTargets) {
|
|
1302
1424
|
const tableAlias = qb.getNextAlias(targetMeta.className);
|
|
@@ -1314,7 +1436,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1314
1436
|
continue;
|
|
1315
1437
|
}
|
|
1316
1438
|
// ignore ref joins of known FKs unless it's a filter hint
|
|
1317
|
-
if (ref &&
|
|
1439
|
+
if (ref &&
|
|
1440
|
+
!hint.filter &&
|
|
1441
|
+
(prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
|
|
1318
1442
|
continue;
|
|
1319
1443
|
}
|
|
1320
1444
|
const meta2 = prop.targetMeta;
|
|
@@ -1360,7 +1484,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1360
1484
|
childExplicitFields.push(f.substring(prop.name.length + 1));
|
|
1361
1485
|
}
|
|
1362
1486
|
});
|
|
1363
|
-
const childExclude = options.exclude
|
|
1487
|
+
const childExclude = options.exclude
|
|
1488
|
+
? Utils.extractChildElements(options.exclude, prop.name)
|
|
1489
|
+
: options.exclude;
|
|
1364
1490
|
if (!ref && (!prop.mapToPk || hint.dataOnly)) {
|
|
1365
1491
|
fields.push(...this.getFieldsForJoinedLoad(qb, meta2, {
|
|
1366
1492
|
...options,
|
|
@@ -1371,7 +1497,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1371
1497
|
parentJoinPath: path,
|
|
1372
1498
|
}));
|
|
1373
1499
|
}
|
|
1374
|
-
else if (hint.filter ||
|
|
1500
|
+
else if (hint.filter ||
|
|
1501
|
+
(prop.mapToPk && !hint.dataOnly) ||
|
|
1502
|
+
(ref && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind))) {
|
|
1375
1503
|
fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
|
|
1376
1504
|
}
|
|
1377
1505
|
}
|
|
@@ -1410,7 +1538,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1410
1538
|
* @internal
|
|
1411
1539
|
*/
|
|
1412
1540
|
findTPTChildAlias(qb, childMeta) {
|
|
1413
|
-
const joins = qb.
|
|
1541
|
+
const joins = qb.state.joins;
|
|
1414
1542
|
for (const key of Object.keys(joins)) {
|
|
1415
1543
|
if (joins[key].table === childMeta.tableName && key.includes('[tpt]')) {
|
|
1416
1544
|
return joins[key].alias;
|
|
@@ -1473,7 +1601,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1473
1601
|
if (childAlias) {
|
|
1474
1602
|
// Map fields using same filtering as joined loading, plus skip PKs
|
|
1475
1603
|
for (const prop of currentMeta.ownProps.filter(p => !p.primary && this.platform.shouldHaveColumn(p, []))) {
|
|
1476
|
-
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1604
|
+
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1605
|
+
deleteFromRoot: true,
|
|
1606
|
+
});
|
|
1477
1607
|
}
|
|
1478
1608
|
}
|
|
1479
1609
|
currentMeta = currentMeta.tptParent;
|
|
@@ -1522,7 +1652,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1522
1652
|
/** @internal */
|
|
1523
1653
|
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
|
|
1524
1654
|
// do not compute the connectionType if EM is provided as it will be computed from it in the QB later on
|
|
1525
|
-
const connectionType = em
|
|
1655
|
+
const connectionType = em
|
|
1656
|
+
? preferredConnectionType
|
|
1657
|
+
: this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
1526
1658
|
const qb = new QueryBuilder(entityName, this.metadata, this, ctx, alias, connectionType, em, loggerContext);
|
|
1527
1659
|
if (!convertCustomTypes) {
|
|
1528
1660
|
qb.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
@@ -1598,6 +1730,47 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1598
1730
|
/* v8 ignore next */
|
|
1599
1731
|
return { $and: [options.populateWhere, where] };
|
|
1600
1732
|
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Builds a UNION ALL (or UNION) subquery from `unionWhere` branches and merges it
|
|
1735
|
+
* into the main WHERE as `pk IN (branch_1 UNION ALL branch_2 ...)`.
|
|
1736
|
+
* Each branch is planned independently by the database, enabling per-table index usage.
|
|
1737
|
+
*/
|
|
1738
|
+
async applyUnionWhere(meta, where, options, forDml = false) {
|
|
1739
|
+
const unionWhere = options.unionWhere;
|
|
1740
|
+
const strategy = options.unionWhereStrategy ?? 'union-all';
|
|
1741
|
+
const schema = this.getSchemaName(meta, options);
|
|
1742
|
+
const connectionType = this.resolveConnectionType({
|
|
1743
|
+
ctx: options.ctx,
|
|
1744
|
+
connectionType: options.connectionType,
|
|
1745
|
+
});
|
|
1746
|
+
const branchQbs = [];
|
|
1747
|
+
for (const branch of unionWhere) {
|
|
1748
|
+
const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging).withSchema(schema);
|
|
1749
|
+
const pkFields = meta.primaryKeys.map(pk => {
|
|
1750
|
+
const prop = meta.properties[pk];
|
|
1751
|
+
return `${qb.alias}.${prop.fieldNames[0]}`;
|
|
1752
|
+
});
|
|
1753
|
+
qb.select(pkFields).where(branch);
|
|
1754
|
+
if (options.em) {
|
|
1755
|
+
await qb.applyJoinedFilters(options.em, options.filters);
|
|
1756
|
+
}
|
|
1757
|
+
branchQbs.push(qb);
|
|
1758
|
+
}
|
|
1759
|
+
const [first, ...rest] = branchQbs;
|
|
1760
|
+
const unionQb = strategy === 'union' ? first.union(...rest) : first.unionAll(...rest);
|
|
1761
|
+
const pkHash = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
1762
|
+
// MySQL does not allow referencing the target table in a subquery
|
|
1763
|
+
// for UPDATE/DELETE, so we wrap the union in a derived table.
|
|
1764
|
+
if (forDml) {
|
|
1765
|
+
const { sql, params } = unionQb.toQuery();
|
|
1766
|
+
return {
|
|
1767
|
+
$and: [where, { [pkHash]: { $in: raw(`select * from (${sql}) as __u`, params) } }],
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
return {
|
|
1771
|
+
$and: [where, { [pkHash]: { $in: unionQb.toRaw() } }],
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1601
1774
|
buildOrderBy(qb, meta, populate, options) {
|
|
1602
1775
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
1603
1776
|
// `options._populateWhere` is a copy of the value provided by user with a fallback to the global config option
|
|
@@ -1628,7 +1801,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1628
1801
|
let path = parentPath;
|
|
1629
1802
|
const meta2 = prop.targetMeta;
|
|
1630
1803
|
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
1631
|
-
(![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
1804
|
+
(![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) ||
|
|
1805
|
+
!prop.owner ||
|
|
1806
|
+
Utils.isPlainObject(childOrder))) {
|
|
1632
1807
|
path += `.${field}`;
|
|
1633
1808
|
}
|
|
1634
1809
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
|
|
@@ -1639,7 +1814,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1639
1814
|
if (!join) {
|
|
1640
1815
|
continue;
|
|
1641
1816
|
}
|
|
1642
|
-
if (join &&
|
|
1817
|
+
if (join &&
|
|
1818
|
+
![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) &&
|
|
1819
|
+
typeof childOrder === 'object') {
|
|
1643
1820
|
const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
|
|
1644
1821
|
orderBy.push(...children);
|
|
1645
1822
|
continue;
|
|
@@ -1770,7 +1947,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1770
1947
|
if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
|
|
1771
1948
|
ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
|
|
1772
1949
|
}
|
|
1773
|
-
if (meta.root.inheritanceType === 'sti' &&
|
|
1950
|
+
if (meta.root.inheritanceType === 'sti' &&
|
|
1951
|
+
!options.fields.includes(`${qb.alias}.${meta.root.discriminatorColumn}`)) {
|
|
1774
1952
|
ret.push(meta.root.discriminatorColumn);
|
|
1775
1953
|
}
|
|
1776
1954
|
}
|
package/AbstractSqlPlatform.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { type SchemaHelper } from './schema/SchemaHelper.js';
|
|
|
4
4
|
import type { IndexDef } from './typings.js';
|
|
5
5
|
import { NativeQueryBuilder } from './query/NativeQueryBuilder.js';
|
|
6
6
|
export declare abstract class AbstractSqlPlatform extends Platform {
|
|
7
|
+
#private;
|
|
7
8
|
protected readonly schemaHelper?: SchemaHelper;
|
|
8
9
|
usesPivotTable(): boolean;
|
|
9
10
|
indexForeignKeys(): boolean;
|
|
@@ -26,7 +27,14 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
26
27
|
quoteValue(value: any): string;
|
|
27
28
|
getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string | RawQueryFragment;
|
|
28
29
|
getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string | RawQueryFragment;
|
|
30
|
+
/**
|
|
31
|
+
* Quotes a key for use inside a JSON path expression (e.g. `$.key`).
|
|
32
|
+
* Simple alphanumeric keys are left unquoted; others are wrapped in double quotes.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
quoteJsonKey(key: string): string;
|
|
29
36
|
getJsonIndexDefinition(index: IndexDef): string[];
|
|
37
|
+
supportsUnionWhere(): boolean;
|
|
30
38
|
supportsSchemas(): boolean;
|
|
31
39
|
/** @inheritDoc */
|
|
32
40
|
generateCustomOrder(escapedColumn: string, values: unknown[]): string;
|
|
@@ -41,4 +49,31 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
41
49
|
quoteCollation(collation: string): string;
|
|
42
50
|
/** @internal */
|
|
43
51
|
protected validateCollationName(collation: string): void;
|
|
52
|
+
/** @internal */
|
|
53
|
+
validateJsonPropertyName(name: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Returns FROM clause for JSON array iteration.
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
getJsonArrayFromSQL(column: string, alias: string, _properties: {
|
|
59
|
+
name: string;
|
|
60
|
+
type: string;
|
|
61
|
+
}[]): string;
|
|
62
|
+
/**
|
|
63
|
+
* Returns SQL expression to access an element's property within a JSON array iteration.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
getJsonArrayElementPropertySQL(alias: string, property: string, _type: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Wraps JSON array FROM clause and WHERE condition into a full EXISTS condition.
|
|
69
|
+
* MySQL overrides this because `json_table` doesn't support correlated subqueries.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
getJsonArrayExistsSQL(from: string, where: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Maps a runtime type name (e.g. 'string', 'number') to a driver-specific bind type constant.
|
|
75
|
+
* Used by NativeQueryBuilder for output bindings.
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
mapToBindType(type: string): unknown;
|
|
44
79
|
}
|