@mikro-orm/sql 7.0.0-rc.2 → 7.0.0-rc.3
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.d.ts +18 -12
- package/AbstractSqlDriver.js +187 -38
- package/AbstractSqlPlatform.d.ts +1 -0
- package/AbstractSqlPlatform.js +5 -3
- package/PivotCollectionPersister.js +2 -2
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -5
- package/dialects/mssql/MsSqlNativeQueryBuilder.d.ts +2 -0
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +8 -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 +63 -47
- 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 +30 -30
- package/plugin/transformer.js +17 -16
- package/query/CriteriaNode.js +28 -10
- package/query/CriteriaNodeFactory.js +5 -1
- package/query/NativeQueryBuilder.d.ts +25 -0
- package/query/NativeQueryBuilder.js +61 -1
- package/query/ObjectCriteriaNode.js +71 -27
- package/query/QueryBuilder.d.ts +151 -48
- package/query/QueryBuilder.js +233 -54
- package/query/QueryBuilderHelper.d.ts +4 -3
- package/query/QueryBuilderHelper.js +47 -17
- package/query/ScalarCriteriaNode.js +14 -7
- package/query/raw.js +1 -1
- package/schema/DatabaseSchema.js +21 -15
- package/schema/DatabaseTable.js +114 -54
- package/schema/SchemaComparator.js +56 -32
- package/schema/SchemaHelper.js +28 -8
- package/schema/SqlSchemaGenerator.js +13 -7
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +6 -4
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.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AnyEntity, type Collection, type Configuration, type ConnectionType, type Constructor, type CountOptions, DatabaseDriver, type DeleteOptions, type Dictionary, type DriverMethodOptions, type EntityData, type EntityDictionary, type EntityField, EntityManagerType, type EntityMetadata, type EntityName, type EntityProperty, type FilterQuery, type FindOneOptions, type FindOptions, type FormulaTable, type LockOptions, type LoggingOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type ObjectQuery, type Options, type OrderDefinition, type PopulateOptions, type PopulatePath, type Primary, type QueryOrderMap, type QueryResult, type Raw, RawQueryFragment, type StreamOptions, type Transaction, type UpsertManyOptions, type UpsertOptions } from '@mikro-orm/core';
|
|
2
2
|
import type { AbstractSqlConnection } from './AbstractSqlConnection.js';
|
|
3
3
|
import type { AbstractSqlPlatform } from './AbstractSqlPlatform.js';
|
|
4
|
-
import {
|
|
4
|
+
import { type AnyQueryBuilder } from './query/QueryBuilder.js';
|
|
5
5
|
import { type NativeQueryBuilder } from './query/NativeQueryBuilder.js';
|
|
6
6
|
import { QueryType } from './query/enums.js';
|
|
7
7
|
import { SqlEntityManager } from './SqlEntityManager.js';
|
|
@@ -36,7 +36,7 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
36
36
|
* Force balanced strategy to load to-many relations via separate queries.
|
|
37
37
|
*/
|
|
38
38
|
private forceBalancedStrategy;
|
|
39
|
-
mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?:
|
|
39
|
+
mapResult<T extends object>(result: EntityData<T>, meta: EntityMetadata<T>, populate?: PopulateOptions<T>[], qb?: AnyQueryBuilder<T>, map?: Dictionary): EntityData<T> | null;
|
|
40
40
|
/**
|
|
41
41
|
* Maps aliased columns from TPT parent tables back to their original field names.
|
|
42
42
|
* TPT parent columns are selected with aliases like `parent_alias__column_name`,
|
|
@@ -99,17 +99,17 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
99
99
|
*/
|
|
100
100
|
mergeJoinedResult<T extends object>(rawResults: EntityData<T>[], meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[]): EntityData<T>[];
|
|
101
101
|
protected shouldHaveColumn<T, U>(meta: EntityMetadata<T>, prop: EntityProperty<U>, populate: readonly PopulateOptions<U>[], fields?: readonly InternalField<U>[], exclude?: readonly InternalField<U>[]): boolean;
|
|
102
|
-
protected getFieldsForJoinedLoad<T extends object>(qb:
|
|
102
|
+
protected getFieldsForJoinedLoad<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, options: FieldsForJoinedLoadOptions<T>): InternalField<T>[];
|
|
103
103
|
/**
|
|
104
104
|
* Adds LEFT JOINs and fields for TPT polymorphic loading when populating a relation to a TPT base class.
|
|
105
105
|
* @internal
|
|
106
106
|
*/
|
|
107
|
-
protected addTPTPolymorphicJoinsForRelation<T extends object>(qb:
|
|
107
|
+
protected addTPTPolymorphicJoinsForRelation<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, baseAlias: string, fields: InternalField<T>[]): void;
|
|
108
108
|
/**
|
|
109
109
|
* Find the alias for a TPT child table in the query builder.
|
|
110
110
|
* @internal
|
|
111
111
|
*/
|
|
112
|
-
protected findTPTChildAlias<T extends object>(qb:
|
|
112
|
+
protected findTPTChildAlias<T extends object>(qb: AnyQueryBuilder<T>, childMeta: EntityMetadata): string | undefined;
|
|
113
113
|
/**
|
|
114
114
|
* Builds a CASE WHEN expression for TPT discriminator.
|
|
115
115
|
* Determines concrete entity type based on which child table has a non-null PK.
|
|
@@ -122,13 +122,13 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
122
122
|
* This method reads the discriminator to determine the concrete type and maps child-specific fields.
|
|
123
123
|
* @internal
|
|
124
124
|
*/
|
|
125
|
-
protected mapTPTChildFields<T extends object>(relationPojo: EntityData<T>, meta: EntityMetadata<T>, relationAlias: string, qb:
|
|
125
|
+
protected mapTPTChildFields<T extends object>(relationPojo: EntityData<T>, meta: EntityMetadata<T>, relationAlias: string, qb: AnyQueryBuilder<T>, root: EntityData<T>): void;
|
|
126
126
|
/**
|
|
127
127
|
* @internal
|
|
128
128
|
*/
|
|
129
|
-
mapPropToFieldNames<T extends object>(qb:
|
|
129
|
+
mapPropToFieldNames<T extends object>(qb: AnyQueryBuilder<T>, prop: EntityProperty<T>, tableAlias: string, meta: EntityMetadata<T>, schema?: string, explicitFields?: readonly InternalField<T>[]): InternalField<T>[];
|
|
130
130
|
/** @internal */
|
|
131
|
-
createQueryBuilder<T extends object>(entityName: EntityName<T> |
|
|
131
|
+
createQueryBuilder<T extends object>(entityName: EntityName<T> | AnyQueryBuilder<T>, ctx?: Transaction, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean, loggerContext?: LoggingOptions, alias?: string, em?: SqlEntityManager): AnyQueryBuilder<T>;
|
|
132
132
|
protected resolveConnectionType(args: {
|
|
133
133
|
ctx?: Transaction;
|
|
134
134
|
connectionType?: ConnectionType;
|
|
@@ -137,13 +137,19 @@ export declare abstract class AbstractSqlDriver<Connection extends AbstractSqlCo
|
|
|
137
137
|
protected processManyToMany<T extends object>(meta: EntityMetadata<T>, pks: Primary<T>[], collections: EntityData<T>, clear: boolean, options?: DriverMethodOptions): Promise<void>;
|
|
138
138
|
lockPessimistic<T extends object>(entity: T, options: LockOptions): Promise<void>;
|
|
139
139
|
protected buildPopulateWhere<T extends object>(meta: EntityMetadata<T>, joinedProps: PopulateOptions<T>[], options: Pick<FindOptions<any>, 'populateWhere'>): ObjectQuery<T>;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Builds a UNION ALL (or UNION) subquery from `unionWhere` branches and merges it
|
|
142
|
+
* into the main WHERE as `pk IN (branch_1 UNION ALL branch_2 ...)`.
|
|
143
|
+
* Each branch is planned independently by the database, enabling per-table index usage.
|
|
144
|
+
*/
|
|
145
|
+
protected applyUnionWhere<T extends object>(meta: EntityMetadata<T>, where: ObjectQuery<T>, options: FindOptions<T, any, any, any> | CountOptions<T> | NativeInsertUpdateOptions<T> | DeleteOptions<T>, forDml?: boolean): Promise<ObjectQuery<T>>;
|
|
146
|
+
protected buildOrderBy<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>): QueryOrderMap<T>[];
|
|
147
|
+
protected buildPopulateOrderBy<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, populateOrderBy: QueryOrderMap<T>[], parentPath: string, explicit: boolean, parentAlias?: string): QueryOrderMap<T>[];
|
|
148
|
+
protected buildJoinedPropsOrderBy<T extends object>(qb: AnyQueryBuilder<T>, meta: EntityMetadata<T>, populate: PopulateOptions<T>[], options?: Pick<FindOptions<any>, 'strategy' | 'orderBy' | 'populateOrderBy'>, parentPath?: string): QueryOrderMap<T>[];
|
|
143
149
|
private buildToManyOrderBy;
|
|
144
150
|
protected normalizeFields<T extends object>(fields: InternalField<T>[], prefix?: string): string[];
|
|
145
151
|
protected processField<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T> | undefined, field: string, ret: InternalField<T>[]): void;
|
|
146
|
-
protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb:
|
|
152
|
+
protected buildFields<T extends object>(meta: EntityMetadata<T>, populate: PopulateOptions<T>[], joinedProps: PopulateOptions<T>[], qb: AnyQueryBuilder<T>, alias: string, options: Pick<FindOptions<T, any, any, any>, 'strategy' | 'fields' | 'exclude'>, schema?: string): InternalField<T>[];
|
|
147
153
|
}
|
|
148
154
|
interface FieldsForJoinedLoadOptions<T extends object> {
|
|
149
155
|
explicitFields?: readonly InternalField<T>[];
|
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) {
|
|
@@ -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) {
|
|
@@ -297,7 +300,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
297
300
|
}
|
|
298
301
|
// Polymorphic to-one: iterate targets, find the matching one, build entity from its columns.
|
|
299
302
|
// Skip :ref hints — no JOINs were created, so the FK reference is already set by the result mapper.
|
|
300
|
-
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)) {
|
|
301
307
|
const basePath = parentJoinPath ? `${parentJoinPath}.${prop.name}` : `${meta.name}.${prop.name}`;
|
|
302
308
|
const pathPrefix = !parentJoinPath ? '[populate]' : '';
|
|
303
309
|
let matched = false;
|
|
@@ -306,16 +312,22 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
306
312
|
const relationAlias = qb.getAliasForJoinPath(targetPath, { matchPopulateJoins: true });
|
|
307
313
|
const meta2 = targetMeta;
|
|
308
314
|
const targetProps = meta2.props.filter(p => this.platform.shouldHaveColumn(p, hint.children || []));
|
|
309
|
-
const hasPK = meta2
|
|
315
|
+
const hasPK = meta2
|
|
316
|
+
.getPrimaryProps()
|
|
317
|
+
.every(pk => pk.fieldNames.every(name => root[`${relationAlias}__${name}`] != null));
|
|
310
318
|
if (hasPK && !matched) {
|
|
311
319
|
matched = true;
|
|
312
|
-
|
|
320
|
+
const relationPojo = {};
|
|
313
321
|
const tz = this.platform.getTimezone();
|
|
314
322
|
for (const p of targetProps) {
|
|
315
323
|
this.mapJoinedProp(relationPojo, p, relationAlias, root, tz, meta2);
|
|
316
324
|
}
|
|
317
325
|
// Inject the entity class constructor so that the factory creates the correct type
|
|
318
|
-
Object.defineProperty(relationPojo, 'constructor', {
|
|
326
|
+
Object.defineProperty(relationPojo, 'constructor', {
|
|
327
|
+
value: meta2.class,
|
|
328
|
+
enumerable: false,
|
|
329
|
+
configurable: true,
|
|
330
|
+
});
|
|
319
331
|
result[prop.name] = relationPojo;
|
|
320
332
|
const populateChildren = hint.children || [];
|
|
321
333
|
this.mapJoinedProps(relationPojo, meta2, populateChildren, qb, root, map, targetPath);
|
|
@@ -366,7 +378,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
366
378
|
return;
|
|
367
379
|
}
|
|
368
380
|
const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
|
|
369
|
-
const targetProps = mapToPk
|
|
381
|
+
const targetProps = mapToPk
|
|
382
|
+
? meta2.getPrimaryProps()
|
|
383
|
+
: meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
|
|
370
384
|
// If the primary key value for the relation is null, we know we haven't joined to anything
|
|
371
385
|
// and therefore we don't return any record (since all values would be null)
|
|
372
386
|
const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
|
|
@@ -459,7 +473,12 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
459
473
|
else if (prop.runtimeType === 'Date') {
|
|
460
474
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
461
475
|
const value = root[alias];
|
|
462
|
-
if (tz &&
|
|
476
|
+
if (tz &&
|
|
477
|
+
tz !== 'local' &&
|
|
478
|
+
typeof value === 'string' &&
|
|
479
|
+
!value.includes('+') &&
|
|
480
|
+
value.lastIndexOf('-') < 11 &&
|
|
481
|
+
!value.endsWith('Z')) {
|
|
463
482
|
relationPojo[prop.name] = this.platform.parseDate(value + tz);
|
|
464
483
|
}
|
|
465
484
|
else if (['string', 'number'].includes(typeof value)) {
|
|
@@ -475,10 +494,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
475
494
|
if (prop.kind === ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
|
|
476
495
|
const item = parseJsonSafe(relationPojo[prop.name]);
|
|
477
496
|
if (Array.isArray(item)) {
|
|
478
|
-
relationPojo[prop.name] = item.map(row =>
|
|
497
|
+
relationPojo[prop.name] = item.map(row => row == null ? row : this.comparator.mapResult(prop.targetMeta, row));
|
|
479
498
|
}
|
|
480
499
|
else {
|
|
481
|
-
relationPojo[prop.name] =
|
|
500
|
+
relationPojo[prop.name] =
|
|
501
|
+
item == null ? item : this.comparator.mapResult(prop.targetMeta, item);
|
|
482
502
|
}
|
|
483
503
|
}
|
|
484
504
|
}
|
|
@@ -493,6 +513,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
493
513
|
if (meta.virtual) {
|
|
494
514
|
return this.countVirtual(entityName, where, options);
|
|
495
515
|
}
|
|
516
|
+
if (options.unionWhere?.length) {
|
|
517
|
+
where = await this.applyUnionWhere(meta, where, options);
|
|
518
|
+
}
|
|
496
519
|
options = { populate: [], ...options };
|
|
497
520
|
const populate = options.populate;
|
|
498
521
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
@@ -557,13 +580,19 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
557
580
|
}
|
|
558
581
|
const tableName = this.getTableName(meta, options);
|
|
559
582
|
let sql = `insert into ${tableName} `;
|
|
560
|
-
sql +=
|
|
583
|
+
sql +=
|
|
584
|
+
fields.length > 0
|
|
585
|
+
? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')'
|
|
586
|
+
: `(${this.platform.quoteIdentifier(pks[0])})`;
|
|
561
587
|
if (this.platform.usesOutputStatement()) {
|
|
562
588
|
const returningProps = this.getTableProps(meta)
|
|
563
589
|
.filter(prop => (prop.persist !== false && prop.defaultRaw) || prop.autoincrement || prop.generated)
|
|
564
590
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
565
591
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
566
|
-
sql +=
|
|
592
|
+
sql +=
|
|
593
|
+
returningFields.length > 0
|
|
594
|
+
? ` output ${returningFields.map(field => 'inserted.' + this.platform.quoteIdentifier(field)).join(', ')}`
|
|
595
|
+
: '';
|
|
567
596
|
}
|
|
568
597
|
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
569
598
|
sql += ' values ';
|
|
@@ -650,7 +679,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
650
679
|
else {
|
|
651
680
|
const field = prop.fieldNames[0];
|
|
652
681
|
if (!duplicates.includes(field) || !usedDups.includes(field)) {
|
|
653
|
-
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])) {
|
|
654
687
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
655
688
|
}
|
|
656
689
|
else {
|
|
@@ -671,7 +704,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
671
704
|
.filter(prop => !(prop.name in data[0]) || isRaw(data[0][prop.name]));
|
|
672
705
|
const returningFields = Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
673
706
|
/* v8 ignore next */
|
|
674
|
-
sql +=
|
|
707
|
+
sql +=
|
|
708
|
+
returningFields.length > 0
|
|
709
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
710
|
+
: '';
|
|
675
711
|
}
|
|
676
712
|
if (transform) {
|
|
677
713
|
sql = transform(sql);
|
|
@@ -704,11 +740,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
704
740
|
/* v8 ignore next */
|
|
705
741
|
where = { [meta.primaryKeys[0] ?? pks[0]]: where };
|
|
706
742
|
}
|
|
743
|
+
if (!options.upsert && options.unionWhere?.length) {
|
|
744
|
+
where = (await this.applyUnionWhere(meta, where, options, true));
|
|
745
|
+
}
|
|
707
746
|
if (Utils.hasObjectKeys(data)) {
|
|
708
747
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
709
748
|
if (options.upsert) {
|
|
710
749
|
/* v8 ignore next */
|
|
711
|
-
const uniqueFields = options.onConflictFields ??
|
|
750
|
+
const uniqueFields = options.onConflictFields ??
|
|
751
|
+
(Utils.isPlainObject(where) ? Utils.keys(where) : meta.primaryKeys);
|
|
712
752
|
const returning = getOnConflictReturningFields(meta, data, uniqueFields, options);
|
|
713
753
|
qb.insert(data)
|
|
714
754
|
.onConflict(uniqueFields)
|
|
@@ -728,7 +768,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
728
768
|
qb.update(data).where(where);
|
|
729
769
|
// reload generated columns and version fields
|
|
730
770
|
const returning = [];
|
|
731
|
-
meta.props
|
|
771
|
+
meta.props
|
|
772
|
+
.filter(prop => (prop.generated && !prop.primary) || prop.version)
|
|
773
|
+
.forEach(prop => returning.push(prop.name));
|
|
732
774
|
qb.returning(returning);
|
|
733
775
|
}
|
|
734
776
|
res = await this.rethrow(qb.execute('run', false));
|
|
@@ -744,7 +786,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
744
786
|
const meta = this.metadata.get(entityName);
|
|
745
787
|
if (options.upsert) {
|
|
746
788
|
const uniqueFields = options.onConflictFields ??
|
|
747
|
-
(Utils.isPlainObject(where[0])
|
|
789
|
+
(Utils.isPlainObject(where[0])
|
|
790
|
+
? Object.keys(where[0]).flatMap(key => Utils.splitPrimaryKeys(key))
|
|
791
|
+
: meta.primaryKeys);
|
|
748
792
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes, options.loggerContext).withSchema(this.getSchemaName(meta, options));
|
|
749
793
|
const returning = getOnConflictReturningFields(meta, data[0], uniqueFields, options);
|
|
750
794
|
qb.insert(data)
|
|
@@ -815,7 +859,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
815
859
|
if (key in data[idx]) {
|
|
816
860
|
const pks = Utils.getOrderedPrimaryKeys(cond, meta);
|
|
817
861
|
sql += ` when (${pkCond}) then `;
|
|
818
|
-
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])) {
|
|
819
867
|
sql += prop.customType.convertToDatabaseValueSQL('?', this.platform);
|
|
820
868
|
}
|
|
821
869
|
else {
|
|
@@ -844,7 +892,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
844
892
|
sql = sql.substring(0, sql.length - 2) + ' where ';
|
|
845
893
|
const pkProps = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
|
|
846
894
|
const pks = Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
847
|
-
sql +=
|
|
895
|
+
sql +=
|
|
896
|
+
pks.length > 1
|
|
897
|
+
? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})`
|
|
898
|
+
: this.platform.quoteIdentifier(pks[0]);
|
|
848
899
|
const conds = where.map(cond => {
|
|
849
900
|
if (Utils.isPlainObject(cond) && Utils.getObjectKeysSize(cond) === 1) {
|
|
850
901
|
cond = Object.values(cond)[0];
|
|
@@ -867,7 +918,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
867
918
|
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
868
919
|
const returningFields = Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
869
920
|
/* v8 ignore next */
|
|
870
|
-
sql +=
|
|
921
|
+
sql +=
|
|
922
|
+
returningFields.length > 0
|
|
923
|
+
? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}`
|
|
924
|
+
: '';
|
|
871
925
|
}
|
|
872
926
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx, options.loggerContext));
|
|
873
927
|
for (let i = 0; i < collections.length; i++) {
|
|
@@ -881,6 +935,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
881
935
|
if (Utils.isPrimaryKey(where) && pks.length === 1) {
|
|
882
936
|
where = { [pks[0]]: where };
|
|
883
937
|
}
|
|
938
|
+
if (options.unionWhere?.length) {
|
|
939
|
+
where = await this.applyUnionWhere(meta, where, options, true);
|
|
940
|
+
}
|
|
884
941
|
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false, options.loggerContext)
|
|
885
942
|
.delete(where)
|
|
886
943
|
.withSchema(this.getSchemaName(meta, options));
|
|
@@ -935,7 +992,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
935
992
|
const qb = this.createQueryBuilder(coll.property.targetMeta.class, options?.ctx, 'write').withSchema(this.getSchemaName(meta, options));
|
|
936
993
|
if (coll.getSnapshot() === undefined) {
|
|
937
994
|
if (coll.property.orphanRemoval) {
|
|
938
|
-
const query = qb
|
|
995
|
+
const query = qb
|
|
996
|
+
.delete({ [coll.property.mappedBy]: pks })
|
|
997
|
+
.andWhere({ [cols.join(Utils.PK_SEPARATOR)]: { $nin: insertDiff } });
|
|
939
998
|
await this.rethrow(query.execute());
|
|
940
999
|
continue;
|
|
941
1000
|
}
|
|
@@ -947,7 +1006,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
947
1006
|
continue;
|
|
948
1007
|
}
|
|
949
1008
|
/* v8 ignore next */
|
|
950
|
-
const query = qb
|
|
1009
|
+
const query = qb
|
|
1010
|
+
.update({ [coll.property.mappedBy]: pks })
|
|
1011
|
+
.where({ [cols.join(Utils.PK_SEPARATOR)]: { $in: insertDiff } });
|
|
951
1012
|
await this.rethrow(query.execute());
|
|
952
1013
|
continue;
|
|
953
1014
|
}
|
|
@@ -960,7 +1021,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
960
1021
|
else {
|
|
961
1022
|
const targetMeta = coll.property.targetMeta;
|
|
962
1023
|
const targetSchema = (coll[0] ?? snap?.[0]) && helper(coll[0] ?? snap?.[0]).getSchema();
|
|
963
|
-
schema =
|
|
1024
|
+
schema =
|
|
1025
|
+
targetMeta.schema === '*'
|
|
1026
|
+
? (options?.schema ?? targetSchema ?? this.config.get('schema'))
|
|
1027
|
+
: targetMeta.schema;
|
|
964
1028
|
}
|
|
965
1029
|
}
|
|
966
1030
|
else if (schema == null) {
|
|
@@ -997,7 +1061,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
997
1061
|
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, options?.populate ?? [], options?.fields);
|
|
998
1062
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
999
1063
|
const childExclude = !Utils.isEmpty(options?.exclude) ? options.exclude.map(f => `${pivotProp1.name}.${f}`) : [];
|
|
1000
|
-
const fields = pivotJoin
|
|
1064
|
+
const fields = pivotJoin
|
|
1065
|
+
? [pivotProp1.name, pivotProp2.name]
|
|
1066
|
+
: [pivotProp1.name, pivotProp2.name, ...childFields];
|
|
1001
1067
|
const res = await this.find(pivotMeta.class, where, {
|
|
1002
1068
|
ctx,
|
|
1003
1069
|
...options,
|
|
@@ -1050,7 +1116,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1050
1116
|
const populateField = pivotJoin ? `${inverseProp.name}:ref` : inverseProp.name;
|
|
1051
1117
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1052
1118
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${inverseProp.name}.${f}`) : [];
|
|
1053
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1119
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1120
|
+
? options.exclude.map(f => `${inverseProp.name}.${f}`)
|
|
1121
|
+
: [];
|
|
1054
1122
|
const fields = pivotJoin
|
|
1055
1123
|
? [inverseProp.name, prop.discriminator, prop.discriminatorColumn]
|
|
1056
1124
|
: [inverseProp.name, prop.discriminator, prop.discriminatorColumn, ...childFields];
|
|
@@ -1060,7 +1128,15 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1060
1128
|
fields,
|
|
1061
1129
|
exclude: childExclude,
|
|
1062
1130
|
orderBy: this.getPivotOrderBy(prop, inverseProp, orderBy, options?.orderBy),
|
|
1063
|
-
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
|
+
],
|
|
1064
1140
|
populateWhere: undefined,
|
|
1065
1141
|
// @ts-ignore
|
|
1066
1142
|
_populateWhere: 'infer',
|
|
@@ -1092,7 +1168,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1092
1168
|
const populateField = ownerRelationName;
|
|
1093
1169
|
const populate = this.autoJoinOneToOneOwner(targetMeta, options?.populate ?? [], options?.fields);
|
|
1094
1170
|
const childFields = !Utils.isEmpty(options?.fields) ? options.fields.map(f => `${ownerRelationName}.${f}`) : [];
|
|
1095
|
-
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1171
|
+
const childExclude = !Utils.isEmpty(options?.exclude)
|
|
1172
|
+
? options.exclude.map(f => `${ownerRelationName}.${f}`)
|
|
1173
|
+
: [];
|
|
1096
1174
|
const fields = [ownerRelationName, tagProp.name, prop.discriminatorColumn, ...childFields];
|
|
1097
1175
|
const res = await this.find(pivotMeta.class, cond, {
|
|
1098
1176
|
ctx,
|
|
@@ -1100,7 +1178,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1100
1178
|
fields,
|
|
1101
1179
|
exclude: childExclude,
|
|
1102
1180
|
orderBy: this.getPivotOrderBy(prop, ownerProp, orderBy, options?.orderBy),
|
|
1103
|
-
populate: [
|
|
1181
|
+
populate: [
|
|
1182
|
+
{
|
|
1183
|
+
field: populateField,
|
|
1184
|
+
strategy: LoadStrategy.JOINED,
|
|
1185
|
+
joinType: JoinType.innerJoin,
|
|
1186
|
+
children: populate,
|
|
1187
|
+
},
|
|
1188
|
+
],
|
|
1104
1189
|
populateWhere: undefined,
|
|
1105
1190
|
// @ts-ignore
|
|
1106
1191
|
_populateWhere: 'infer',
|
|
@@ -1179,7 +1264,10 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1179
1264
|
}
|
|
1180
1265
|
const relationsToPopulate = populate.map(({ field }) => field.split(':')[0]);
|
|
1181
1266
|
const toPopulate = meta.relations
|
|
1182
|
-
.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))
|
|
1183
1271
|
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
1184
1272
|
.map(prop => ({ field: `${prop.name}:ref`, strategy: LoadStrategy.JOINED }));
|
|
1185
1273
|
return [...populate, ...toPopulate];
|
|
@@ -1295,8 +1383,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1295
1383
|
const prop = meta.properties[propName];
|
|
1296
1384
|
// Polymorphic to-one: create a LEFT JOIN per target type
|
|
1297
1385
|
// Skip :ref hints — polymorphic to-one already has FK + discriminator in the row
|
|
1298
|
-
if (prop.polymorphic &&
|
|
1299
|
-
|
|
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}`;
|
|
1300
1393
|
const pathPrefix = !options.parentJoinPath && populateWhereAll && !basePath.startsWith('[populate]') ? '[populate]' : '';
|
|
1301
1394
|
for (const targetMeta of prop.polymorphTargets) {
|
|
1302
1395
|
const tableAlias = qb.getNextAlias(targetMeta.className);
|
|
@@ -1314,7 +1407,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1314
1407
|
continue;
|
|
1315
1408
|
}
|
|
1316
1409
|
// ignore ref joins of known FKs unless it's a filter hint
|
|
1317
|
-
if (ref &&
|
|
1410
|
+
if (ref &&
|
|
1411
|
+
!hint.filter &&
|
|
1412
|
+
(prop.kind === ReferenceKind.MANY_TO_ONE || (prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner))) {
|
|
1318
1413
|
continue;
|
|
1319
1414
|
}
|
|
1320
1415
|
const meta2 = prop.targetMeta;
|
|
@@ -1360,7 +1455,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1360
1455
|
childExplicitFields.push(f.substring(prop.name.length + 1));
|
|
1361
1456
|
}
|
|
1362
1457
|
});
|
|
1363
|
-
const childExclude = options.exclude
|
|
1458
|
+
const childExclude = options.exclude
|
|
1459
|
+
? Utils.extractChildElements(options.exclude, prop.name)
|
|
1460
|
+
: options.exclude;
|
|
1364
1461
|
if (!ref && (!prop.mapToPk || hint.dataOnly)) {
|
|
1365
1462
|
fields.push(...this.getFieldsForJoinedLoad(qb, meta2, {
|
|
1366
1463
|
...options,
|
|
@@ -1371,7 +1468,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1371
1468
|
parentJoinPath: path,
|
|
1372
1469
|
}));
|
|
1373
1470
|
}
|
|
1374
|
-
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))) {
|
|
1375
1474
|
fields.push(...prop.referencedColumnNames.map(col => qb.helper.mapper(`${tableAlias}.${col}`, qb.type, undefined, `${tableAlias}__${col}`)));
|
|
1376
1475
|
}
|
|
1377
1476
|
}
|
|
@@ -1473,7 +1572,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1473
1572
|
if (childAlias) {
|
|
1474
1573
|
// Map fields using same filtering as joined loading, plus skip PKs
|
|
1475
1574
|
for (const prop of currentMeta.ownProps.filter(p => !p.primary && this.platform.shouldHaveColumn(p, []))) {
|
|
1476
|
-
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1575
|
+
this.mapJoinedProp(relationPojo, prop, childAlias, root, tz, currentMeta, {
|
|
1576
|
+
deleteFromRoot: true,
|
|
1577
|
+
});
|
|
1477
1578
|
}
|
|
1478
1579
|
}
|
|
1479
1580
|
currentMeta = currentMeta.tptParent;
|
|
@@ -1522,7 +1623,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1522
1623
|
/** @internal */
|
|
1523
1624
|
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, loggerContext, alias, em) {
|
|
1524
1625
|
// 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
|
|
1626
|
+
const connectionType = em
|
|
1627
|
+
? preferredConnectionType
|
|
1628
|
+
: this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
1526
1629
|
const qb = new QueryBuilder(entityName, this.metadata, this, ctx, alias, connectionType, em, loggerContext);
|
|
1527
1630
|
if (!convertCustomTypes) {
|
|
1528
1631
|
qb.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
@@ -1598,6 +1701,47 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1598
1701
|
/* v8 ignore next */
|
|
1599
1702
|
return { $and: [options.populateWhere, where] };
|
|
1600
1703
|
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Builds a UNION ALL (or UNION) subquery from `unionWhere` branches and merges it
|
|
1706
|
+
* into the main WHERE as `pk IN (branch_1 UNION ALL branch_2 ...)`.
|
|
1707
|
+
* Each branch is planned independently by the database, enabling per-table index usage.
|
|
1708
|
+
*/
|
|
1709
|
+
async applyUnionWhere(meta, where, options, forDml = false) {
|
|
1710
|
+
const unionWhere = options.unionWhere;
|
|
1711
|
+
const strategy = options.unionWhereStrategy ?? 'union-all';
|
|
1712
|
+
const schema = this.getSchemaName(meta, options);
|
|
1713
|
+
const connectionType = this.resolveConnectionType({
|
|
1714
|
+
ctx: options.ctx,
|
|
1715
|
+
connectionType: options.connectionType,
|
|
1716
|
+
});
|
|
1717
|
+
const branchQbs = [];
|
|
1718
|
+
for (const branch of unionWhere) {
|
|
1719
|
+
const qb = this.createQueryBuilder(meta.class, options.ctx, connectionType, false, options.logging).withSchema(schema);
|
|
1720
|
+
const pkFields = meta.primaryKeys.map(pk => {
|
|
1721
|
+
const prop = meta.properties[pk];
|
|
1722
|
+
return `${qb.alias}.${prop.fieldNames[0]}`;
|
|
1723
|
+
});
|
|
1724
|
+
qb.select(pkFields).where(branch);
|
|
1725
|
+
if (options.em) {
|
|
1726
|
+
await qb.applyJoinedFilters(options.em, options.filters);
|
|
1727
|
+
}
|
|
1728
|
+
branchQbs.push(qb);
|
|
1729
|
+
}
|
|
1730
|
+
const [first, ...rest] = branchQbs;
|
|
1731
|
+
const unionQb = strategy === 'union' ? first.union(...rest) : first.unionAll(...rest);
|
|
1732
|
+
const pkHash = Utils.getPrimaryKeyHash(meta.primaryKeys);
|
|
1733
|
+
// MySQL does not allow referencing the target table in a subquery
|
|
1734
|
+
// for UPDATE/DELETE, so we wrap the union in a derived table.
|
|
1735
|
+
if (forDml) {
|
|
1736
|
+
const { sql, params } = unionQb.toQuery();
|
|
1737
|
+
return {
|
|
1738
|
+
$and: [where, { [pkHash]: { $in: raw(`select * from (${sql}) as __u`, params) } }],
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
return {
|
|
1742
|
+
$and: [where, { [pkHash]: { $in: unionQb.toRaw() } }],
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1601
1745
|
buildOrderBy(qb, meta, populate, options) {
|
|
1602
1746
|
const joinedProps = this.joinedProps(meta, populate, options);
|
|
1603
1747
|
// `options._populateWhere` is a copy of the value provided by user with a fallback to the global config option
|
|
@@ -1628,7 +1772,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1628
1772
|
let path = parentPath;
|
|
1629
1773
|
const meta2 = prop.targetMeta;
|
|
1630
1774
|
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
1631
|
-
(![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))) {
|
|
1632
1778
|
path += `.${field}`;
|
|
1633
1779
|
}
|
|
1634
1780
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && typeof childOrder !== 'object') {
|
|
@@ -1639,7 +1785,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1639
1785
|
if (!join) {
|
|
1640
1786
|
continue;
|
|
1641
1787
|
}
|
|
1642
|
-
if (join &&
|
|
1788
|
+
if (join &&
|
|
1789
|
+
![ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind) &&
|
|
1790
|
+
typeof childOrder === 'object') {
|
|
1643
1791
|
const children = this.buildPopulateOrderBy(qb, meta2, Utils.asArray(childOrder), path, explicit, propAlias);
|
|
1644
1792
|
orderBy.push(...children);
|
|
1645
1793
|
continue;
|
|
@@ -1770,7 +1918,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
|
|
|
1770
1918
|
if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
|
|
1771
1919
|
ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
|
|
1772
1920
|
}
|
|
1773
|
-
if (meta.root.inheritanceType === 'sti' &&
|
|
1921
|
+
if (meta.root.inheritanceType === 'sti' &&
|
|
1922
|
+
!options.fields.includes(`${qb.alias}.${meta.root.discriminatorColumn}`)) {
|
|
1774
1923
|
ret.push(meta.root.discriminatorColumn);
|
|
1775
1924
|
}
|
|
1776
1925
|
}
|
package/AbstractSqlPlatform.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export declare abstract class AbstractSqlPlatform extends Platform {
|
|
|
27
27
|
getSearchJsonPropertySQL(path: string, type: string, aliased: boolean): string | RawQueryFragment;
|
|
28
28
|
getSearchJsonPropertyKey(path: string[], type: string, aliased: boolean, value?: unknown): string | RawQueryFragment;
|
|
29
29
|
getJsonIndexDefinition(index: IndexDef): string[];
|
|
30
|
+
supportsUnionWhere(): boolean;
|
|
30
31
|
supportsSchemas(): boolean;
|
|
31
32
|
/** @inheritDoc */
|
|
32
33
|
generateCustomOrder(escapedColumn: string, values: unknown[]): string;
|
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
|
}
|
|
@@ -80,6 +79,9 @@ export class AbstractSqlPlatform extends Platform {
|
|
|
80
79
|
return `(json_extract(${root}, '$.${path.join('.')}'))`;
|
|
81
80
|
});
|
|
82
81
|
}
|
|
82
|
+
supportsUnionWhere() {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
83
85
|
supportsSchemas() {
|
|
84
86
|
return false;
|
|
85
87
|
}
|