@mikro-orm/sql 7.0.0-dev.321 → 7.0.0-dev.322
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/AbstractSqlDriver.js +4 -4
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +4 -4
- package/package.json +2 -2
- package/plugin/index.d.ts +1 -3
- package/plugin/index.js +12 -12
- package/plugin/transformer.d.ts +5 -20
- package/plugin/transformer.js +81 -73
- package/query/ObjectCriteriaNode.js +3 -3
- package/query/QueryBuilder.d.ts +57 -51
- package/query/QueryBuilder.js +339 -368
- package/query/QueryBuilderHelper.d.ts +1 -8
- package/query/QueryBuilderHelper.js +97 -97
- package/query/ScalarCriteriaNode.js +3 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +50 -33
- package/schema/DatabaseTable.d.ts +7 -5
- package/schema/DatabaseTable.js +77 -59
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +18 -18
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +4 -1
|
@@ -7,14 +7,7 @@ import type { NativeQueryBuilder } from './NativeQueryBuilder.js';
|
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
9
|
export declare class QueryBuilderHelper {
|
|
10
|
-
private
|
|
11
|
-
private alias;
|
|
12
|
-
private readonly aliasMap;
|
|
13
|
-
private readonly subQueries;
|
|
14
|
-
private readonly driver;
|
|
15
|
-
private readonly tptAliasMap;
|
|
16
|
-
private readonly platform;
|
|
17
|
-
private readonly metadata;
|
|
10
|
+
#private;
|
|
18
11
|
constructor(entityName: EntityName, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, driver: AbstractSqlDriver, tptAliasMap?: Dictionary<string>);
|
|
19
12
|
/**
|
|
20
13
|
* For TPT inheritance, finds the correct alias for a property based on which entity owns it.
|
|
@@ -4,30 +4,30 @@ import { JoinType, QueryType } from './enums.js';
|
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
export class QueryBuilderHelper {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
#platform;
|
|
8
|
+
#metadata;
|
|
9
|
+
#entityName;
|
|
10
|
+
#alias;
|
|
11
|
+
#aliasMap;
|
|
12
|
+
#subQueries;
|
|
13
|
+
#driver;
|
|
14
|
+
#tptAliasMap;
|
|
15
15
|
constructor(entityName, alias, aliasMap, subQueries, driver, tptAliasMap = {}) {
|
|
16
|
-
this
|
|
17
|
-
this
|
|
18
|
-
this
|
|
19
|
-
this
|
|
20
|
-
this
|
|
21
|
-
this
|
|
22
|
-
this
|
|
23
|
-
this
|
|
16
|
+
this.#entityName = entityName;
|
|
17
|
+
this.#alias = alias;
|
|
18
|
+
this.#aliasMap = aliasMap;
|
|
19
|
+
this.#subQueries = subQueries;
|
|
20
|
+
this.#driver = driver;
|
|
21
|
+
this.#tptAliasMap = tptAliasMap;
|
|
22
|
+
this.#platform = this.#driver.getPlatform();
|
|
23
|
+
this.#metadata = this.#driver.getMetadata();
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* For TPT inheritance, finds the correct alias for a property based on which entity owns it.
|
|
27
27
|
* Returns the main alias if not a TPT property or if the property belongs to the main entity.
|
|
28
28
|
*/
|
|
29
29
|
getTPTAliasForProperty(propName, defaultAlias) {
|
|
30
|
-
const meta = this
|
|
30
|
+
const meta = this.#aliasMap[defaultAlias]?.meta ?? this.#metadata.get(this.#entityName);
|
|
31
31
|
if (meta?.inheritanceType !== 'tpt' || !meta.tptParent) {
|
|
32
32
|
return defaultAlias;
|
|
33
33
|
}
|
|
@@ -38,7 +38,7 @@ export class QueryBuilderHelper {
|
|
|
38
38
|
// Walk up the TPT hierarchy to find which parent owns this property
|
|
39
39
|
let parentMeta = meta.tptParent;
|
|
40
40
|
while (parentMeta) {
|
|
41
|
-
const parentAlias = this
|
|
41
|
+
const parentAlias = this.#tptAliasMap[parentMeta.className];
|
|
42
42
|
if (parentAlias && parentMeta.ownProps?.some(p => p.name === propName || p.fieldNames?.includes(propName))) {
|
|
43
43
|
return parentAlias;
|
|
44
44
|
}
|
|
@@ -67,13 +67,13 @@ export class QueryBuilderHelper {
|
|
|
67
67
|
const prop = this.getProperty(f, a);
|
|
68
68
|
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
69
69
|
if (fkIdx2 !== -1) {
|
|
70
|
-
parts.push(this.mapper(a !== this
|
|
70
|
+
parts.push(this.mapper(a !== this.#alias ? `${a}.${prop.fieldNames[fkIdx2]}` : prop.fieldNames[fkIdx2], type, value, alias));
|
|
71
71
|
}
|
|
72
72
|
else if (prop) {
|
|
73
|
-
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this
|
|
73
|
+
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.#alias ? `${a}.${f}` : f, type, value, alias)));
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
-
parts.push(this.mapper(a !== this
|
|
76
|
+
parts.push(this.mapper(a !== this.#alias ? `${a}.${f}` : f, type, value, alias));
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// flatten the value if we see we are expanding nested composite key
|
|
@@ -87,16 +87,16 @@ export class QueryBuilderHelper {
|
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
-
return raw('(' + parts.map(part => this
|
|
90
|
+
return raw('(' + parts.map(part => this.#platform.quoteIdentifier(part)).join(', ') + ')');
|
|
91
91
|
}
|
|
92
92
|
const [a, f] = this.splitField(field);
|
|
93
93
|
const prop = this.getProperty(f, a);
|
|
94
94
|
// For TPT inheritance, resolve the correct alias for this property
|
|
95
95
|
// Only apply TPT resolution when `a` is an actual table alias (in aliasMap),
|
|
96
96
|
// not when it's an embedded property name like 'profile1.identity.links'
|
|
97
|
-
const isTableAlias = !!this
|
|
98
|
-
const baseAlias = isTableAlias ? a : this
|
|
99
|
-
const resolvedAlias = isTableAlias ? this.getTPTAliasForProperty(prop?.name ?? f, a) : this
|
|
97
|
+
const isTableAlias = !!this.#aliasMap[a];
|
|
98
|
+
const baseAlias = isTableAlias ? a : this.#alias;
|
|
99
|
+
const resolvedAlias = isTableAlias ? this.getTPTAliasForProperty(prop?.name ?? f, a) : this.#alias;
|
|
100
100
|
const aliasPrefix = isTableNameAliasRequired ? resolvedAlias + '.' : '';
|
|
101
101
|
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
102
102
|
const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
|
|
@@ -108,13 +108,13 @@ export class QueryBuilderHelper {
|
|
|
108
108
|
return raw(this.prefix(field, isTableNameAliasRequired));
|
|
109
109
|
}
|
|
110
110
|
if (prop?.formula) {
|
|
111
|
-
const alias2 = this
|
|
111
|
+
const alias2 = this.#platform.quoteIdentifier(a).toString();
|
|
112
112
|
const aliasName = alias === undefined ? prop.fieldNames[0] : alias;
|
|
113
|
-
const as = aliasName === null ? '' : ` as ${this
|
|
114
|
-
const meta = this
|
|
113
|
+
const as = aliasName === null ? '' : ` as ${this.#platform.quoteIdentifier(aliasName)}`;
|
|
114
|
+
const meta = this.#aliasMap[a]?.meta ?? this.#metadata.get(this.#entityName);
|
|
115
115
|
const table = this.createFormulaTable(alias2, meta, schema);
|
|
116
116
|
const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, a), alias2);
|
|
117
|
-
let value = this
|
|
117
|
+
let value = this.#driver.evaluateFormula(prop.formula, columns, table);
|
|
118
118
|
if (!this.isTableNameAliasRequired(type)) {
|
|
119
119
|
value = value.replaceAll(alias2 + '.', '');
|
|
120
120
|
}
|
|
@@ -125,16 +125,16 @@ export class QueryBuilderHelper {
|
|
|
125
125
|
if (prop.fieldNames.length > 1 && fkIdx !== -1) {
|
|
126
126
|
const fk = prop.targetMeta.getPrimaryProps()[fkIdx];
|
|
127
127
|
const prefixed = this.prefix(field, isTableNameAliasRequired, true, fkIdx);
|
|
128
|
-
valueSQL = fk.customType.convertToJSValueSQL(prefixed, this
|
|
128
|
+
valueSQL = fk.customType.convertToJSValueSQL(prefixed, this.#platform);
|
|
129
129
|
}
|
|
130
130
|
else {
|
|
131
131
|
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
132
|
-
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this
|
|
132
|
+
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.#platform);
|
|
133
133
|
}
|
|
134
134
|
if (alias === null) {
|
|
135
135
|
return raw(valueSQL);
|
|
136
136
|
}
|
|
137
|
-
return raw(`${valueSQL} as ${this
|
|
137
|
+
return raw(`${valueSQL} as ${this.#platform.quoteIdentifier(alias ?? prop.fieldNames[fkIdx])}`);
|
|
138
138
|
}
|
|
139
139
|
let ret = this.prefix(field, false, false, fkIdx);
|
|
140
140
|
if (alias) {
|
|
@@ -149,11 +149,11 @@ export class QueryBuilderHelper {
|
|
|
149
149
|
if (Array.isArray(data)) {
|
|
150
150
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
151
151
|
}
|
|
152
|
-
const meta = this
|
|
153
|
-
data = this
|
|
152
|
+
const meta = this.#metadata.find(this.#entityName);
|
|
153
|
+
data = this.#driver.mapDataToFieldNames(data, true, meta?.properties, convertCustomTypes);
|
|
154
154
|
if (!Utils.hasObjectKeys(data) && meta && multi) {
|
|
155
155
|
/* v8 ignore next */
|
|
156
|
-
data[meta.getPrimaryProps()[0].fieldNames[0]] = this
|
|
156
|
+
data[meta.getPrimaryProps()[0].fieldNames[0]] = this.#platform.usesDefaultKeyword() ? raw('default') : undefined;
|
|
157
157
|
}
|
|
158
158
|
return data;
|
|
159
159
|
}
|
|
@@ -163,11 +163,11 @@ export class QueryBuilderHelper {
|
|
|
163
163
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
164
164
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
165
165
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
166
|
-
schema ??= prop.targetMeta?.schema === '*' ? '*' : this
|
|
166
|
+
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.#driver.getSchemaName(prop.targetMeta);
|
|
167
167
|
cond = Utils.merge(cond, prop.where);
|
|
168
168
|
// For inverse side of polymorphic relations, add discriminator condition
|
|
169
169
|
if (!prop.owner && prop2.polymorphic && prop2.discriminatorColumn && prop2.discriminatorMap) {
|
|
170
|
-
const ownerMeta = this
|
|
170
|
+
const ownerMeta = this.#aliasMap[ownerAlias]?.meta ?? this.#metadata.get(this.#entityName);
|
|
171
171
|
const discriminatorValue = QueryHelper.findDiscriminatorValue(prop2.discriminatorMap, ownerMeta.class);
|
|
172
172
|
if (discriminatorValue) {
|
|
173
173
|
cond[`${alias}.${prop2.discriminatorColumn}`] = discriminatorValue;
|
|
@@ -194,7 +194,7 @@ export class QueryBuilderHelper {
|
|
|
194
194
|
ownerAlias,
|
|
195
195
|
alias,
|
|
196
196
|
table: this.getTableName(prop.targetMeta.class),
|
|
197
|
-
schema: prop.targetMeta?.schema === '*' ? '*' : this
|
|
197
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.#driver.getSchemaName(prop.targetMeta, { schema }),
|
|
198
198
|
joinColumns: prop.referencedColumnNames,
|
|
199
199
|
// For polymorphic relations, fieldNames includes the discriminator column which is not
|
|
200
200
|
// part of the join condition - use joinColumns (the FK columns only) instead
|
|
@@ -202,7 +202,7 @@ export class QueryBuilderHelper {
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
|
|
205
|
-
const pivotMeta = this
|
|
205
|
+
const pivotMeta = this.#metadata.find(prop.pivotEntity);
|
|
206
206
|
const ret = {
|
|
207
207
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
208
208
|
prop,
|
|
@@ -215,7 +215,7 @@ export class QueryBuilderHelper {
|
|
|
215
215
|
primaryKeys: prop.referencedColumnNames,
|
|
216
216
|
cond: {},
|
|
217
217
|
table: pivotMeta.tableName,
|
|
218
|
-
schema: prop.targetMeta?.schema === '*' ? '*' : this
|
|
218
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.#driver.getSchemaName(pivotMeta, { schema }),
|
|
219
219
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
220
220
|
},
|
|
221
221
|
};
|
|
@@ -248,25 +248,25 @@ export class QueryBuilderHelper {
|
|
|
248
248
|
const conditions = [];
|
|
249
249
|
const params = [];
|
|
250
250
|
schema = join.schema === '*' ? schema : (join.schema ?? schemaOverride);
|
|
251
|
-
if (schema && schema !== this
|
|
251
|
+
if (schema && schema !== this.#platform.getDefaultSchemaName()) {
|
|
252
252
|
table = `${schema}.${table}`;
|
|
253
253
|
}
|
|
254
254
|
if (join.prop.name !== '__subquery__') {
|
|
255
255
|
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
256
256
|
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
257
257
|
if (join.prop.formula) {
|
|
258
|
-
const quotedAlias = this
|
|
259
|
-
const ownerMeta = this
|
|
258
|
+
const quotedAlias = this.#platform.quoteIdentifier(join.ownerAlias).toString();
|
|
259
|
+
const ownerMeta = this.#aliasMap[join.ownerAlias]?.meta ?? this.#metadata.get(this.#entityName);
|
|
260
260
|
const table = this.createFormulaTable(quotedAlias, ownerMeta, schema);
|
|
261
261
|
const columns = ownerMeta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, join.ownerAlias), quotedAlias);
|
|
262
|
-
const left = this
|
|
263
|
-
conditions.push(`${left} = ${this
|
|
262
|
+
const left = this.#driver.evaluateFormula(join.prop.formula, columns, table);
|
|
263
|
+
conditions.push(`${left} = ${this.#platform.quoteIdentifier(right)}`);
|
|
264
264
|
return;
|
|
265
265
|
}
|
|
266
266
|
const left = join.prop.object && join.prop.fieldNameRaw
|
|
267
267
|
? join.prop.fieldNameRaw.replaceAll(ALIAS_REPLACEMENT, join.ownerAlias)
|
|
268
|
-
: this
|
|
269
|
-
conditions.push(`${left} = ${this
|
|
268
|
+
: this.#platform.quoteIdentifier(`${join.ownerAlias}.${primaryKey}`);
|
|
269
|
+
conditions.push(`${left} = ${this.#platform.quoteIdentifier(right)}`);
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
if (join.prop.targetMeta?.root.inheritanceType === 'sti' &&
|
|
@@ -280,15 +280,15 @@ export class QueryBuilderHelper {
|
|
|
280
280
|
if (join.prop.polymorphic && join.prop.discriminatorColumn && join.prop.discriminatorMap) {
|
|
281
281
|
const discriminatorValue = QueryHelper.findDiscriminatorValue(join.prop.discriminatorMap, join.prop.targetMeta.class);
|
|
282
282
|
if (discriminatorValue) {
|
|
283
|
-
const discriminatorCol = this
|
|
283
|
+
const discriminatorCol = this.#platform.quoteIdentifier(`${join.ownerAlias}.${join.prop.discriminatorColumn}`);
|
|
284
284
|
conditions.push(`${discriminatorCol} = ?`);
|
|
285
285
|
params.push(discriminatorValue);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
let sql = method + ' ';
|
|
289
289
|
if (join.nested) {
|
|
290
|
-
const asKeyword = this
|
|
291
|
-
sql += `(${this
|
|
290
|
+
const asKeyword = this.#platform.usesAsKeyword() ? ' as ' : ' ';
|
|
291
|
+
sql += `(${this.#platform.quoteIdentifier(table)}${asKeyword}${this.#platform.quoteIdentifier(join.alias)}`;
|
|
292
292
|
for (const nested of join.nested) {
|
|
293
293
|
const { sql: nestedSql, params: nestedParams } = this.createJoinExpression(nested, joins, schema, schemaOverride);
|
|
294
294
|
sql += ' ' + nestedSql;
|
|
@@ -297,19 +297,19 @@ export class QueryBuilderHelper {
|
|
|
297
297
|
sql += `)`;
|
|
298
298
|
}
|
|
299
299
|
else if (join.subquery) {
|
|
300
|
-
const asKeyword = this
|
|
301
|
-
sql += `(${join.subquery})${asKeyword}${this
|
|
300
|
+
const asKeyword = this.#platform.usesAsKeyword() ? ' as ' : ' ';
|
|
301
|
+
sql += `(${join.subquery})${asKeyword}${this.#platform.quoteIdentifier(join.alias)}`;
|
|
302
302
|
}
|
|
303
303
|
else {
|
|
304
304
|
sql +=
|
|
305
|
-
this
|
|
306
|
-
(this
|
|
307
|
-
this
|
|
305
|
+
this.#platform.quoteIdentifier(table) +
|
|
306
|
+
(this.#platform.usesAsKeyword() ? ' as ' : ' ') +
|
|
307
|
+
this.#platform.quoteIdentifier(join.alias);
|
|
308
308
|
}
|
|
309
|
-
const oldAlias = this
|
|
310
|
-
this
|
|
309
|
+
const oldAlias = this.#alias;
|
|
310
|
+
this.#alias = join.alias;
|
|
311
311
|
const subquery = this._appendQueryCondition(QueryType.SELECT, join.cond);
|
|
312
|
-
this
|
|
312
|
+
this.#alias = oldAlias;
|
|
313
313
|
if (subquery.sql) {
|
|
314
314
|
conditions.push(subquery.sql);
|
|
315
315
|
subquery.params.forEach(p => params.push(p));
|
|
@@ -332,12 +332,12 @@ export class QueryBuilderHelper {
|
|
|
332
332
|
];
|
|
333
333
|
}
|
|
334
334
|
isOneToOneInverse(field, meta) {
|
|
335
|
-
meta ??= this
|
|
335
|
+
meta ??= this.#metadata.find(this.#entityName);
|
|
336
336
|
const prop = meta.properties[field.replace(/:ref$/, '')];
|
|
337
337
|
return prop?.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
338
338
|
}
|
|
339
339
|
getTableName(entityName) {
|
|
340
|
-
const meta = this
|
|
340
|
+
const meta = this.#metadata.find(entityName);
|
|
341
341
|
return meta?.tableName ?? Utils.className(entityName);
|
|
342
342
|
}
|
|
343
343
|
/**
|
|
@@ -432,7 +432,7 @@ export class QueryBuilderHelper {
|
|
|
432
432
|
const parts = [];
|
|
433
433
|
const params = [];
|
|
434
434
|
if (this.isSimpleRegExp(cond[key])) {
|
|
435
|
-
parts.push(`${this
|
|
435
|
+
parts.push(`${this.#platform.quoteIdentifier(this.mapper(key, type))} like ?`);
|
|
436
436
|
params.push(this.getRegExpParam(cond[key]));
|
|
437
437
|
return { sql: parts.join(' and '), params };
|
|
438
438
|
}
|
|
@@ -442,7 +442,7 @@ export class QueryBuilderHelper {
|
|
|
442
442
|
const op = cond[key] === null ? 'is' : '=';
|
|
443
443
|
if (Raw.isKnownFragmentSymbol(key)) {
|
|
444
444
|
const raw = Raw.getKnownFragment(key);
|
|
445
|
-
const sql = raw.sql.replaceAll(ALIAS_REPLACEMENT, this
|
|
445
|
+
const sql = raw.sql.replaceAll(ALIAS_REPLACEMENT, this.#alias);
|
|
446
446
|
const value = Utils.asArray(cond[key]);
|
|
447
447
|
params.push(...raw.params);
|
|
448
448
|
if (value.length > 0) {
|
|
@@ -455,13 +455,13 @@ export class QueryBuilderHelper {
|
|
|
455
455
|
return { sql: parts.join(' and '), params };
|
|
456
456
|
}
|
|
457
457
|
const fields = Utils.splitPrimaryKeys(key);
|
|
458
|
-
if (this
|
|
458
|
+
if (this.#subQueries[key]) {
|
|
459
459
|
const val = this.getValueReplacement(fields, cond[key], params, key);
|
|
460
|
-
parts.push(`(${this
|
|
460
|
+
parts.push(`(${this.#subQueries[key]}) ${op} ${val}`);
|
|
461
461
|
return { sql: parts.join(' and '), params };
|
|
462
462
|
}
|
|
463
463
|
const val = this.getValueReplacement(fields, cond[key], params, key);
|
|
464
|
-
parts.push(`${this
|
|
464
|
+
parts.push(`${this.#platform.quoteIdentifier(this.mapper(key, type, cond[key], null))} ${op} ${val}`);
|
|
465
465
|
return { sql: parts.join(' and '), params };
|
|
466
466
|
}
|
|
467
467
|
processObjectSubCondition(cond, key, type) {
|
|
@@ -483,7 +483,7 @@ export class QueryBuilderHelper {
|
|
|
483
483
|
return { sql: parts.join(' and '), params };
|
|
484
484
|
}
|
|
485
485
|
if (value instanceof RegExp) {
|
|
486
|
-
value = this
|
|
486
|
+
value = this.#platform.getRegExpValue(value);
|
|
487
487
|
}
|
|
488
488
|
// operators
|
|
489
489
|
const op = Object.keys(QueryOperator).find(op => op in value);
|
|
@@ -496,17 +496,17 @@ export class QueryBuilderHelper {
|
|
|
496
496
|
const fields = rawField ? [key] : Utils.splitPrimaryKeys(key);
|
|
497
497
|
if (fields.length > 1 && Array.isArray(value[op])) {
|
|
498
498
|
const singleTuple = !value[op].every((v) => Array.isArray(v));
|
|
499
|
-
if (!this
|
|
499
|
+
if (!this.#platform.allowsComparingTuples()) {
|
|
500
500
|
const mapped = fields.map(f => this.mapper(f, type));
|
|
501
501
|
if (op === '$in') {
|
|
502
502
|
const conds = value[op].map(() => {
|
|
503
|
-
return `(${mapped.map(field => `${this
|
|
503
|
+
return `(${mapped.map(field => `${this.#platform.quoteIdentifier(field)} = ?`).join(' and ')})`;
|
|
504
504
|
});
|
|
505
505
|
parts.push(`(${conds.join(' or ')})`);
|
|
506
506
|
params.push(...Utils.flatten(value[op]));
|
|
507
507
|
return { sql: parts.join(' and '), params };
|
|
508
508
|
}
|
|
509
|
-
parts.push(...mapped.map(field => `${this
|
|
509
|
+
parts.push(...mapped.map(field => `${this.#platform.quoteIdentifier(field)} = ?`));
|
|
510
510
|
params.push(...Utils.flatten(value[op]));
|
|
511
511
|
return { sql: parts.join(' and '), params };
|
|
512
512
|
}
|
|
@@ -516,9 +516,9 @@ export class QueryBuilderHelper {
|
|
|
516
516
|
value[op] = raw(sql, tmp);
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
|
-
if (this
|
|
519
|
+
if (this.#subQueries[key]) {
|
|
520
520
|
const val = this.getValueReplacement(fields, value[op], params, op);
|
|
521
|
-
parts.push(`(${this
|
|
521
|
+
parts.push(`(${this.#subQueries[key]}) ${replacement} ${val}`);
|
|
522
522
|
return { sql: parts.join(' and '), params };
|
|
523
523
|
}
|
|
524
524
|
const [a, f] = rawField ? [] : this.splitField(key);
|
|
@@ -531,7 +531,7 @@ export class QueryBuilderHelper {
|
|
|
531
531
|
if (!prop) {
|
|
532
532
|
throw new Error(`Cannot use $fulltext operator on ${String(key)}, property not found`);
|
|
533
533
|
}
|
|
534
|
-
const { sql, params: params2 } = raw(this
|
|
534
|
+
const { sql, params: params2 } = raw(this.#platform.getFullTextWhereClause(prop), {
|
|
535
535
|
column: this.mapper(key, type, undefined, null),
|
|
536
536
|
query: value[op],
|
|
537
537
|
});
|
|
@@ -543,7 +543,7 @@ export class QueryBuilderHelper {
|
|
|
543
543
|
}
|
|
544
544
|
else if (op === '$re') {
|
|
545
545
|
const mappedKey = this.mapper(key, type, value[op], null);
|
|
546
|
-
const processed = this
|
|
546
|
+
const processed = this.#platform.mapRegExpCondition(mappedKey, value);
|
|
547
547
|
parts.push(processed.sql);
|
|
548
548
|
params.push(...processed.params);
|
|
549
549
|
}
|
|
@@ -554,13 +554,13 @@ export class QueryBuilderHelper {
|
|
|
554
554
|
if (['$in', '$nin'].includes(op)) {
|
|
555
555
|
sql = `(${sql})`;
|
|
556
556
|
}
|
|
557
|
-
parts.push(`${this
|
|
557
|
+
parts.push(`${this.#platform.quoteIdentifier(mappedKey)} ${replacement} ${sql}`);
|
|
558
558
|
params.push(...query.params);
|
|
559
559
|
}
|
|
560
560
|
else {
|
|
561
561
|
const mappedKey = this.mapper(key, type, value[op], null);
|
|
562
562
|
const val = this.getValueReplacement(fields, value[op], params, op, prop);
|
|
563
|
-
parts.push(`${this
|
|
563
|
+
parts.push(`${this.#platform.quoteIdentifier(mappedKey)} ${replacement} ${val}`);
|
|
564
564
|
}
|
|
565
565
|
return { sql: parts.join(' and '), params };
|
|
566
566
|
}
|
|
@@ -575,7 +575,7 @@ export class QueryBuilderHelper {
|
|
|
575
575
|
return `(${tmp.join(', ')})`;
|
|
576
576
|
}
|
|
577
577
|
if (prop?.customType instanceof ArrayType) {
|
|
578
|
-
const item = prop.customType.convertToDatabaseValue(value, this
|
|
578
|
+
const item = prop.customType.convertToDatabaseValue(value, this.#platform, {
|
|
579
579
|
fromQuery: true,
|
|
580
580
|
key,
|
|
581
581
|
mode: 'query',
|
|
@@ -603,7 +603,7 @@ export class QueryBuilderHelper {
|
|
|
603
603
|
replacement = op === '$eq' ? 'is' : 'is not';
|
|
604
604
|
}
|
|
605
605
|
if (op === '$re') {
|
|
606
|
-
replacement = this
|
|
606
|
+
replacement = this.#platform.getRegExpOperator(value[op], value.$flags);
|
|
607
607
|
}
|
|
608
608
|
if (replacement.includes('?')) {
|
|
609
609
|
replacement = replacement.replaceAll('?', '\\?');
|
|
@@ -648,7 +648,7 @@ export class QueryBuilderHelper {
|
|
|
648
648
|
const order = typeof direction === 'number' ? QueryOrderNumeric[direction] : direction;
|
|
649
649
|
if (Raw.isKnownFragmentSymbol(key)) {
|
|
650
650
|
const raw = Raw.getKnownFragment(key);
|
|
651
|
-
ret.push(...this
|
|
651
|
+
ret.push(...this.#platform.getOrderByExpression(this.#platform.formatQuery(raw.sql, raw.params), order, collation));
|
|
652
652
|
continue;
|
|
653
653
|
}
|
|
654
654
|
for (const f of Utils.splitPrimaryKeys(key)) {
|
|
@@ -662,19 +662,19 @@ export class QueryBuilderHelper {
|
|
|
662
662
|
const rawColumn = typeof column === 'string'
|
|
663
663
|
? column
|
|
664
664
|
.split('.')
|
|
665
|
-
.map(e => this
|
|
665
|
+
.map(e => this.#platform.quoteIdentifier(e))
|
|
666
666
|
.join('.')
|
|
667
667
|
: column;
|
|
668
668
|
const customOrder = prop?.customOrder;
|
|
669
|
-
let colPart = customOrder ? this
|
|
669
|
+
let colPart = customOrder ? this.#platform.generateCustomOrder(rawColumn, customOrder) : rawColumn;
|
|
670
670
|
if (isRaw(colPart)) {
|
|
671
|
-
colPart = this
|
|
671
|
+
colPart = this.#platform.formatQuery(colPart.sql, colPart.params);
|
|
672
672
|
}
|
|
673
673
|
if (Array.isArray(order)) {
|
|
674
674
|
order.forEach(part => ret.push(...this.getQueryOrderFromObject(type, part, populate, collation)));
|
|
675
675
|
}
|
|
676
676
|
else {
|
|
677
|
-
ret.push(...this
|
|
677
|
+
ret.push(...this.#platform.getOrderByExpression(colPart, order, collation));
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
}
|
|
@@ -687,7 +687,7 @@ export class QueryBuilderHelper {
|
|
|
687
687
|
parts[parts.length - 1] = parts[parts.length - 1].substring(0, parts[parts.length - 1].indexOf(':'));
|
|
688
688
|
}
|
|
689
689
|
if (parts.length === 1) {
|
|
690
|
-
return [this
|
|
690
|
+
return [this.#alias, parts[0], ref];
|
|
691
691
|
}
|
|
692
692
|
if (greedyAlias) {
|
|
693
693
|
const fromField = parts.pop();
|
|
@@ -699,28 +699,28 @@ export class QueryBuilderHelper {
|
|
|
699
699
|
return [fromAlias, fromField, ref];
|
|
700
700
|
}
|
|
701
701
|
getLockSQL(qb, lockMode, lockTables = [], joinsMap) {
|
|
702
|
-
const meta = this
|
|
702
|
+
const meta = this.#metadata.find(this.#entityName);
|
|
703
703
|
if (lockMode === LockMode.OPTIMISTIC && meta && !meta.versionProperty) {
|
|
704
|
-
throw OptimisticLockError.lockFailed(Utils.className(this
|
|
704
|
+
throw OptimisticLockError.lockFailed(Utils.className(this.#entityName));
|
|
705
705
|
}
|
|
706
706
|
if (lockMode !== LockMode.OPTIMISTIC && lockTables.length === 0 && joinsMap) {
|
|
707
707
|
const joins = Object.values(joinsMap);
|
|
708
708
|
const innerJoins = joins.filter(join => [JoinType.innerJoin, JoinType.innerJoinLateral, JoinType.nestedInnerJoin].includes(join.type));
|
|
709
709
|
if (joins.length > innerJoins.length) {
|
|
710
|
-
lockTables.push(this
|
|
710
|
+
lockTables.push(this.#alias, ...innerJoins.map(join => join.alias));
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
713
|
qb.lockMode(lockMode, lockTables);
|
|
714
714
|
}
|
|
715
715
|
updateVersionProperty(qb, data) {
|
|
716
|
-
const meta = this
|
|
716
|
+
const meta = this.#metadata.find(this.#entityName);
|
|
717
717
|
if (!meta?.versionProperty || meta.versionProperty in data) {
|
|
718
718
|
return;
|
|
719
719
|
}
|
|
720
720
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
721
|
-
let sql = this
|
|
721
|
+
let sql = this.#platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
722
722
|
if (versionProperty.runtimeType === 'Date') {
|
|
723
|
-
sql = this
|
|
723
|
+
sql = this.#platform.getCurrentTimestampSQL(versionProperty.length);
|
|
724
724
|
}
|
|
725
725
|
qb.update({ [versionProperty.fieldNames[0]]: raw(sql) });
|
|
726
726
|
}
|
|
@@ -728,8 +728,8 @@ export class QueryBuilderHelper {
|
|
|
728
728
|
let ret;
|
|
729
729
|
if (!this.isPrefixed(field)) {
|
|
730
730
|
// For TPT inheritance, resolve the correct alias for this property
|
|
731
|
-
const tptAlias = this.getTPTAliasForProperty(field, this
|
|
732
|
-
const alias = always ? (quote ? tptAlias : this
|
|
731
|
+
const tptAlias = this.getTPTAliasForProperty(field, this.#alias);
|
|
732
|
+
const alias = always ? (quote ? tptAlias : this.#platform.quoteIdentifier(tptAlias)) + '.' : '';
|
|
733
733
|
const fieldName = this.fieldName(field, tptAlias, always, idx);
|
|
734
734
|
if (fieldName instanceof Raw) {
|
|
735
735
|
return fieldName.sql;
|
|
@@ -742,7 +742,7 @@ export class QueryBuilderHelper {
|
|
|
742
742
|
// For TPT inheritance, resolve the correct alias for this property
|
|
743
743
|
// Only apply TPT resolution when `a` is an actual table alias (in aliasMap),
|
|
744
744
|
// not when it's an embedded property name like 'profile1.identity.links'
|
|
745
|
-
const isTableAlias = !!this
|
|
745
|
+
const isTableAlias = !!this.#aliasMap[a];
|
|
746
746
|
const resolvedAlias = isTableAlias ? this.getTPTAliasForProperty(f, a) : a;
|
|
747
747
|
const fieldName = this.fieldName(f, resolvedAlias, always, idx);
|
|
748
748
|
if (fieldName instanceof Raw) {
|
|
@@ -751,7 +751,7 @@ export class QueryBuilderHelper {
|
|
|
751
751
|
ret = resolvedAlias + '.' + fieldName;
|
|
752
752
|
}
|
|
753
753
|
if (quote) {
|
|
754
|
-
return this
|
|
754
|
+
return this.#platform.quoteIdentifier(ret);
|
|
755
755
|
}
|
|
756
756
|
return ret;
|
|
757
757
|
}
|
|
@@ -792,7 +792,7 @@ export class QueryBuilderHelper {
|
|
|
792
792
|
if (!always) {
|
|
793
793
|
return raw(prop.fieldNameRaw
|
|
794
794
|
.replace(new RegExp(ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
795
|
-
.replace(this
|
|
795
|
+
.replace(this.#platform.quoteIdentifier('') + '.', ''));
|
|
796
796
|
}
|
|
797
797
|
if (alias) {
|
|
798
798
|
return raw(prop.fieldNameRaw.replace(new RegExp(ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
@@ -804,8 +804,8 @@ export class QueryBuilderHelper {
|
|
|
804
804
|
return prop.fieldNames?.[idx] ?? field;
|
|
805
805
|
}
|
|
806
806
|
getProperty(field, alias) {
|
|
807
|
-
const entityName = this
|
|
808
|
-
const meta = this
|
|
807
|
+
const entityName = this.#aliasMap[alias]?.entityName || this.#entityName;
|
|
808
|
+
const meta = this.#metadata.find(entityName);
|
|
809
809
|
// raw table name (e.g. CTE) — no metadata available
|
|
810
810
|
if (!meta) {
|
|
811
811
|
return undefined;
|
|
@@ -828,7 +828,7 @@ export class QueryBuilderHelper {
|
|
|
828
828
|
return [QueryType.SELECT, QueryType.COUNT].includes(type);
|
|
829
829
|
}
|
|
830
830
|
processOnConflictCondition(cond, schema) {
|
|
831
|
-
const meta = this
|
|
831
|
+
const meta = this.#metadata.get(this.#entityName);
|
|
832
832
|
const tableName = meta.tableName;
|
|
833
833
|
for (const key of Object.keys(cond)) {
|
|
834
834
|
const mapped = this.mapper(key, QueryType.UPSERT);
|
|
@@ -18,7 +18,9 @@ export class ScalarCriteriaNode extends CriteriaNode {
|
|
|
18
18
|
const type = this.prop.kind === ReferenceKind.MANY_TO_MANY ? JoinType.pivotJoin : JoinType.leftJoin;
|
|
19
19
|
qb.join(field, nestedAlias, undefined, type, path);
|
|
20
20
|
// select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity
|
|
21
|
-
if (this.prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
21
|
+
if (this.prop.kind === ReferenceKind.ONE_TO_ONE &&
|
|
22
|
+
!parentPath.includes('.') &&
|
|
23
|
+
!qb.state.fields?.includes(field)) {
|
|
22
24
|
qb.addSelect(field);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -7,19 +7,21 @@ import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
|
7
7
|
* @internal
|
|
8
8
|
*/
|
|
9
9
|
export declare class DatabaseSchema {
|
|
10
|
-
private
|
|
10
|
+
#private;
|
|
11
11
|
readonly name: string;
|
|
12
|
-
private tables;
|
|
13
|
-
private views;
|
|
14
|
-
private namespaces;
|
|
15
|
-
private nativeEnums;
|
|
16
12
|
constructor(platform: AbstractSqlPlatform, name: string);
|
|
17
13
|
addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
|
|
18
14
|
getTables(): DatabaseTable[];
|
|
15
|
+
/** @internal */
|
|
16
|
+
setTables(tables: DatabaseTable[]): void;
|
|
17
|
+
/** @internal */
|
|
18
|
+
setNamespaces(namespaces: Set<string>): void;
|
|
19
19
|
getTable(name: string): DatabaseTable | undefined;
|
|
20
20
|
hasTable(name: string): boolean;
|
|
21
21
|
addView(name: string, schema: string | undefined | null, definition: string, materialized?: boolean, withData?: boolean): DatabaseView;
|
|
22
22
|
getViews(): DatabaseView[];
|
|
23
|
+
/** @internal */
|
|
24
|
+
setViews(views: DatabaseView[]): void;
|
|
23
25
|
getView(name: string): DatabaseView | undefined;
|
|
24
26
|
hasView(name: string): boolean;
|
|
25
27
|
setNativeEnums(nativeEnums: Dictionary<{
|