@mikro-orm/knex 6.0.0-dev.11 → 6.0.0-dev.111
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 +16 -17
- package/AbstractSqlDriver.js +283 -118
- 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 +39 -19
- package/query/QueryBuilder.js +331 -111
- package/query/QueryBuilderHelper.d.ts +9 -13
- package/query/QueryBuilderHelper.js +153 -107
- 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 +55 -30
- 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 +13 -7
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type
|
|
3
|
-
import { LockMode } from '@mikro-orm/core';
|
|
2
|
+
import { type Dictionary, type EntityData, type EntityKey, type EntityMetadata, type EntityProperty, type FlatQueryOrderMap, LockMode, 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,41 +155,54 @@ 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
|
-
let table =
|
|
164
|
+
let table = join.table;
|
|
153
165
|
const method = join.type === 'innerJoin' ? 'inner join' : 'left join';
|
|
166
|
+
const conditions = [];
|
|
167
|
+
const params = [];
|
|
154
168
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
155
169
|
if (schema) {
|
|
156
170
|
table = `${schema}.${table}`;
|
|
157
171
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
if (!join.subquery) {
|
|
173
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
174
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
175
|
+
if (join.prop.formula) {
|
|
176
|
+
const left = join.prop.formula(join.ownerAlias);
|
|
177
|
+
conditions.push(`${left} = ${this.knex.ref(right)}`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const left = join.prop.kind === core_1.ReferenceKind.SCALAR && join.prop.persist === false ? primaryKey : `${join.ownerAlias}.${primaryKey}`;
|
|
181
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
185
|
+
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
186
|
+
const alias = join.inverseAlias ?? join.alias;
|
|
187
|
+
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
|
|
188
|
+
}
|
|
165
189
|
Object.keys(join.cond).forEach(key => {
|
|
166
|
-
|
|
190
|
+
const needsPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
|
|
191
|
+
const newKey = needsPrefix ? key : `${join.alias}.${key}`;
|
|
192
|
+
conditions.push(this.processJoinClause(newKey, join.cond[key], params));
|
|
167
193
|
});
|
|
168
|
-
|
|
194
|
+
let sql = method + ' ';
|
|
195
|
+
if (join.subquery) {
|
|
196
|
+
sql += `(${join.subquery})`;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
sql += this.knex.ref(table);
|
|
200
|
+
}
|
|
201
|
+
sql += ` as ${this.knex.ref(join.alias)}`;
|
|
202
|
+
if (conditions.length > 0) {
|
|
203
|
+
sql += ` on ${conditions.join(' and ')}`;
|
|
204
|
+
}
|
|
205
|
+
return qb.joinRaw(sql, params);
|
|
169
206
|
});
|
|
170
207
|
}
|
|
171
208
|
processJoinClause(key, value, params, operator = '$eq') {
|
|
@@ -212,20 +249,14 @@ class QueryBuilderHelper {
|
|
|
212
249
|
if (operator === '$exists') {
|
|
213
250
|
value = null;
|
|
214
251
|
}
|
|
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 + ' = ?';
|
|
252
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
253
|
+
if (rawField) {
|
|
254
|
+
let sql = rawField.sql;
|
|
255
|
+
params.push(...rawField.params);
|
|
256
|
+
params.push(...core_1.Utils.asArray(value));
|
|
257
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
258
|
+
sql += ' = ?';
|
|
227
259
|
}
|
|
228
|
-
params.push(...bindings);
|
|
229
260
|
return sql;
|
|
230
261
|
}
|
|
231
262
|
const sql = this.mapper(key);
|
|
@@ -241,7 +272,7 @@ class QueryBuilderHelper {
|
|
|
241
272
|
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
242
273
|
}
|
|
243
274
|
mapJoinColumns(type, join) {
|
|
244
|
-
if (join.prop && join.prop.
|
|
275
|
+
if (join.prop && join.prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !join.prop.owner) {
|
|
245
276
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
246
277
|
return this.mapper(`${join.alias}.${join.inverseJoinColumns[idx]}`, type, undefined, fieldName);
|
|
247
278
|
});
|
|
@@ -254,7 +285,7 @@ class QueryBuilderHelper {
|
|
|
254
285
|
isOneToOneInverse(field) {
|
|
255
286
|
const meta = this.metadata.find(this.entityName);
|
|
256
287
|
const prop = meta.properties[field];
|
|
257
|
-
return prop && prop.
|
|
288
|
+
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
258
289
|
}
|
|
259
290
|
getTableName(entityName) {
|
|
260
291
|
const meta = this.metadata.find(entityName);
|
|
@@ -293,13 +324,13 @@ class QueryBuilderHelper {
|
|
|
293
324
|
}
|
|
294
325
|
appendOnConflictClause(type, onConflict, qb) {
|
|
295
326
|
onConflict.forEach(item => {
|
|
296
|
-
const sub = qb.onConflict(item.fields);
|
|
327
|
+
const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
|
|
297
328
|
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
298
329
|
core_1.Utils.runIfNotEmpty(() => {
|
|
299
330
|
let mergeParam = item.merge;
|
|
300
331
|
if (core_1.Utils.isObject(item.merge)) {
|
|
301
332
|
mergeParam = {};
|
|
302
|
-
|
|
333
|
+
core_1.Utils.keys(item.merge).forEach(key => {
|
|
303
334
|
const k = this.mapper(key, type);
|
|
304
335
|
mergeParam[k] = item.merge[key];
|
|
305
336
|
});
|
|
@@ -330,34 +361,29 @@ class QueryBuilderHelper {
|
|
|
330
361
|
}
|
|
331
362
|
appendQuerySubCondition(qb, type, method, cond, key, operator) {
|
|
332
363
|
const m = operator === '$or' ? 'orWhere' : method;
|
|
364
|
+
if (cond[key] instanceof core_1.RawQueryFragment) {
|
|
365
|
+
cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
|
|
366
|
+
}
|
|
333
367
|
if (this.isSimpleRegExp(cond[key])) {
|
|
334
368
|
return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
|
|
335
369
|
}
|
|
336
370
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
337
371
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
338
372
|
}
|
|
339
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
340
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
341
|
-
}
|
|
342
373
|
const op = cond[key] === null ? 'is' : '=';
|
|
374
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
375
|
+
if (raw) {
|
|
376
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
377
|
+
if (value.length > 0) {
|
|
378
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
379
|
+
}
|
|
380
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
381
|
+
}
|
|
343
382
|
if (this.subQueries[key]) {
|
|
344
383
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
345
384
|
}
|
|
346
385
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
347
386
|
}
|
|
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
387
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
362
388
|
// grouped condition for one field
|
|
363
389
|
let value = cond[key];
|
|
@@ -376,7 +402,8 @@ class QueryBuilderHelper {
|
|
|
376
402
|
const replacement = this.getOperatorReplacement(op, value);
|
|
377
403
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
378
404
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
379
|
-
value[op]
|
|
405
|
+
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
406
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
|
|
380
407
|
}
|
|
381
408
|
if (this.subQueries[key]) {
|
|
382
409
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
@@ -394,7 +421,8 @@ class QueryBuilderHelper {
|
|
|
394
421
|
}));
|
|
395
422
|
}
|
|
396
423
|
else {
|
|
397
|
-
|
|
424
|
+
const mappedKey = this.mapper(key, type, value[op], null);
|
|
425
|
+
qb[m](mappedKey, replacement, value[op]);
|
|
398
426
|
}
|
|
399
427
|
}
|
|
400
428
|
getOperatorReplacement(op, value) {
|
|
@@ -422,36 +450,58 @@ class QueryBuilderHelper {
|
|
|
422
450
|
}
|
|
423
451
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
424
452
|
const ret = [];
|
|
425
|
-
Object.keys(orderBy).forEach(
|
|
426
|
-
const direction = orderBy[
|
|
453
|
+
Object.keys(orderBy).forEach(key => {
|
|
454
|
+
const direction = orderBy[key];
|
|
427
455
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
428
|
-
|
|
429
|
-
|
|
456
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
457
|
+
if (raw) {
|
|
458
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
430
459
|
return;
|
|
431
460
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const prop = this.getProperty(
|
|
437
|
-
const noPrefix = (prop && prop.persist === false && !prop.formula) ||
|
|
438
|
-
const column = this.mapper(noPrefix ?
|
|
461
|
+
core_1.Utils.splitPrimaryKeys(key).forEach(f => {
|
|
462
|
+
// eslint-disable-next-line prefer-const
|
|
463
|
+
let [alias, field] = this.splitField(f, true);
|
|
464
|
+
alias = populate[alias] || alias;
|
|
465
|
+
const prop = this.getProperty(field, alias);
|
|
466
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
467
|
+
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
439
468
|
/* istanbul ignore next */
|
|
440
469
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
441
470
|
const customOrder = prop?.customOrder;
|
|
442
|
-
|
|
471
|
+
let colPart = customOrder
|
|
443
472
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
444
473
|
: rawColumn;
|
|
474
|
+
if (core_1.Utils.isRawSql(colPart)) {
|
|
475
|
+
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
476
|
+
}
|
|
445
477
|
ret.push(`${colPart} ${order.toLowerCase()}`);
|
|
446
478
|
});
|
|
447
479
|
});
|
|
448
480
|
return ret.join(', ');
|
|
449
481
|
}
|
|
450
|
-
finalize(type, qb, meta) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
482
|
+
finalize(type, qb, meta, data, returning) {
|
|
483
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
// always respect explicit returning hint
|
|
487
|
+
if (returning && returning.length > 0) {
|
|
488
|
+
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
492
|
+
const returningProps = meta.hydrateProps
|
|
493
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
494
|
+
.filter(prop => !(prop.name in data));
|
|
495
|
+
if (returningProps.length > 0) {
|
|
496
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
497
|
+
}
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
501
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
502
|
+
if (returningProps.length > 0) {
|
|
503
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
504
|
+
}
|
|
455
505
|
}
|
|
456
506
|
}
|
|
457
507
|
splitField(field, greedyAlias = false) {
|
|
@@ -489,23 +539,18 @@ class QueryBuilderHelper {
|
|
|
489
539
|
}
|
|
490
540
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
491
541
|
let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
492
|
-
if (versionProperty.
|
|
542
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
493
543
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
494
544
|
}
|
|
495
545
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
496
546
|
}
|
|
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
547
|
prefix(field, always = false, quote = false) {
|
|
503
548
|
let ret;
|
|
504
549
|
if (!this.isPrefixed(field)) {
|
|
505
550
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
506
551
|
const fieldName = this.fieldName(field, this.alias, always);
|
|
507
|
-
if (
|
|
508
|
-
return fieldName;
|
|
552
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
553
|
+
return fieldName.sql;
|
|
509
554
|
}
|
|
510
555
|
ret = alias + fieldName;
|
|
511
556
|
}
|
|
@@ -545,15 +590,15 @@ class QueryBuilderHelper {
|
|
|
545
590
|
}
|
|
546
591
|
if (prop.fieldNameRaw) {
|
|
547
592
|
if (!always) {
|
|
548
|
-
return prop.fieldNameRaw
|
|
549
|
-
.replace(
|
|
550
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
593
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
594
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
595
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
551
596
|
}
|
|
552
597
|
if (alias) {
|
|
553
|
-
return prop.fieldNameRaw.replace(
|
|
598
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
554
599
|
}
|
|
555
600
|
/* istanbul ignore next */
|
|
556
|
-
return prop.fieldNameRaw;
|
|
601
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
557
602
|
}
|
|
558
603
|
/* istanbul ignore next */
|
|
559
604
|
return prop.fieldNames?.[0] ?? field;
|
|
@@ -564,7 +609,7 @@ class QueryBuilderHelper {
|
|
|
564
609
|
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
565
610
|
if (alias && meta) {
|
|
566
611
|
const prop = meta.properties[alias];
|
|
567
|
-
if (prop?.
|
|
612
|
+
if (prop?.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
568
613
|
// we want to select the full object property so hydration works as expected
|
|
569
614
|
if (prop.object) {
|
|
570
615
|
return prop;
|
|
@@ -596,17 +641,18 @@ class QueryBuilderHelper {
|
|
|
596
641
|
return;
|
|
597
642
|
}
|
|
598
643
|
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
599
|
-
const copy = data[k];
|
|
644
|
+
const copy = core_1.Utils.flatten(data[k]);
|
|
600
645
|
delete data[k];
|
|
601
646
|
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
602
647
|
return;
|
|
603
648
|
}
|
|
604
649
|
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' });
|
|
650
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query-data' });
|
|
606
651
|
}
|
|
607
|
-
if (prop.
|
|
652
|
+
if (prop.hasConvertToDatabaseValueSQL && !this.platform.isRaw(data[k])) {
|
|
608
653
|
const quoted = this.platform.quoteValue(data[k]);
|
|
609
|
-
|
|
654
|
+
const sql = prop.customType.convertToDatabaseValueSQL(quoted, this.platform);
|
|
655
|
+
data[k] = this.knex.raw(sql.replace(/\?/g, '\\?'));
|
|
610
656
|
}
|
|
611
657
|
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
612
658
|
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;
|