@mikro-orm/knex 6.0.0-dev.10 → 6.0.0-dev.100
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 +8 -4
- package/AbstractSqlConnection.js +30 -18
- package/AbstractSqlDriver.d.ts +22 -22
- package/AbstractSqlDriver.js +233 -88
- package/AbstractSqlPlatform.d.ts +5 -4
- package/AbstractSqlPlatform.js +34 -5
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +2 -6
- package/SqlEntityManager.js +5 -9
- package/SqlEntityRepository.d.ts +7 -6
- package/SqlEntityRepository.js +11 -8
- package/index.mjs +22 -6
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +3 -3
- package/query/CriteriaNode.d.ts +8 -8
- package/query/CriteriaNode.js +16 -9
- package/query/CriteriaNodeFactory.d.ts +6 -6
- package/query/CriteriaNodeFactory.js +10 -13
- package/query/ObjectCriteriaNode.d.ts +3 -3
- package/query/ObjectCriteriaNode.js +12 -8
- package/query/QueryBuilder.d.ts +41 -19
- package/query/QueryBuilder.js +319 -110
- package/query/QueryBuilderHelper.d.ts +9 -13
- package/query/QueryBuilderHelper.js +123 -97
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +9 -6
- package/query/enums.js +1 -1
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +26 -8
- package/schema/DatabaseTable.d.ts +4 -2
- package/schema/DatabaseTable.js +53 -28
- package/schema/SchemaComparator.js +12 -2
- package/schema/SchemaHelper.d.ts +6 -4
- package/schema/SchemaHelper.js +11 -4
- package/schema/SqlSchemaGenerator.d.ts +3 -6
- package/schema/SqlSchemaGenerator.js +61 -28
- package/typings.d.ts +12 -7
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type
|
|
3
|
-
import { LockMode } from '@mikro-orm/core';
|
|
2
|
+
import { LockMode, type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityProperty, type FlatQueryOrderMap, type QBFilterQuery } from '@mikro-orm/core';
|
|
4
3
|
import { QueryType } from './enums';
|
|
5
4
|
import type { Field, JoinOptions } from '../typings';
|
|
6
5
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
@@ -16,14 +15,13 @@ export declare class QueryBuilderHelper {
|
|
|
16
15
|
private readonly driver;
|
|
17
16
|
private readonly platform;
|
|
18
17
|
private readonly metadata;
|
|
19
|
-
constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias
|
|
18
|
+
constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias<any>>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
|
|
20
19
|
mapper(field: string | Knex.Raw, type?: QueryType): string;
|
|
21
20
|
mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
|
|
22
21
|
processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
|
|
23
|
-
joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
24
|
-
joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
25
|
-
joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string): Dictionary<JoinOptions>;
|
|
26
|
-
joinPivotTable(field: string, prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
22
|
+
joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary, schema?: string): JoinOptions;
|
|
23
|
+
joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary, schema?: string): JoinOptions;
|
|
24
|
+
joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string, schema?: string): Dictionary<JoinOptions>;
|
|
27
25
|
processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
|
|
28
26
|
private processJoinClause;
|
|
29
27
|
private wrapQueryGroup;
|
|
@@ -43,16 +41,14 @@ export declare class QueryBuilderHelper {
|
|
|
43
41
|
}[], qb: Knex.QueryBuilder): void;
|
|
44
42
|
appendQueryCondition(type: QueryType, cond: any, qb: Knex.QueryBuilder, operator?: '$and' | '$or', method?: 'where' | 'having'): void;
|
|
45
43
|
private appendQuerySubCondition;
|
|
46
|
-
private processCustomExpression;
|
|
47
44
|
private processObjectSubCondition;
|
|
48
45
|
private getOperatorReplacement;
|
|
49
46
|
getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
|
|
50
47
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
|
|
51
|
-
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
|
|
52
|
-
splitField(field:
|
|
48
|
+
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata, data?: Dictionary, returning?: Field<any>[]): void;
|
|
49
|
+
splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
|
|
53
50
|
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
|
|
54
51
|
updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
|
|
55
|
-
static isCustomExpression(field: string, hasAlias?: boolean): boolean;
|
|
56
52
|
private prefix;
|
|
57
53
|
private appendGroupCondition;
|
|
58
54
|
private isPrefixed;
|
|
@@ -61,9 +57,9 @@ export declare class QueryBuilderHelper {
|
|
|
61
57
|
isTableNameAliasRequired(type?: QueryType): boolean;
|
|
62
58
|
private mapData;
|
|
63
59
|
}
|
|
64
|
-
export interface Alias {
|
|
60
|
+
export interface Alias<T> {
|
|
65
61
|
aliasName: string;
|
|
66
62
|
entityName: string;
|
|
67
|
-
metadata?: EntityMetadata
|
|
63
|
+
metadata?: EntityMetadata<T>;
|
|
68
64
|
subQuery?: Knex.QueryBuilder;
|
|
69
65
|
}
|
|
@@ -8,6 +8,14 @@ const enums_1 = require("./enums");
|
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
10
|
class QueryBuilderHelper {
|
|
11
|
+
entityName;
|
|
12
|
+
alias;
|
|
13
|
+
aliasMap;
|
|
14
|
+
subQueries;
|
|
15
|
+
knex;
|
|
16
|
+
driver;
|
|
17
|
+
platform;
|
|
18
|
+
metadata;
|
|
11
19
|
constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
|
|
12
20
|
this.entityName = entityName;
|
|
13
21
|
this.alias = alias;
|
|
@@ -19,6 +27,10 @@ class QueryBuilderHelper {
|
|
|
19
27
|
this.metadata = this.driver.getMetadata();
|
|
20
28
|
}
|
|
21
29
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
30
|
+
if (core_1.Utils.isRawSql(field)) {
|
|
31
|
+
return this.knex.raw(field.sql, field.params);
|
|
32
|
+
}
|
|
33
|
+
/* istanbul ignore next */
|
|
22
34
|
if (typeof field !== 'string') {
|
|
23
35
|
return field;
|
|
24
36
|
}
|
|
@@ -36,12 +48,26 @@ class QueryBuilderHelper {
|
|
|
36
48
|
parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
|
|
37
49
|
}
|
|
38
50
|
}
|
|
51
|
+
// flatten the value if we see we are expanding nested composite key
|
|
52
|
+
// hackish, but cleaner solution would require quite a lot of refactoring
|
|
53
|
+
if (fields.length !== parts.length && Array.isArray(value)) {
|
|
54
|
+
value.forEach(row => {
|
|
55
|
+
if (Array.isArray(row)) {
|
|
56
|
+
const tmp = core_1.Utils.flatten(row);
|
|
57
|
+
row.length = 0;
|
|
58
|
+
row.push(...tmp);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
39
62
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
40
63
|
}
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
65
|
+
if (rawField) {
|
|
66
|
+
return this.knex.raw(rawField.sql, rawField.params);
|
|
67
|
+
}
|
|
43
68
|
const [a, f] = this.splitField(field);
|
|
44
69
|
const prop = this.getProperty(f, a);
|
|
70
|
+
let ret = field;
|
|
45
71
|
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
46
72
|
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
47
73
|
return this.alias + '.' + prop.fieldNames[0];
|
|
@@ -56,7 +82,7 @@ class QueryBuilderHelper {
|
|
|
56
82
|
const as = alias === null ? '' : ` as ${aliased}`;
|
|
57
83
|
return this.knex.raw(`${prop.formula(alias2)}${as}`);
|
|
58
84
|
}
|
|
59
|
-
if (prop?.
|
|
85
|
+
if (prop?.hasConvertToJSValueSQL) {
|
|
60
86
|
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
61
87
|
const valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
62
88
|
if (alias === null) {
|
|
@@ -65,15 +91,12 @@ class QueryBuilderHelper {
|
|
|
65
91
|
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
|
|
66
92
|
}
|
|
67
93
|
// do not wrap custom expressions
|
|
68
|
-
if (!
|
|
94
|
+
if (!rawField) {
|
|
69
95
|
ret = this.prefix(field);
|
|
70
96
|
}
|
|
71
97
|
if (alias) {
|
|
72
98
|
ret += ' as ' + alias;
|
|
73
99
|
}
|
|
74
|
-
if (customExpression) {
|
|
75
|
-
return this.knex.raw(ret, value);
|
|
76
|
-
}
|
|
77
100
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
78
101
|
return ret;
|
|
79
102
|
}
|
|
@@ -91,39 +114,40 @@ class QueryBuilderHelper {
|
|
|
91
114
|
}
|
|
92
115
|
return data;
|
|
93
116
|
}
|
|
94
|
-
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
117
|
+
joinOneToReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
95
118
|
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
96
119
|
const table = this.getTableName(prop.type);
|
|
97
|
-
const schema = this.driver.getSchemaName(prop.targetMeta);
|
|
98
120
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
99
121
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
100
122
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
123
|
+
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
101
124
|
return {
|
|
102
125
|
prop, type, cond, ownerAlias, alias, table, schema,
|
|
103
126
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
104
127
|
};
|
|
105
128
|
}
|
|
106
|
-
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
129
|
+
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
107
130
|
return {
|
|
108
131
|
prop, type, cond, ownerAlias, alias,
|
|
109
132
|
table: this.getTableName(prop.type),
|
|
110
|
-
schema: this.driver.getSchemaName(prop.targetMeta),
|
|
133
|
+
schema: this.driver.getSchemaName(prop.targetMeta, { schema }),
|
|
111
134
|
joinColumns: prop.referencedColumnNames,
|
|
112
135
|
primaryKeys: prop.fieldNames,
|
|
113
136
|
};
|
|
114
137
|
}
|
|
115
|
-
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
138
|
+
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
|
|
116
139
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
117
140
|
const ret = {
|
|
118
141
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
119
|
-
prop, type,
|
|
142
|
+
prop, type, ownerAlias,
|
|
120
143
|
alias: pivotAlias,
|
|
121
144
|
inverseAlias: alias,
|
|
122
145
|
joinColumns: prop.joinColumns,
|
|
123
146
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
124
147
|
primaryKeys: prop.referencedColumnNames,
|
|
148
|
+
cond: {},
|
|
125
149
|
table: pivotMeta.tableName,
|
|
126
|
-
schema: this.driver.getSchemaName(pivotMeta),
|
|
150
|
+
schema: this.driver.getSchemaName(pivotMeta, { schema }),
|
|
127
151
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
128
152
|
},
|
|
129
153
|
};
|
|
@@ -131,22 +155,10 @@ class QueryBuilderHelper {
|
|
|
131
155
|
return ret;
|
|
132
156
|
}
|
|
133
157
|
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
134
|
-
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
158
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
|
|
135
159
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
136
160
|
return ret;
|
|
137
161
|
}
|
|
138
|
-
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
139
|
-
const pivotMeta = this.metadata.find(field);
|
|
140
|
-
const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
141
|
-
return {
|
|
142
|
-
prop, type, cond, ownerAlias, alias,
|
|
143
|
-
table: pivotMeta.collection,
|
|
144
|
-
schema: pivotMeta.schema,
|
|
145
|
-
joinColumns: prop.joinColumns,
|
|
146
|
-
inverseJoinColumns: prop2.joinColumns,
|
|
147
|
-
primaryKeys: prop.referencedColumnNames,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
162
|
processJoins(qb, joins, schema) {
|
|
151
163
|
Object.values(joins).forEach(join => {
|
|
152
164
|
let table = `${join.table} as ${join.alias}`;
|
|
@@ -162,6 +174,11 @@ class QueryBuilderHelper {
|
|
|
162
174
|
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
163
175
|
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
164
176
|
});
|
|
177
|
+
if (join.prop.targetMeta.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
178
|
+
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
179
|
+
const alias = join.inverseAlias ?? join.alias;
|
|
180
|
+
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
|
|
181
|
+
}
|
|
165
182
|
Object.keys(join.cond).forEach(key => {
|
|
166
183
|
conditions.push(this.processJoinClause(key, join.cond[key], params));
|
|
167
184
|
});
|
|
@@ -212,20 +229,14 @@ class QueryBuilderHelper {
|
|
|
212
229
|
if (operator === '$exists') {
|
|
213
230
|
value = null;
|
|
214
231
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const { sql, bindings } = k.toSQL().toNative();
|
|
223
|
-
if (params2.length > 0) {
|
|
224
|
-
params.push(...bindings);
|
|
225
|
-
params.push(...params2);
|
|
226
|
-
return sql + ' = ?';
|
|
232
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
233
|
+
if (rawField) {
|
|
234
|
+
let sql = rawField.sql;
|
|
235
|
+
params.push(...rawField.params);
|
|
236
|
+
params.push(...core_1.Utils.asArray(value));
|
|
237
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
238
|
+
sql += ' = ?';
|
|
227
239
|
}
|
|
228
|
-
params.push(...bindings);
|
|
229
240
|
return sql;
|
|
230
241
|
}
|
|
231
242
|
const sql = this.mapper(key);
|
|
@@ -241,7 +252,7 @@ class QueryBuilderHelper {
|
|
|
241
252
|
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
242
253
|
}
|
|
243
254
|
mapJoinColumns(type, join) {
|
|
244
|
-
if (join.prop && join.prop.
|
|
255
|
+
if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
|
|
245
256
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
246
257
|
return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
|
|
247
258
|
});
|
|
@@ -254,7 +265,7 @@ class QueryBuilderHelper {
|
|
|
254
265
|
isOneToOneInverse(field) {
|
|
255
266
|
const meta = this.metadata.find(this.entityName);
|
|
256
267
|
const prop = meta.properties[field];
|
|
257
|
-
return prop && prop.
|
|
268
|
+
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
258
269
|
}
|
|
259
270
|
getTableName(entityName) {
|
|
260
271
|
const meta = this.metadata.find(entityName);
|
|
@@ -293,13 +304,13 @@ class QueryBuilderHelper {
|
|
|
293
304
|
}
|
|
294
305
|
appendOnConflictClause(type, onConflict, qb) {
|
|
295
306
|
onConflict.forEach(item => {
|
|
296
|
-
const sub = qb.onConflict(item.fields);
|
|
307
|
+
const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
|
|
297
308
|
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
298
309
|
core_1.Utils.runIfNotEmpty(() => {
|
|
299
310
|
let mergeParam = item.merge;
|
|
300
311
|
if (core_1.Utils.isObject(item.merge)) {
|
|
301
312
|
mergeParam = {};
|
|
302
|
-
|
|
313
|
+
core_1.Utils.keys(item.merge).forEach(key => {
|
|
303
314
|
const k = this.mapper(key, type);
|
|
304
315
|
mergeParam[k] = item.merge[key];
|
|
305
316
|
});
|
|
@@ -330,34 +341,29 @@ class QueryBuilderHelper {
|
|
|
330
341
|
}
|
|
331
342
|
appendQuerySubCondition(qb, type, method, cond, key, operator) {
|
|
332
343
|
const m = operator === '$or' ? 'orWhere' : method;
|
|
344
|
+
if (cond[key] instanceof core_1.RawQueryFragment) {
|
|
345
|
+
cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
|
|
346
|
+
}
|
|
333
347
|
if (this.isSimpleRegExp(cond[key])) {
|
|
334
348
|
return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
|
|
335
349
|
}
|
|
336
350
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
337
351
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
338
352
|
}
|
|
339
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
340
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
341
|
-
}
|
|
342
353
|
const op = cond[key] === null ? 'is' : '=';
|
|
354
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
355
|
+
if (raw) {
|
|
356
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
357
|
+
if (value.length > 0) {
|
|
358
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
359
|
+
}
|
|
360
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
361
|
+
}
|
|
343
362
|
if (this.subQueries[key]) {
|
|
344
363
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
345
364
|
}
|
|
346
365
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
347
366
|
}
|
|
348
|
-
processCustomExpression(clause, m, key, cond, type) {
|
|
349
|
-
// unwind parameters when ? found in field name
|
|
350
|
-
const count = key.concat('?').match(/\?/g).length - 1;
|
|
351
|
-
const value = core_1.Utils.asArray(cond[key]);
|
|
352
|
-
const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
353
|
-
const params2 = value.slice(count);
|
|
354
|
-
const k = this.mapper(key, type, params1);
|
|
355
|
-
if (params2.length > 0) {
|
|
356
|
-
const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
|
|
357
|
-
return void clause[m](k, val);
|
|
358
|
-
}
|
|
359
|
-
clause[m](k);
|
|
360
|
-
}
|
|
361
367
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
362
368
|
// grouped condition for one field
|
|
363
369
|
let value = cond[key];
|
|
@@ -376,7 +382,8 @@ class QueryBuilderHelper {
|
|
|
376
382
|
const replacement = this.getOperatorReplacement(op, value);
|
|
377
383
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
378
384
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
379
|
-
value[op]
|
|
385
|
+
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
386
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
|
|
380
387
|
}
|
|
381
388
|
if (this.subQueries[key]) {
|
|
382
389
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
@@ -394,7 +401,8 @@ class QueryBuilderHelper {
|
|
|
394
401
|
}));
|
|
395
402
|
}
|
|
396
403
|
else {
|
|
397
|
-
|
|
404
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
405
|
+
qb[m](mappedKey, replacement, value[op]);
|
|
398
406
|
}
|
|
399
407
|
}
|
|
400
408
|
getOperatorReplacement(op, value) {
|
|
@@ -422,36 +430,58 @@ class QueryBuilderHelper {
|
|
|
422
430
|
}
|
|
423
431
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
424
432
|
const ret = [];
|
|
425
|
-
Object.keys(orderBy).forEach(
|
|
426
|
-
const direction = orderBy[
|
|
433
|
+
Object.keys(orderBy).forEach(key => {
|
|
434
|
+
const direction = orderBy[key];
|
|
427
435
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
428
|
-
|
|
429
|
-
|
|
436
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
437
|
+
if (raw) {
|
|
438
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
430
439
|
return;
|
|
431
440
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const prop = this.getProperty(
|
|
437
|
-
const noPrefix = (prop && prop.persist === false && !prop.formula) ||
|
|
438
|
-
const column = this.mapper(noPrefix ?
|
|
441
|
+
core_1.Utils.splitPrimaryKeys(key).forEach(f => {
|
|
442
|
+
// eslint-disable-next-line prefer-const
|
|
443
|
+
let [alias, field] = this.splitField(f, true);
|
|
444
|
+
alias = populate[alias] || alias;
|
|
445
|
+
const prop = this.getProperty(field, alias);
|
|
446
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
447
|
+
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
439
448
|
/* istanbul ignore next */
|
|
440
449
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
441
450
|
const customOrder = prop?.customOrder;
|
|
442
|
-
|
|
451
|
+
let colPart = customOrder
|
|
443
452
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
444
453
|
: rawColumn;
|
|
454
|
+
if (core_1.Utils.isRawSql(colPart)) {
|
|
455
|
+
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
456
|
+
}
|
|
445
457
|
ret.push(`${colPart} ${order.toLowerCase()}`);
|
|
446
458
|
});
|
|
447
459
|
});
|
|
448
460
|
return ret.join(', ');
|
|
449
461
|
}
|
|
450
|
-
finalize(type, qb, meta) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
462
|
+
finalize(type, qb, meta, data, returning) {
|
|
463
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
// always respect explicit returning hint
|
|
467
|
+
if (returning && returning.length > 0) {
|
|
468
|
+
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
472
|
+
const returningProps = meta.hydrateProps
|
|
473
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
474
|
+
.filter(prop => !(prop.name in data));
|
|
475
|
+
if (returningProps.length > 0) {
|
|
476
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
481
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
482
|
+
if (returningProps.length > 0) {
|
|
483
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
484
|
+
}
|
|
455
485
|
}
|
|
456
486
|
}
|
|
457
487
|
splitField(field, greedyAlias = false) {
|
|
@@ -489,23 +519,18 @@ class QueryBuilderHelper {
|
|
|
489
519
|
}
|
|
490
520
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
491
521
|
let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
492
|
-
if (versionProperty.
|
|
522
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
493
523
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
494
524
|
}
|
|
495
525
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
496
526
|
}
|
|
497
|
-
static isCustomExpression(field, hasAlias = false) {
|
|
498
|
-
// if we do not have alias, we don't consider spaces as custom expressions
|
|
499
|
-
const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
|
|
500
|
-
return !!field.match(re);
|
|
501
|
-
}
|
|
502
527
|
prefix(field, always = false, quote = false) {
|
|
503
528
|
let ret;
|
|
504
529
|
if (!this.isPrefixed(field)) {
|
|
505
530
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
506
531
|
const fieldName = this.fieldName(field, this.alias, always);
|
|
507
|
-
if (
|
|
508
|
-
return fieldName;
|
|
532
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
533
|
+
return fieldName.sql;
|
|
509
534
|
}
|
|
510
535
|
ret = alias + fieldName;
|
|
511
536
|
}
|
|
@@ -545,15 +570,15 @@ class QueryBuilderHelper {
|
|
|
545
570
|
}
|
|
546
571
|
if (prop.fieldNameRaw) {
|
|
547
572
|
if (!always) {
|
|
548
|
-
return prop.fieldNameRaw
|
|
549
|
-
.replace(
|
|
550
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
573
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
574
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
575
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
551
576
|
}
|
|
552
577
|
if (alias) {
|
|
553
|
-
return prop.fieldNameRaw.replace(
|
|
578
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
554
579
|
}
|
|
555
580
|
/* istanbul ignore next */
|
|
556
|
-
return prop.fieldNameRaw;
|
|
581
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
557
582
|
}
|
|
558
583
|
/* istanbul ignore next */
|
|
559
584
|
return prop.fieldNames?.[0] ?? field;
|
|
@@ -564,7 +589,7 @@ class QueryBuilderHelper {
|
|
|
564
589
|
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
565
590
|
if (alias && meta) {
|
|
566
591
|
const prop = meta.properties[alias];
|
|
567
|
-
if (prop?.
|
|
592
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
568
593
|
// we want to select the full object property so hydration works as expected
|
|
569
594
|
if (prop.object) {
|
|
570
595
|
return prop;
|
|
@@ -596,17 +621,18 @@ class QueryBuilderHelper {
|
|
|
596
621
|
return;
|
|
597
622
|
}
|
|
598
623
|
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
599
|
-
const copy = data[k];
|
|
624
|
+
const copy = core_1.Utils.flatten(data[k]);
|
|
600
625
|
delete data[k];
|
|
601
626
|
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
602
627
|
return;
|
|
603
628
|
}
|
|
604
629
|
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
605
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
|
|
630
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
|
|
606
631
|
}
|
|
607
|
-
if (prop.
|
|
632
|
+
if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
|
|
608
633
|
const quoted = this.platform.quoteValue(data[k]);
|
|
609
|
-
|
|
634
|
+
const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
|
|
635
|
+
data[k] = this.knex.raw(sql.replace(/\?/g, '\\?'));
|
|
610
636
|
}
|
|
611
637
|
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
612
638
|
data[k] = JSON.stringify(data[k]);
|
|
@@ -3,7 +3,8 @@ import type { IQueryBuilder } from '../typings';
|
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
|
-
export declare class ScalarCriteriaNode extends CriteriaNode {
|
|
7
|
-
process
|
|
6
|
+
export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
+
process(qb: IQueryBuilder<T>, alias?: string): any;
|
|
8
|
+
willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
|
|
8
9
|
shouldJoin(): boolean;
|
|
9
10
|
}
|
|
@@ -13,23 +13,26 @@ class ScalarCriteriaNode extends CriteriaNode_1.CriteriaNode {
|
|
|
13
13
|
const parentPath = this.parent.getPath(); // the parent is always there, otherwise `shouldJoin` would return `false`
|
|
14
14
|
const nestedAlias = qb.getAliasForJoinPath(path) || qb.getNextAlias(this.prop?.pivotTable ?? this.entityName);
|
|
15
15
|
const field = `${alias}.${this.prop.name}`;
|
|
16
|
-
const type = this.prop.
|
|
16
|
+
const type = this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY ? 'pivotJoin' : 'leftJoin';
|
|
17
17
|
qb.join(field, nestedAlias, undefined, type, path);
|
|
18
18
|
// select the owner as virtual property when joining from 1:1 inverse side, but only if the parent is root entity
|
|
19
|
-
if (this.prop.
|
|
19
|
+
if (this.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !parentPath.includes('.')) {
|
|
20
20
|
qb.addSelect(field);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
return this.payload;
|
|
24
24
|
}
|
|
25
|
+
willAutoJoin(qb, alias) {
|
|
26
|
+
return this.shouldJoin();
|
|
27
|
+
}
|
|
25
28
|
shouldJoin() {
|
|
26
29
|
if (!this.parent || !this.prop) {
|
|
27
30
|
return false;
|
|
28
31
|
}
|
|
29
|
-
switch (this.prop.
|
|
30
|
-
case core_1.
|
|
31
|
-
case core_1.
|
|
32
|
-
case core_1.
|
|
32
|
+
switch (this.prop.kind) {
|
|
33
|
+
case core_1.ReferenceKind.ONE_TO_MANY: return true;
|
|
34
|
+
case core_1.ReferenceKind.MANY_TO_MANY: return true;
|
|
35
|
+
case core_1.ReferenceKind.ONE_TO_ONE: return !this.prop.owner;
|
|
33
36
|
default: return false; // SCALAR, MANY_TO_ONE
|
|
34
37
|
}
|
|
35
38
|
}
|
package/query/enums.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Configuration, type Dictionary, type EntityMetadata } from '@mikro-orm/core';
|
|
2
2
|
import { DatabaseTable } from './DatabaseTable';
|
|
3
3
|
import type { AbstractSqlConnection } from '../AbstractSqlConnection';
|
|
4
4
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform';
|
|
@@ -10,14 +10,17 @@ export declare class DatabaseSchema {
|
|
|
10
10
|
readonly name: string;
|
|
11
11
|
private tables;
|
|
12
12
|
private namespaces;
|
|
13
|
+
private nativeEnums;
|
|
13
14
|
constructor(platform: AbstractSqlPlatform, name: string);
|
|
14
15
|
addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
|
|
15
16
|
getTables(): DatabaseTable[];
|
|
16
17
|
getTable(name: string): DatabaseTable | undefined;
|
|
17
18
|
hasTable(name: string): boolean;
|
|
19
|
+
setNativeEnums(nativeEnums: Dictionary<unknown[]>): void;
|
|
20
|
+
getNativeEnums(): Dictionary<unknown[]>;
|
|
18
21
|
hasNamespace(namespace: string): boolean;
|
|
19
22
|
getNamespaces(): string[];
|
|
20
|
-
static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): Promise<DatabaseSchema>;
|
|
23
|
+
static create(connection: AbstractSqlConnection, platform: AbstractSqlPlatform, config: Configuration, schemaName?: string, schemas?: string[]): Promise<DatabaseSchema>;
|
|
21
24
|
static fromMetadata(metadata: EntityMetadata[], platform: AbstractSqlPlatform, config: Configuration, schemaName?: string): DatabaseSchema;
|
|
22
25
|
private static getSchemaName;
|
|
23
26
|
private static shouldHaveColumn;
|