@mikro-orm/knex 6.0.0-dev.32 → 6.0.0-dev.320
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 +22 -4
- package/AbstractSqlConnection.js +45 -20
- package/AbstractSqlDriver.d.ts +30 -24
- package/AbstractSqlDriver.js +606 -268
- package/AbstractSqlPlatform.d.ts +9 -5
- package/AbstractSqlPlatform.js +25 -29
- package/MonkeyPatchable.d.ts +1 -0
- package/MonkeyPatchable.js +3 -0
- package/PivotCollectionPersister.d.ts +17 -0
- package/PivotCollectionPersister.js +128 -0
- package/README.md +64 -107
- package/SqlEntityManager.d.ts +4 -8
- package/SqlEntityManager.js +8 -12
- package/SqlEntityRepository.d.ts +7 -5
- package/SqlEntityRepository.js +11 -7
- package/index.mjs +24 -3
- package/package.json +8 -8
- package/query/ArrayCriteriaNode.d.ts +3 -2
- package/query/ArrayCriteriaNode.js +7 -2
- package/query/CriteriaNode.d.ts +6 -4
- package/query/CriteriaNode.js +22 -9
- package/query/CriteriaNodeFactory.d.ts +1 -1
- package/query/CriteriaNodeFactory.js +27 -11
- package/query/ObjectCriteriaNode.d.ts +3 -2
- package/query/ObjectCriteriaNode.js +72 -28
- package/query/QueryBuilder.d.ts +69 -36
- package/query/QueryBuilder.js +469 -146
- package/query/QueryBuilderHelper.d.ts +13 -18
- package/query/QueryBuilderHelper.js +199 -171
- package/query/ScalarCriteriaNode.d.ts +3 -2
- package/query/ScalarCriteriaNode.js +14 -4
- package/query/enums.d.ts +7 -0
- package/query/enums.js +10 -2
- package/schema/DatabaseSchema.d.ts +5 -2
- package/schema/DatabaseSchema.js +26 -8
- package/schema/DatabaseTable.d.ts +12 -4
- package/schema/DatabaseTable.js +370 -42
- package/schema/SchemaComparator.d.ts +7 -5
- package/schema/SchemaComparator.js +69 -35
- package/schema/SchemaHelper.d.ts +7 -5
- package/schema/SchemaHelper.js +30 -11
- package/schema/SqlSchemaGenerator.d.ts +11 -48
- package/schema/SqlSchemaGenerator.js +84 -30
- package/typings.d.ts +27 -8
|
@@ -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
|
}
|
|
@@ -29,7 +41,11 @@ class QueryBuilderHelper {
|
|
|
29
41
|
for (const p of fields) {
|
|
30
42
|
const [a, f] = this.splitField(p);
|
|
31
43
|
const prop = this.getProperty(f, a);
|
|
32
|
-
|
|
44
|
+
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
45
|
+
if (fkIdx2 !== -1) {
|
|
46
|
+
parts.push(this.mapper(a !== this.alias ? `${a}.${prop.fieldNames[fkIdx2]}` : prop.fieldNames[fkIdx2], type, value, alias));
|
|
47
|
+
}
|
|
48
|
+
else if (prop) {
|
|
33
49
|
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias)));
|
|
34
50
|
}
|
|
35
51
|
else {
|
|
@@ -49,13 +65,18 @@ class QueryBuilderHelper {
|
|
|
49
65
|
}
|
|
50
66
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
51
67
|
}
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(field);
|
|
69
|
+
if (rawField) {
|
|
70
|
+
return this.knex.raw(rawField.sql, rawField.params);
|
|
71
|
+
}
|
|
54
72
|
const [a, f] = this.splitField(field);
|
|
55
73
|
const prop = this.getProperty(f, a);
|
|
74
|
+
const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
|
|
75
|
+
const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
|
|
76
|
+
let ret = field;
|
|
56
77
|
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
57
78
|
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
58
|
-
return this.alias + '.' + prop.fieldNames[
|
|
79
|
+
return this.alias + '.' + prop.fieldNames[fkIdx];
|
|
59
80
|
}
|
|
60
81
|
const noPrefix = prop && prop.persist === false;
|
|
61
82
|
if (prop?.fieldNameRaw) {
|
|
@@ -67,25 +88,29 @@ class QueryBuilderHelper {
|
|
|
67
88
|
const as = alias === null ? '' : ` as ${aliased}`;
|
|
68
89
|
return this.knex.raw(`${prop.formula(alias2)}${as}`);
|
|
69
90
|
}
|
|
70
|
-
if (prop?.
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
if (prop?.hasConvertToJSValueSQL) {
|
|
92
|
+
let valueSQL;
|
|
93
|
+
if (prop.fieldNames.length > 1 && fkIdx !== -1) {
|
|
94
|
+
const fk = prop.targetMeta.getPrimaryProps()[fkIdx];
|
|
95
|
+
const prefixed = this.prefix(field, isTableNameAliasRequired, true, fkIdx);
|
|
96
|
+
valueSQL = fk.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const prefixed = this.prefix(field, isTableNameAliasRequired, true);
|
|
100
|
+
valueSQL = prop.customType.convertToJSValueSQL(prefixed, this.platform);
|
|
101
|
+
}
|
|
73
102
|
if (alias === null) {
|
|
74
103
|
return this.knex.raw(valueSQL);
|
|
75
104
|
}
|
|
76
|
-
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[
|
|
105
|
+
return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[fkIdx])}`);
|
|
77
106
|
}
|
|
78
107
|
// do not wrap custom expressions
|
|
79
|
-
if (!
|
|
80
|
-
ret = this.prefix(field);
|
|
108
|
+
if (!rawField) {
|
|
109
|
+
ret = this.prefix(field, false, false, fkIdx);
|
|
81
110
|
}
|
|
82
111
|
if (alias) {
|
|
83
112
|
ret += ' as ' + alias;
|
|
84
113
|
}
|
|
85
|
-
if (customExpression) {
|
|
86
|
-
const bindings = core_1.Utils.asArray(value).slice(0, ret.match(/\?/g)?.length ?? 0);
|
|
87
|
-
return this.knex.raw(ret, bindings);
|
|
88
|
-
}
|
|
89
114
|
if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
|
|
90
115
|
return ret;
|
|
91
116
|
}
|
|
@@ -96,94 +121,110 @@ class QueryBuilderHelper {
|
|
|
96
121
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
97
122
|
}
|
|
98
123
|
const meta = this.metadata.find(this.entityName);
|
|
99
|
-
data = this.
|
|
124
|
+
data = this.driver.mapDataToFieldNames(data, true, meta?.properties, convertCustomTypes);
|
|
100
125
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
101
126
|
/* istanbul ignore next */
|
|
102
127
|
data[meta.primaryKeys[0]] = this.platform.usesDefaultKeyword() ? this.knex.raw('default') : undefined;
|
|
103
128
|
}
|
|
104
129
|
return data;
|
|
105
130
|
}
|
|
106
|
-
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
131
|
+
joinOneToReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
107
132
|
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
108
133
|
const table = this.getTableName(prop.type);
|
|
109
|
-
const schema = this.driver.getSchemaName(prop.targetMeta);
|
|
110
134
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
111
135
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
112
136
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
137
|
+
schema ??= prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta);
|
|
113
138
|
return {
|
|
114
139
|
prop, type, cond, ownerAlias, alias, table, schema,
|
|
115
140
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
116
141
|
};
|
|
117
142
|
}
|
|
118
|
-
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
143
|
+
joinManyToOneReference(prop, ownerAlias, alias, type, cond = {}, schema) {
|
|
119
144
|
return {
|
|
120
145
|
prop, type, cond, ownerAlias, alias,
|
|
121
146
|
table: this.getTableName(prop.type),
|
|
122
|
-
schema: this.driver.getSchemaName(prop.targetMeta),
|
|
147
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(prop.targetMeta, { schema }),
|
|
123
148
|
joinColumns: prop.referencedColumnNames,
|
|
124
149
|
primaryKeys: prop.fieldNames,
|
|
125
150
|
};
|
|
126
151
|
}
|
|
127
|
-
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
152
|
+
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path, schema) {
|
|
128
153
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
129
154
|
const ret = {
|
|
130
155
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
131
|
-
prop, type,
|
|
156
|
+
prop, type, ownerAlias,
|
|
132
157
|
alias: pivotAlias,
|
|
133
158
|
inverseAlias: alias,
|
|
134
159
|
joinColumns: prop.joinColumns,
|
|
135
160
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
136
161
|
primaryKeys: prop.referencedColumnNames,
|
|
162
|
+
cond: {},
|
|
137
163
|
table: pivotMeta.tableName,
|
|
138
|
-
schema: this.driver.getSchemaName(pivotMeta),
|
|
164
|
+
schema: prop.targetMeta?.schema === '*' ? '*' : this.driver.getSchemaName(pivotMeta, { schema }),
|
|
139
165
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
140
166
|
},
|
|
141
167
|
};
|
|
142
|
-
if (type ===
|
|
168
|
+
if (type === enums_1.JoinType.pivotJoin) {
|
|
143
169
|
return ret;
|
|
144
170
|
}
|
|
145
171
|
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
146
|
-
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
172
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type, cond, schema);
|
|
147
173
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
174
|
+
const tmp = prop2.referencedTableName.split('.');
|
|
175
|
+
ret[`${pivotAlias}.${prop2.name}#${alias}`].schema ??= tmp.length > 1 ? tmp[0] : undefined;
|
|
148
176
|
return ret;
|
|
149
177
|
}
|
|
150
|
-
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
151
|
-
const pivotMeta = this.metadata.find(field);
|
|
152
|
-
const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
153
|
-
return {
|
|
154
|
-
prop, type, cond, ownerAlias, alias,
|
|
155
|
-
table: pivotMeta.collection,
|
|
156
|
-
schema: pivotMeta.schema,
|
|
157
|
-
joinColumns: prop.joinColumns,
|
|
158
|
-
inverseJoinColumns: prop2.joinColumns,
|
|
159
|
-
primaryKeys: prop.referencedColumnNames,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
178
|
processJoins(qb, joins, schema) {
|
|
163
179
|
Object.values(joins).forEach(join => {
|
|
164
|
-
let table =
|
|
165
|
-
const method = join.type ===
|
|
180
|
+
let table = join.table;
|
|
181
|
+
const method = join.type === enums_1.JoinType.pivotJoin ? 'left join' : join.type;
|
|
182
|
+
const conditions = [];
|
|
183
|
+
const params = [];
|
|
166
184
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
167
185
|
if (schema) {
|
|
168
186
|
table = `${schema}.${table}`;
|
|
169
187
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
if (!join.subquery) {
|
|
189
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
190
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
191
|
+
if (join.prop.formula) {
|
|
192
|
+
const left = join.prop.formula(join.ownerAlias);
|
|
193
|
+
conditions.push(`${left} = ${this.knex.ref(right)}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const left = `${join.ownerAlias}.${primaryKey}`;
|
|
197
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (join.prop.targetMeta?.discriminatorValue && !join.path?.endsWith('[pivot]')) {
|
|
201
|
+
const typeProperty = join.prop.targetMeta.root.discriminatorColumn;
|
|
202
|
+
const alias = join.inverseAlias ?? join.alias;
|
|
203
|
+
join.cond[`${alias}.${typeProperty}`] = join.prop.targetMeta.discriminatorValue;
|
|
204
|
+
}
|
|
205
|
+
for (const key of Object.keys(join.cond)) {
|
|
206
|
+
const hasPrefix = key.includes('.') || core_1.Utils.isOperator(key) || core_1.RawQueryFragment.isKnownFragment(key);
|
|
207
|
+
const newKey = hasPrefix ? key : `${join.alias}.${key}`;
|
|
208
|
+
conditions.push(this.processJoinClause(newKey, join.cond[key], join.alias, params));
|
|
209
|
+
}
|
|
210
|
+
let sql = method + ' ';
|
|
211
|
+
if (join.subquery) {
|
|
212
|
+
sql += `(${join.subquery})`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
sql += this.knex.ref(table);
|
|
216
|
+
}
|
|
217
|
+
sql += ` as ${this.knex.ref(join.alias)}`;
|
|
218
|
+
if (conditions.length > 0) {
|
|
219
|
+
sql += ` on ${conditions.join(' and ')}`;
|
|
220
|
+
}
|
|
221
|
+
return qb.joinRaw(sql, params);
|
|
181
222
|
});
|
|
182
223
|
}
|
|
183
|
-
processJoinClause(key, value, params, operator = '$eq') {
|
|
224
|
+
processJoinClause(key, value, alias, params, operator = '$eq') {
|
|
184
225
|
if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
|
|
185
226
|
const parts = value.map(sub => {
|
|
186
|
-
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], params)));
|
|
227
|
+
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], alias, params)));
|
|
187
228
|
});
|
|
188
229
|
return this.wrapQueryGroup(parts, key);
|
|
189
230
|
}
|
|
@@ -195,11 +236,11 @@ class QueryBuilderHelper {
|
|
|
195
236
|
value = this.platform.getRegExpValue(value);
|
|
196
237
|
}
|
|
197
238
|
if (core_1.Utils.isOperator(key, false) && core_1.Utils.isPlainObject(value)) {
|
|
198
|
-
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], params, key));
|
|
239
|
+
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], alias, params, key));
|
|
199
240
|
return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
|
|
200
241
|
}
|
|
201
242
|
if (core_1.Utils.isPlainObject(value) && Object.keys(value).every(k => core_1.Utils.isOperator(k, false))) {
|
|
202
|
-
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], params, op));
|
|
243
|
+
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], alias, params, op));
|
|
203
244
|
return this.wrapQueryGroup(parts);
|
|
204
245
|
}
|
|
205
246
|
operator = operator === '$not' ? '$eq' : operator;
|
|
@@ -224,20 +265,14 @@ class QueryBuilderHelper {
|
|
|
224
265
|
if (operator === '$exists') {
|
|
225
266
|
value = null;
|
|
226
267
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const { sql, bindings } = k.toSQL().toNative();
|
|
235
|
-
if (params2.length > 0) {
|
|
236
|
-
params.push(...bindings);
|
|
237
|
-
params.push(...params2);
|
|
238
|
-
return sql + ' = ?';
|
|
268
|
+
const rawField = core_1.RawQueryFragment.getKnownFragment(key);
|
|
269
|
+
if (rawField) {
|
|
270
|
+
let sql = rawField.sql.replaceAll(core_1.ALIAS_REPLACEMENT, alias);
|
|
271
|
+
params.push(...rawField.params);
|
|
272
|
+
params.push(...core_1.Utils.asArray(value));
|
|
273
|
+
if (core_1.Utils.asArray(value).length > 0) {
|
|
274
|
+
sql += ' = ?';
|
|
239
275
|
}
|
|
240
|
-
params.push(...bindings);
|
|
241
276
|
return sql;
|
|
242
277
|
}
|
|
243
278
|
const sql = this.mapper(key);
|
|
@@ -253,9 +288,10 @@ class QueryBuilderHelper {
|
|
|
253
288
|
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
254
289
|
}
|
|
255
290
|
mapJoinColumns(type, join) {
|
|
256
|
-
if (join.prop &&
|
|
291
|
+
if (join.prop && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(join.prop.kind)) {
|
|
257
292
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
258
|
-
|
|
293
|
+
const columns = join.prop.owner ? join.joinColumns : join.inverseJoinColumns;
|
|
294
|
+
return this.mapper(`${join.alias}.${columns[idx]}`, type, undefined, fieldName);
|
|
259
295
|
});
|
|
260
296
|
}
|
|
261
297
|
return [
|
|
@@ -263,9 +299,9 @@ class QueryBuilderHelper {
|
|
|
263
299
|
...join.inverseJoinColumns.map(col => this.mapper(`${join.alias}.${col}`, type, undefined, `fk__${col}`)),
|
|
264
300
|
];
|
|
265
301
|
}
|
|
266
|
-
isOneToOneInverse(field) {
|
|
267
|
-
|
|
268
|
-
const prop = meta.properties[field];
|
|
302
|
+
isOneToOneInverse(field, meta) {
|
|
303
|
+
meta ??= this.metadata.find(this.entityName);
|
|
304
|
+
const prop = meta.properties[field.replace(/:ref$/, '')];
|
|
269
305
|
return prop && prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
270
306
|
}
|
|
271
307
|
getTableName(entityName) {
|
|
@@ -305,7 +341,7 @@ class QueryBuilderHelper {
|
|
|
305
341
|
}
|
|
306
342
|
appendOnConflictClause(type, onConflict, qb) {
|
|
307
343
|
onConflict.forEach(item => {
|
|
308
|
-
const sub = qb.onConflict(item.fields);
|
|
344
|
+
const sub = item.fields.length > 0 ? qb.onConflict(item.fields) : qb.onConflict();
|
|
309
345
|
core_1.Utils.runIfNotEmpty(() => sub.ignore(), item.ignore);
|
|
310
346
|
core_1.Utils.runIfNotEmpty(() => {
|
|
311
347
|
let mergeParam = item.merge;
|
|
@@ -342,38 +378,37 @@ class QueryBuilderHelper {
|
|
|
342
378
|
}
|
|
343
379
|
appendQuerySubCondition(qb, type, method, cond, key, operator) {
|
|
344
380
|
const m = operator === '$or' ? 'orWhere' : method;
|
|
381
|
+
if (cond[key] instanceof core_1.RawQueryFragment) {
|
|
382
|
+
cond[key] = this.knex.raw(cond[key].sql, cond[key].params);
|
|
383
|
+
}
|
|
345
384
|
if (this.isSimpleRegExp(cond[key])) {
|
|
346
385
|
return void qb[m](this.mapper(key, type), 'like', this.getRegExpParam(cond[key]));
|
|
347
386
|
}
|
|
348
387
|
if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
|
|
349
388
|
return this.processObjectSubCondition(cond, key, qb, method, m, type);
|
|
350
389
|
}
|
|
351
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
352
|
-
return this.processCustomExpression(qb, m, key, cond, type);
|
|
353
|
-
}
|
|
354
390
|
const op = cond[key] === null ? 'is' : '=';
|
|
391
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
392
|
+
if (raw) {
|
|
393
|
+
const value = core_1.Utils.asArray(cond[key]);
|
|
394
|
+
if (value.length > 0) {
|
|
395
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
|
|
396
|
+
}
|
|
397
|
+
return void qb[m](this.knex.raw(raw.sql, raw.params));
|
|
398
|
+
}
|
|
355
399
|
if (this.subQueries[key]) {
|
|
356
400
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
|
|
357
401
|
}
|
|
358
402
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
359
403
|
}
|
|
360
|
-
processCustomExpression(clause, m, key, cond, type) {
|
|
361
|
-
// unwind parameters when ? found in field name
|
|
362
|
-
const count = key.concat('?').match(/\?/g).length - 1;
|
|
363
|
-
const value = core_1.Utils.asArray(cond[key]);
|
|
364
|
-
const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
365
|
-
const params2 = value.slice(count);
|
|
366
|
-
const k = this.mapper(key, type, params1);
|
|
367
|
-
if (params2.length > 0) {
|
|
368
|
-
const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
|
|
369
|
-
return void clause[m](k, val);
|
|
370
|
-
}
|
|
371
|
-
clause[m](k);
|
|
372
|
-
}
|
|
373
404
|
processObjectSubCondition(cond, key, qb, method, m, type) {
|
|
374
405
|
// grouped condition for one field
|
|
375
406
|
let value = cond[key];
|
|
376
|
-
|
|
407
|
+
const size = core_1.Utils.getObjectKeysSize(value);
|
|
408
|
+
if (core_1.Utils.isPlainObject(value) && size === 0) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (size > 1) {
|
|
377
412
|
const subCondition = Object.entries(value).map(([subKey, subValue]) => ({ [key]: { [subKey]: subValue } }));
|
|
378
413
|
return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
|
|
379
414
|
}
|
|
@@ -382,13 +417,15 @@ class QueryBuilderHelper {
|
|
|
382
417
|
}
|
|
383
418
|
// operators
|
|
384
419
|
const op = Object.keys(core_1.QueryOperator).find(op => op in value);
|
|
420
|
+
/* istanbul ignore next */
|
|
385
421
|
if (!op) {
|
|
386
422
|
throw new Error(`Invalid query condition: ${(0, util_1.inspect)(cond)}`);
|
|
387
423
|
}
|
|
388
424
|
const replacement = this.getOperatorReplacement(op, value);
|
|
389
425
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
390
426
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
391
|
-
value[op]
|
|
427
|
+
const tmp = value[op].length === 1 && core_1.Utils.isPlainObject(value[op][0]) ? fields.map(f => value[op][0][f]) : value[op];
|
|
428
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, tmp);
|
|
392
429
|
}
|
|
393
430
|
if (this.subQueries[key]) {
|
|
394
431
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
@@ -426,60 +463,88 @@ class QueryBuilderHelper {
|
|
|
426
463
|
}
|
|
427
464
|
getQueryOrder(type, orderBy, populate) {
|
|
428
465
|
if (Array.isArray(orderBy)) {
|
|
429
|
-
return orderBy
|
|
430
|
-
.map(o => this.getQueryOrder(type, o, populate))
|
|
431
|
-
.filter(o => o)
|
|
432
|
-
.join(', ');
|
|
466
|
+
return orderBy.flatMap(o => this.getQueryOrder(type, o, populate));
|
|
433
467
|
}
|
|
434
468
|
return this.getQueryOrderFromObject(type, orderBy, populate);
|
|
435
469
|
}
|
|
436
470
|
getQueryOrderFromObject(type, orderBy, populate) {
|
|
437
471
|
const ret = [];
|
|
438
|
-
Object.keys(orderBy)
|
|
439
|
-
const direction = orderBy[
|
|
472
|
+
for (const key of Object.keys(orderBy)) {
|
|
473
|
+
const direction = orderBy[key];
|
|
440
474
|
const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
475
|
+
const raw = core_1.RawQueryFragment.getKnownFragment(key);
|
|
476
|
+
if (raw) {
|
|
477
|
+
ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
|
|
478
|
+
continue;
|
|
444
479
|
}
|
|
445
|
-
core_1.Utils.splitPrimaryKeys(
|
|
480
|
+
for (const f of core_1.Utils.splitPrimaryKeys(key)) {
|
|
446
481
|
// eslint-disable-next-line prefer-const
|
|
447
482
|
let [alias, field] = this.splitField(f, true);
|
|
448
483
|
alias = populate[alias] || alias;
|
|
449
484
|
const prop = this.getProperty(field, alias);
|
|
450
|
-
const noPrefix = (prop && prop.persist === false && !prop.formula) ||
|
|
485
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
|
|
451
486
|
const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
|
|
452
487
|
/* istanbul ignore next */
|
|
453
488
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
454
489
|
const customOrder = prop?.customOrder;
|
|
455
|
-
|
|
490
|
+
let colPart = customOrder
|
|
456
491
|
? this.platform.generateCustomOrder(rawColumn, customOrder)
|
|
457
492
|
: rawColumn;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
493
|
+
if (core_1.Utils.isRawSql(colPart)) {
|
|
494
|
+
colPart = this.platform.formatQuery(colPart.sql, colPart.params);
|
|
495
|
+
}
|
|
496
|
+
if (Array.isArray(order)) {
|
|
497
|
+
order.forEach(part => ret.push(...this.getQueryOrderFromObject(type, part, populate)));
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
ret.push(...this.platform.getOrderByExpression(colPart, order));
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return ret;
|
|
462
505
|
}
|
|
463
|
-
finalize(type, qb, meta) {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
506
|
+
finalize(type, qb, meta, data, returning) {
|
|
507
|
+
if (!meta || !data || !this.platform.usesReturningStatement()) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
// always respect explicit returning hint
|
|
511
|
+
if (returning && returning.length > 0) {
|
|
512
|
+
qb.returning(returning.map(field => this.mapper(field, type)));
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (type === enums_1.QueryType.INSERT) {
|
|
516
|
+
const returningProps = meta.hydrateProps
|
|
517
|
+
.filter(prop => prop.returning || (prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw)))
|
|
518
|
+
.filter(prop => !(prop.name in data));
|
|
519
|
+
if (returningProps.length > 0) {
|
|
520
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
521
|
+
}
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (type === enums_1.QueryType.UPDATE) {
|
|
525
|
+
const returningProps = meta.hydrateProps.filter(prop => core_1.Utils.isRawSql(data[prop.name]));
|
|
526
|
+
if (returningProps.length > 0) {
|
|
527
|
+
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
528
|
+
}
|
|
468
529
|
}
|
|
469
530
|
}
|
|
470
531
|
splitField(field, greedyAlias = false) {
|
|
471
532
|
const parts = field.split('.');
|
|
533
|
+
const ref = parts[parts.length - 1].split(':')[1];
|
|
534
|
+
if (ref) {
|
|
535
|
+
parts[parts.length - 1] = parts[parts.length - 1].substring(0, parts[parts.length - 1].indexOf(':'));
|
|
536
|
+
}
|
|
472
537
|
if (parts.length === 1) {
|
|
473
|
-
return [this.alias, parts[0]];
|
|
538
|
+
return [this.alias, parts[0], ref];
|
|
474
539
|
}
|
|
475
540
|
if (greedyAlias) {
|
|
476
541
|
const fromField = parts.pop();
|
|
477
542
|
const fromAlias = parts.join('.');
|
|
478
|
-
return [fromAlias, fromField];
|
|
543
|
+
return [fromAlias, fromField, ref];
|
|
479
544
|
}
|
|
480
545
|
const fromAlias = parts.shift();
|
|
481
546
|
const fromField = parts.join('.');
|
|
482
|
-
return [fromAlias, fromField];
|
|
547
|
+
return [fromAlias, fromField, ref];
|
|
483
548
|
}
|
|
484
549
|
getLockSQL(qb, lockMode, lockTables = []) {
|
|
485
550
|
const meta = this.metadata.find(this.entityName);
|
|
@@ -502,30 +567,25 @@ class QueryBuilderHelper {
|
|
|
502
567
|
}
|
|
503
568
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
504
569
|
let sql = this.platform.quoteIdentifier(versionProperty.fieldNames[0]) + ' + 1';
|
|
505
|
-
if (versionProperty.
|
|
570
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
506
571
|
sql = this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
507
572
|
}
|
|
508
573
|
qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
|
|
509
574
|
}
|
|
510
|
-
|
|
511
|
-
// if we do not have alias, we don't consider spaces as custom expressions
|
|
512
|
-
const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
|
|
513
|
-
return !!field.match(re);
|
|
514
|
-
}
|
|
515
|
-
prefix(field, always = false, quote = false) {
|
|
575
|
+
prefix(field, always = false, quote = false, idx) {
|
|
516
576
|
let ret;
|
|
517
577
|
if (!this.isPrefixed(field)) {
|
|
518
578
|
const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
|
|
519
|
-
const fieldName = this.fieldName(field, this.alias, always);
|
|
520
|
-
if (
|
|
521
|
-
return fieldName;
|
|
579
|
+
const fieldName = this.fieldName(field, this.alias, always, idx);
|
|
580
|
+
if (fieldName instanceof core_1.RawQueryFragment) {
|
|
581
|
+
return fieldName.sql;
|
|
522
582
|
}
|
|
523
583
|
ret = alias + fieldName;
|
|
524
584
|
}
|
|
525
585
|
else {
|
|
526
586
|
const [a, ...rest] = field.split('.');
|
|
527
587
|
const f = rest.join('.');
|
|
528
|
-
ret = a + '.' + this.fieldName(f, a);
|
|
588
|
+
ret = a + '.' + this.fieldName(f, a, always, idx);
|
|
529
589
|
}
|
|
530
590
|
if (quote) {
|
|
531
591
|
return this.platform.quoteIdentifier(ret);
|
|
@@ -551,25 +611,25 @@ class QueryBuilderHelper {
|
|
|
551
611
|
isPrefixed(field) {
|
|
552
612
|
return !!field.match(/[\w`"[\]]+\./);
|
|
553
613
|
}
|
|
554
|
-
fieldName(field, alias, always) {
|
|
614
|
+
fieldName(field, alias, always, idx = 0) {
|
|
555
615
|
const prop = this.getProperty(field, alias);
|
|
556
616
|
if (!prop) {
|
|
557
617
|
return field;
|
|
558
618
|
}
|
|
559
619
|
if (prop.fieldNameRaw) {
|
|
560
620
|
if (!always) {
|
|
561
|
-
return prop.fieldNameRaw
|
|
562
|
-
.replace(
|
|
563
|
-
.replace(this.platform.quoteIdentifier('') + '.', '');
|
|
621
|
+
return (0, core_1.raw)(prop.fieldNameRaw
|
|
622
|
+
.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
|
|
623
|
+
.replace(this.platform.quoteIdentifier('') + '.', ''));
|
|
564
624
|
}
|
|
565
625
|
if (alias) {
|
|
566
|
-
return prop.fieldNameRaw.replace(
|
|
626
|
+
return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
|
|
567
627
|
}
|
|
568
628
|
/* istanbul ignore next */
|
|
569
|
-
return prop.fieldNameRaw;
|
|
629
|
+
return (0, core_1.raw)(prop.fieldNameRaw);
|
|
570
630
|
}
|
|
571
631
|
/* istanbul ignore next */
|
|
572
|
-
return prop.fieldNames?.[
|
|
632
|
+
return prop.fieldNames?.[idx] ?? field;
|
|
573
633
|
}
|
|
574
634
|
getProperty(field, alias) {
|
|
575
635
|
const entityName = this.aliasMap[alias]?.entityName || this.entityName;
|
|
@@ -587,48 +647,16 @@ class QueryBuilderHelper {
|
|
|
587
647
|
return nest(prop);
|
|
588
648
|
}
|
|
589
649
|
}
|
|
590
|
-
|
|
650
|
+
if (meta) {
|
|
651
|
+
if (meta.properties[field]) {
|
|
652
|
+
return meta.properties[field];
|
|
653
|
+
}
|
|
654
|
+
return meta.relations.find(prop => prop.fieldNames?.some(name => field === name));
|
|
655
|
+
}
|
|
656
|
+
return undefined;
|
|
591
657
|
}
|
|
592
658
|
isTableNameAliasRequired(type) {
|
|
593
659
|
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type ?? enums_1.QueryType.SELECT);
|
|
594
660
|
}
|
|
595
|
-
mapData(data, properties, convertCustomTypes) {
|
|
596
|
-
if (!properties) {
|
|
597
|
-
return data;
|
|
598
|
-
}
|
|
599
|
-
data = Object.assign({}, data); // copy first
|
|
600
|
-
Object.keys(data).forEach(k => {
|
|
601
|
-
const prop = properties[k];
|
|
602
|
-
if (!prop) {
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
if (prop.embeddedProps && !prop.object) {
|
|
606
|
-
const copy = data[k];
|
|
607
|
-
delete data[k];
|
|
608
|
-
Object.assign(data, this.mapData(copy, prop.embeddedProps, convertCustomTypes));
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
612
|
-
const copy = data[k];
|
|
613
|
-
delete data[k];
|
|
614
|
-
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
618
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
|
|
619
|
-
}
|
|
620
|
-
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
621
|
-
const quoted = this.platform.quoteValue(data[k]);
|
|
622
|
-
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform).replace(/\?/, '\\?'));
|
|
623
|
-
}
|
|
624
|
-
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
625
|
-
data[k] = JSON.stringify(data[k]);
|
|
626
|
-
}
|
|
627
|
-
if (prop.fieldNames) {
|
|
628
|
-
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
return data;
|
|
632
|
-
}
|
|
633
661
|
}
|
|
634
662
|
exports.QueryBuilderHelper = QueryBuilderHelper;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { CriteriaNode } from './CriteriaNode';
|
|
2
|
-
import type { IQueryBuilder } from '../typings';
|
|
2
|
+
import type { IQueryBuilder, ICriteriaNodeProcessOptions } from '../typings';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
export declare class ScalarCriteriaNode<T extends object> extends CriteriaNode<T> {
|
|
7
|
-
process(qb: IQueryBuilder<T>,
|
|
7
|
+
process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
|
|
8
|
+
willAutoJoin<T>(qb: IQueryBuilder<T>, alias?: string): boolean;
|
|
8
9
|
shouldJoin(): boolean;
|
|
9
10
|
}
|