@mikro-orm/knex 6.0.0-dev.1 → 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/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +8 -4
- package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +35 -18
- package/AbstractSqlDriver.d.ts +66 -0
- package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +422 -129
- package/AbstractSqlPlatform.d.ts +22 -0
- package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +46 -19
- package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +1 -0
- package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +3 -0
- package/{dist/README.md → README.md} +66 -109
- package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +6 -10
- package/{dist/SqlEntityManager.js → SqlEntityManager.js} +5 -10
- package/SqlEntityRepository.d.ts +24 -0
- package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +11 -8
- package/{dist/index.d.ts → index.d.ts} +1 -0
- package/{dist/index.js → index.js} +6 -1
- package/index.mjs +208 -0
- package/package.json +20 -49
- package/query/ArrayCriteriaNode.d.ts +9 -0
- package/{dist/query → query}/CriteriaNode.d.ts +8 -8
- package/{dist/query → query}/CriteriaNode.js +23 -13
- package/query/CriteriaNodeFactory.d.ts +12 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +10 -13
- package/{dist/query → query}/ObjectCriteriaNode.d.ts +3 -3
- package/{dist/query → query}/ObjectCriteriaNode.js +26 -9
- package/{dist/query → query}/QueryBuilder.d.ts +90 -34
- package/query/QueryBuilder.js +1098 -0
- package/{dist/query → query}/QueryBuilderHelper.d.ts +23 -19
- package/{dist/query → query}/QueryBuilderHelper.js +300 -176
- package/query/ScalarCriteriaNode.d.ts +10 -0
- package/{dist/query → query}/ScalarCriteriaNode.js +9 -6
- package/{dist/query → query}/enums.js +1 -1
- package/{dist/query → query}/index.js +5 -1
- package/{dist/schema → schema}/DatabaseSchema.d.ts +6 -3
- package/{dist/schema → schema}/DatabaseSchema.js +33 -23
- package/{dist/schema → schema}/DatabaseTable.d.ts +12 -10
- package/{dist/schema → schema}/DatabaseTable.js +55 -29
- package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
- package/{dist/schema → schema}/SchemaComparator.js +68 -10
- package/{dist/schema → schema}/SchemaHelper.d.ts +20 -13
- package/{dist/schema → schema}/SchemaHelper.js +56 -11
- package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +11 -14
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +139 -55
- package/{dist/schema → schema}/index.d.ts +1 -1
- package/{dist/schema → schema}/index.js +6 -2
- package/{dist/typings.d.ts → typings.d.ts} +27 -18
- package/CHANGELOG.md +0 -661
- package/dist/AbstractSqlDriver.d.ts +0 -55
- package/dist/AbstractSqlPlatform.d.ts +0 -21
- package/dist/LICENSE +0 -21
- package/dist/SqlEntityRepository.d.ts +0 -23
- package/dist/index.mjs +0 -26
- package/dist/package.json +0 -99
- package/dist/query/ArrayCriteriaNode.d.ts +0 -9
- package/dist/query/CriteriaNodeFactory.d.ts +0 -12
- package/dist/query/QueryBuilder.js +0 -688
- package/dist/query/ScalarCriteriaNode.d.ts +0 -9
- /package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
- /package/{dist/query → query}/enums.d.ts +0 -0
- /package/{dist/query → query}/index.d.ts +0 -0
- /package/{dist/typings.js → typings.js} +0 -0
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AbstractSqlDriver = void 0;
|
|
4
4
|
const core_1 = require("@mikro-orm/core");
|
|
5
|
-
const
|
|
5
|
+
const query_1 = require("./query");
|
|
6
6
|
const SqlEntityManager_1 = require("./SqlEntityManager");
|
|
7
7
|
class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
8
|
+
[core_1.EntityManagerType];
|
|
9
|
+
connection;
|
|
10
|
+
replicas = [];
|
|
11
|
+
platform;
|
|
8
12
|
constructor(config, platform, connection, connector) {
|
|
9
13
|
super(config, connector);
|
|
10
|
-
this.replicas = [];
|
|
11
14
|
this.connection = new connection(this.config);
|
|
12
15
|
this.replicas = this.createReplicas(conf => new connection(this.config, conf, 'read'));
|
|
13
16
|
this.platform = platform;
|
|
@@ -21,21 +24,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
21
24
|
async find(entityName, where, options = {}) {
|
|
22
25
|
options = { populate: [], orderBy: [], ...options };
|
|
23
26
|
const meta = this.metadata.find(entityName);
|
|
27
|
+
if (meta?.virtual) {
|
|
28
|
+
return this.findVirtual(entityName, where, options);
|
|
29
|
+
}
|
|
24
30
|
const populate = this.autoJoinOneToOneOwner(meta, options.populate, options.fields);
|
|
25
31
|
const joinedProps = this.joinedProps(meta, populate);
|
|
26
|
-
const qb = this.createQueryBuilder(entityName, options.ctx,
|
|
32
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false, options.logging);
|
|
27
33
|
const fields = this.buildFields(meta, populate, joinedProps, qb, options.fields);
|
|
28
34
|
const joinedPropsOrderBy = this.buildJoinedPropsOrderBy(entityName, qb, meta, joinedProps);
|
|
35
|
+
const orderBy = [...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy];
|
|
29
36
|
if (core_1.Utils.isPrimaryKey(where, meta.compositePK)) {
|
|
30
37
|
where = { [core_1.Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
31
38
|
}
|
|
39
|
+
const { first, last, before, after } = options;
|
|
40
|
+
const isCursorPagination = [first, last, before, after].some(v => v != null);
|
|
32
41
|
qb.select(fields)
|
|
33
|
-
.populate(populate)
|
|
42
|
+
.populate(populate, joinedProps.length > 0 ? options.populateWhere : undefined)
|
|
34
43
|
.where(where)
|
|
35
|
-
.orderBy([...core_1.Utils.asArray(options.orderBy), ...joinedPropsOrderBy])
|
|
36
44
|
.groupBy(options.groupBy)
|
|
37
45
|
.having(options.having)
|
|
46
|
+
.indexHint(options.indexHint)
|
|
47
|
+
.comment(options.comments)
|
|
48
|
+
.hintComment(options.hintComments)
|
|
38
49
|
.withSchema(this.getSchemaName(meta, options));
|
|
50
|
+
if (isCursorPagination) {
|
|
51
|
+
const { orderBy: newOrderBy, where } = this.processCursorOptions(meta, options, orderBy);
|
|
52
|
+
qb.andWhere(where).orderBy(newOrderBy);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
qb.orderBy(orderBy);
|
|
56
|
+
}
|
|
39
57
|
if (options.limit !== undefined) {
|
|
40
58
|
qb.limit(options.limit, options.offset);
|
|
41
59
|
}
|
|
@@ -44,8 +62,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
44
62
|
}
|
|
45
63
|
core_1.Utils.asArray(options.flags).forEach(flag => qb.setFlag(flag));
|
|
46
64
|
const result = await this.rethrow(qb.execute('all'));
|
|
65
|
+
if (isCursorPagination && !first && !!last) {
|
|
66
|
+
result.reverse();
|
|
67
|
+
}
|
|
47
68
|
if (joinedProps.length > 0) {
|
|
48
|
-
return this.mergeJoinedResult(result, meta);
|
|
69
|
+
return this.mergeJoinedResult(result, meta, joinedProps);
|
|
49
70
|
}
|
|
50
71
|
return result;
|
|
51
72
|
}
|
|
@@ -57,9 +78,73 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
57
78
|
if (joinedProps.length === 0) {
|
|
58
79
|
opts.limit = 1;
|
|
59
80
|
}
|
|
81
|
+
if (opts.limit > 0 && !opts.flags?.includes(core_1.QueryFlag.DISABLE_PAGINATE)) {
|
|
82
|
+
opts.flags ??= [];
|
|
83
|
+
opts.flags.push(core_1.QueryFlag.DISABLE_PAGINATE);
|
|
84
|
+
}
|
|
60
85
|
const res = await this.find(entityName, where, opts);
|
|
61
86
|
return res[0] || null;
|
|
62
87
|
}
|
|
88
|
+
async findVirtual(entityName, where, options) {
|
|
89
|
+
const meta = this.metadata.get(entityName);
|
|
90
|
+
/* istanbul ignore next */
|
|
91
|
+
if (!meta.expression) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
if (typeof meta.expression === 'string') {
|
|
95
|
+
return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options);
|
|
96
|
+
}
|
|
97
|
+
const em = this.createEntityManager(false);
|
|
98
|
+
em.setTransactionContext(options.ctx);
|
|
99
|
+
const res = meta.expression(em, where, options);
|
|
100
|
+
if (res instanceof query_1.QueryBuilder) {
|
|
101
|
+
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options);
|
|
102
|
+
}
|
|
103
|
+
/* istanbul ignore next */
|
|
104
|
+
return res;
|
|
105
|
+
}
|
|
106
|
+
async countVirtual(entityName, where, options) {
|
|
107
|
+
const meta = this.metadata.get(entityName);
|
|
108
|
+
/* istanbul ignore next */
|
|
109
|
+
if (!meta.expression) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
if (typeof meta.expression === 'string') {
|
|
113
|
+
return this.wrapVirtualExpressionInSubquery(meta, meta.expression, where, options, query_1.QueryType.COUNT);
|
|
114
|
+
}
|
|
115
|
+
const em = this.createEntityManager(false);
|
|
116
|
+
em.setTransactionContext(options.ctx);
|
|
117
|
+
const res = meta.expression(em, where, options);
|
|
118
|
+
if (res instanceof query_1.QueryBuilder) {
|
|
119
|
+
return this.wrapVirtualExpressionInSubquery(meta, res.getFormattedQuery(), where, options, query_1.QueryType.COUNT);
|
|
120
|
+
}
|
|
121
|
+
/* istanbul ignore next */
|
|
122
|
+
return res;
|
|
123
|
+
}
|
|
124
|
+
async wrapVirtualExpressionInSubquery(meta, expression, where, options, type = query_1.QueryType.SELECT) {
|
|
125
|
+
const qb = this.createQueryBuilder(meta.className, options?.ctx, options.connectionType, options.convertCustomTypes)
|
|
126
|
+
.limit(options?.limit, options?.offset)
|
|
127
|
+
.indexHint(options.indexHint)
|
|
128
|
+
.comment(options.comments)
|
|
129
|
+
.hintComment(options.hintComments);
|
|
130
|
+
if (options.orderBy) {
|
|
131
|
+
qb.orderBy(options.orderBy);
|
|
132
|
+
}
|
|
133
|
+
qb.where(where);
|
|
134
|
+
const kqb = qb.getKnexQuery().clear('select');
|
|
135
|
+
if (type === query_1.QueryType.COUNT) {
|
|
136
|
+
kqb.select(this.connection.getKnex().raw('count(*) as count'));
|
|
137
|
+
}
|
|
138
|
+
else { // select
|
|
139
|
+
kqb.select('*');
|
|
140
|
+
}
|
|
141
|
+
kqb.fromRaw(`(${expression}) as ${this.platform.quoteIdentifier(qb.alias)}`);
|
|
142
|
+
const res = await this.execute(kqb);
|
|
143
|
+
if (type === query_1.QueryType.COUNT) {
|
|
144
|
+
return res[0].count;
|
|
145
|
+
}
|
|
146
|
+
return res.map(row => this.mapResult(row, meta));
|
|
147
|
+
}
|
|
63
148
|
mapResult(result, meta, populate = [], qb, map = {}) {
|
|
64
149
|
const ret = super.mapResult(result, meta);
|
|
65
150
|
/* istanbul ignore if */
|
|
@@ -90,11 +175,12 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
90
175
|
}));
|
|
91
176
|
if (!hasPK) {
|
|
92
177
|
// initialize empty collections
|
|
93
|
-
if ([core_1.
|
|
94
|
-
result[relation.name]
|
|
178
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
179
|
+
result[relation.name] ??= [];
|
|
95
180
|
}
|
|
96
181
|
return;
|
|
97
182
|
}
|
|
183
|
+
const tz = this.platform.getTimezone();
|
|
98
184
|
meta2.props
|
|
99
185
|
.filter(prop => this.platform.shouldHaveColumn(prop, p.children || []))
|
|
100
186
|
.forEach(prop => {
|
|
@@ -102,6 +188,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
102
188
|
relationPojo[prop.name] = prop.fieldNames.map(name => root[`${relationAlias}__${name}`]);
|
|
103
189
|
prop.fieldNames.map(name => delete root[`${relationAlias}__${name}`]);
|
|
104
190
|
}
|
|
191
|
+
else if (prop.runtimeType === 'Date') {
|
|
192
|
+
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
193
|
+
relationPojo[prop.name] = (typeof root[alias] === 'string' ? new Date(root[alias]) : root[alias]);
|
|
194
|
+
delete root[alias];
|
|
195
|
+
}
|
|
105
196
|
else {
|
|
106
197
|
const alias = `${relationAlias}__${prop.fieldNames[0]}`;
|
|
107
198
|
relationPojo[prop.name] = root[alias];
|
|
@@ -115,8 +206,8 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
115
206
|
else {
|
|
116
207
|
map[key] = result;
|
|
117
208
|
}
|
|
118
|
-
if ([core_1.
|
|
119
|
-
result[relation.name]
|
|
209
|
+
if ([core_1.ReferenceKind.MANY_TO_MANY, core_1.ReferenceKind.ONE_TO_MANY].includes(relation.kind)) {
|
|
210
|
+
result[relation.name] ??= [];
|
|
120
211
|
this.appendToCollection(meta2, result[relation.name], relationPojo);
|
|
121
212
|
}
|
|
122
213
|
else {
|
|
@@ -137,23 +228,28 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
137
228
|
collection.push(relationPojo);
|
|
138
229
|
}
|
|
139
230
|
}
|
|
140
|
-
async count(entityName, where, options = {}
|
|
231
|
+
async count(entityName, where, options = {}) {
|
|
141
232
|
const meta = this.metadata.find(entityName);
|
|
142
|
-
|
|
143
|
-
|
|
233
|
+
if (meta?.virtual) {
|
|
234
|
+
return this.countVirtual(entityName, where, options);
|
|
235
|
+
}
|
|
236
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, options.connectionType, false)
|
|
237
|
+
.indexHint(options.indexHint)
|
|
238
|
+
.comment(options.comments)
|
|
239
|
+
.hintComment(options.hintComments)
|
|
144
240
|
.groupBy(options.groupBy)
|
|
145
241
|
.having(options.having)
|
|
146
242
|
.populate(options.populate ?? [])
|
|
147
243
|
.withSchema(this.getSchemaName(meta, options))
|
|
148
244
|
.where(where);
|
|
149
|
-
return this.rethrow(qb.getCount(
|
|
245
|
+
return this.rethrow(qb.getCount());
|
|
150
246
|
}
|
|
151
247
|
async nativeInsert(entityName, data, options = {}) {
|
|
152
|
-
options.convertCustomTypes
|
|
248
|
+
options.convertCustomTypes ??= true;
|
|
153
249
|
const meta = this.metadata.find(entityName);
|
|
154
250
|
const collections = this.extractManyToMany(entityName, data);
|
|
155
|
-
const pks = this.
|
|
156
|
-
const qb = this.createQueryBuilder(entityName, options.ctx,
|
|
251
|
+
const pks = meta?.primaryKeys ?? [this.config.getNamingStrategy().referenceColumnName()];
|
|
252
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
157
253
|
const res = await this.rethrow(qb.insert(data).execute('run', false));
|
|
158
254
|
res.row = res.row || {};
|
|
159
255
|
let pk;
|
|
@@ -161,68 +257,90 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
161
257
|
pk = core_1.Utils.getPrimaryKeyCond(data, pks);
|
|
162
258
|
}
|
|
163
259
|
else {
|
|
164
|
-
|
|
260
|
+
/* istanbul ignore next */
|
|
261
|
+
res.insertId = data[pks[0]] ?? res.insertId ?? res.row[pks[0]];
|
|
165
262
|
pk = [res.insertId];
|
|
166
263
|
}
|
|
167
264
|
await this.processManyToMany(meta, pk, collections, false, options);
|
|
168
265
|
return res;
|
|
169
266
|
}
|
|
170
267
|
async nativeInsertMany(entityName, data, options = {}) {
|
|
171
|
-
options.processCollections
|
|
172
|
-
options.convertCustomTypes
|
|
173
|
-
const meta = this.metadata.
|
|
268
|
+
options.processCollections ??= true;
|
|
269
|
+
options.convertCustomTypes ??= true;
|
|
270
|
+
const meta = this.metadata.find(entityName);
|
|
174
271
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
175
272
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
176
273
|
const set = new Set();
|
|
177
|
-
data.forEach(row =>
|
|
178
|
-
const props = [...set].map(name => meta
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
274
|
+
data.forEach(row => core_1.Utils.keys(row).forEach(k => set.add(k)));
|
|
275
|
+
const props = [...set].map(name => meta?.properties[name] ?? { name, fieldNames: [name] });
|
|
276
|
+
let fields = core_1.Utils.flatten(props.map(prop => prop.fieldNames));
|
|
277
|
+
const duplicates = core_1.Utils.findDuplicates(fields);
|
|
278
|
+
const params = [];
|
|
279
|
+
if (duplicates.length) {
|
|
280
|
+
fields = core_1.Utils.unique(fields);
|
|
281
|
+
}
|
|
282
|
+
/* istanbul ignore next */
|
|
283
|
+
const tableName = meta ? this.getTableName(meta, options) : this.platform.quoteIdentifier(entityName);
|
|
284
|
+
let sql = `insert into ${tableName} `;
|
|
285
|
+
sql += fields.length > 0 ? '(' + fields.map(k => this.platform.quoteIdentifier(k)).join(', ') + ')' : `(${this.platform.quoteIdentifier(pks[0])})`;
|
|
286
|
+
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
287
|
+
sql += ' values ';
|
|
184
288
|
}
|
|
185
289
|
else {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
290
|
+
sql += ' ' + data.map(() => `select null as ${this.platform.quoteIdentifier(pks[0])}`).join(' union all ');
|
|
291
|
+
}
|
|
292
|
+
const addParams = (prop, row) => {
|
|
293
|
+
if (options.convertCustomTypes && prop.customType) {
|
|
294
|
+
params.push(prop.customType.convertToDatabaseValue(row[prop.name], this.platform, { key: prop.name, mode: 'query-data' }));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
params.push(row[prop.name]);
|
|
298
|
+
};
|
|
299
|
+
if (fields.length > 0 || this.platform.usesDefaultKeyword()) {
|
|
191
300
|
sql += data.map(row => {
|
|
192
301
|
const keys = [];
|
|
193
302
|
props.forEach(prop => {
|
|
194
303
|
if (prop.fieldNames.length > 1) {
|
|
195
|
-
|
|
196
|
-
|
|
304
|
+
const param = [...row[prop.name] ?? prop.fieldNames.map(() => null)];
|
|
305
|
+
const key = (row[prop.name] ?? prop.fieldNames).map(() => '?');
|
|
306
|
+
prop.fieldNames.forEach((field, idx) => {
|
|
307
|
+
if (duplicates.includes(field)) {
|
|
308
|
+
param.splice(idx, 1);
|
|
309
|
+
key.splice(idx, 1);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
params.push(...param);
|
|
313
|
+
keys.push(...key);
|
|
197
314
|
}
|
|
198
315
|
else if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(row[prop.name])) {
|
|
199
316
|
keys.push(prop.customType.convertToDatabaseValueSQL('?', this.platform));
|
|
200
|
-
|
|
317
|
+
addParams(prop, row);
|
|
201
318
|
}
|
|
202
319
|
else {
|
|
203
|
-
|
|
320
|
+
addParams(prop, row);
|
|
204
321
|
keys.push('?');
|
|
205
322
|
}
|
|
206
323
|
});
|
|
207
|
-
return '(' + keys.join(', ') + ')';
|
|
324
|
+
return '(' + (keys.join(', ') || 'default') + ')';
|
|
208
325
|
}).join(', ');
|
|
209
|
-
if (this.platform.usesReturningStatement()) {
|
|
210
|
-
/* istanbul ignore next */
|
|
211
|
-
const returningProps = meta.props.filter(prop => prop.primary || prop.defaultRaw);
|
|
212
|
-
const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
213
|
-
/* istanbul ignore next */
|
|
214
|
-
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
215
|
-
}
|
|
216
|
-
res = await this.execute(sql, params, 'run', options.ctx);
|
|
217
326
|
}
|
|
327
|
+
if (meta && this.platform.usesReturningStatement()) {
|
|
328
|
+
const returningProps = meta.props
|
|
329
|
+
.filter(prop => prop.persist !== false && ((prop.primary && prop.autoincrement) || prop.defaultRaw))
|
|
330
|
+
.filter(prop => !(prop.name in data[0]) || core_1.Utils.isRawSql(data[0][prop.name]));
|
|
331
|
+
const returningFields = core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames));
|
|
332
|
+
/* istanbul ignore next */
|
|
333
|
+
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
334
|
+
}
|
|
335
|
+
const res = await this.execute(sql, params, 'run', options.ctx);
|
|
218
336
|
let pk;
|
|
219
337
|
/* istanbul ignore next */
|
|
220
338
|
if (pks.length > 1) { // owner has composite pk
|
|
221
339
|
pk = data.map(d => core_1.Utils.getPrimaryKeyCond(d, pks));
|
|
222
340
|
}
|
|
223
341
|
else {
|
|
224
|
-
res.row
|
|
225
|
-
res.rows
|
|
342
|
+
res.row ??= {};
|
|
343
|
+
res.rows ??= [];
|
|
226
344
|
pk = data.map((d, i) => d[pks[0]] ?? res.rows[i]?.[pks[0]]).map(d => [d]);
|
|
227
345
|
res.insertId = res.insertId || res.row[pks[0]];
|
|
228
346
|
}
|
|
@@ -232,19 +350,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
232
350
|
return res;
|
|
233
351
|
}
|
|
234
352
|
async nativeUpdate(entityName, where, data, options = {}) {
|
|
235
|
-
options.convertCustomTypes
|
|
353
|
+
options.convertCustomTypes ??= true;
|
|
236
354
|
const meta = this.metadata.find(entityName);
|
|
237
355
|
const pks = this.getPrimaryKeyFields(entityName);
|
|
238
356
|
const collections = this.extractManyToMany(entityName, data);
|
|
239
357
|
let res = { affectedRows: 0, insertId: 0, row: {} };
|
|
240
358
|
if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
|
|
241
|
-
|
|
359
|
+
/* istanbul ignore next */
|
|
360
|
+
where = { [meta?.primaryKeys[0] ?? pks[0]]: where };
|
|
242
361
|
}
|
|
243
362
|
if (core_1.Utils.hasObjectKeys(data)) {
|
|
244
|
-
const qb = this.createQueryBuilder(entityName, options.ctx,
|
|
245
|
-
.
|
|
246
|
-
|
|
247
|
-
|
|
363
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes)
|
|
364
|
+
.withSchema(this.getSchemaName(meta, options));
|
|
365
|
+
if (options.upsert) {
|
|
366
|
+
/* istanbul ignore next */
|
|
367
|
+
const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where) ? core_1.Utils.keys(where) : meta.primaryKeys);
|
|
368
|
+
const returning = (0, core_1.getOnConflictReturningFields)(meta, data, uniqueFields, options);
|
|
369
|
+
qb.insert(data)
|
|
370
|
+
.onConflict(uniqueFields)
|
|
371
|
+
.returning(returning);
|
|
372
|
+
if (!options.onConflictAction || options.onConflictAction === 'merge') {
|
|
373
|
+
const fields = (0, core_1.getOnConflictFields)(data, uniqueFields, options);
|
|
374
|
+
qb.merge(fields);
|
|
375
|
+
}
|
|
376
|
+
if (options.onConflictAction === 'ignore') {
|
|
377
|
+
qb.ignore();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
qb.update(data).where(where);
|
|
382
|
+
}
|
|
248
383
|
res = await this.rethrow(qb.execute('run', false));
|
|
249
384
|
}
|
|
250
385
|
/* istanbul ignore next */
|
|
@@ -253,12 +388,36 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
253
388
|
return res;
|
|
254
389
|
}
|
|
255
390
|
async nativeUpdateMany(entityName, where, data, options = {}) {
|
|
256
|
-
options.processCollections
|
|
257
|
-
options.convertCustomTypes
|
|
391
|
+
options.processCollections ??= true;
|
|
392
|
+
options.convertCustomTypes ??= true;
|
|
258
393
|
const meta = this.metadata.get(entityName);
|
|
394
|
+
if (options.upsert) {
|
|
395
|
+
const uniqueFields = options.onConflictFields ?? (core_1.Utils.isPlainObject(where[0]) ? Object.keys(where[0]) : meta.primaryKeys);
|
|
396
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', options.convertCustomTypes).withSchema(this.getSchemaName(meta, options));
|
|
397
|
+
const returning = (0, core_1.getOnConflictReturningFields)(meta, data[0], uniqueFields, options);
|
|
398
|
+
qb.insert(data)
|
|
399
|
+
.onConflict(uniqueFields)
|
|
400
|
+
.returning(returning);
|
|
401
|
+
if (!options.onConflictAction || options.onConflictAction === 'merge') {
|
|
402
|
+
const fields = (0, core_1.getOnConflictFields)(data[0], uniqueFields, options);
|
|
403
|
+
qb.merge(fields);
|
|
404
|
+
}
|
|
405
|
+
if (options.onConflictAction === 'ignore') {
|
|
406
|
+
qb.ignore();
|
|
407
|
+
}
|
|
408
|
+
return qb.execute('run', false);
|
|
409
|
+
}
|
|
259
410
|
const collections = options.processCollections ? data.map(d => this.extractManyToMany(entityName, d)) : [];
|
|
260
411
|
const keys = new Set();
|
|
261
|
-
|
|
412
|
+
const returning = new Set();
|
|
413
|
+
data.forEach(row => {
|
|
414
|
+
core_1.Utils.keys(row).forEach(k => {
|
|
415
|
+
keys.add(k);
|
|
416
|
+
if (core_1.Utils.isRawSql(row[k])) {
|
|
417
|
+
returning.add(k);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
});
|
|
262
421
|
const pkCond = core_1.Utils.flatten(meta.primaryKeys.map(pk => meta.properties[pk].fieldNames)).map(pk => `${this.platform.quoteIdentifier(pk)} = ?`).join(' and ');
|
|
263
422
|
const params = [];
|
|
264
423
|
let sql = `update ${this.getTableName(meta, options)} set `;
|
|
@@ -276,7 +435,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
276
435
|
else {
|
|
277
436
|
sql += '?';
|
|
278
437
|
}
|
|
279
|
-
params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key][fieldNameIdx] : data[idx][key]);
|
|
438
|
+
params.push(...pks, prop.fieldNames.length > 1 ? data[idx][key]?.[fieldNameIdx] : data[idx][key]);
|
|
280
439
|
}
|
|
281
440
|
});
|
|
282
441
|
sql += ` else ${this.platform.quoteIdentifier(fieldName)} end, `;
|
|
@@ -287,7 +446,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
287
446
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
288
447
|
const quotedFieldName = this.platform.quoteIdentifier(versionProperty.fieldNames[0]);
|
|
289
448
|
sql += `${quotedFieldName} = `;
|
|
290
|
-
if (versionProperty.
|
|
449
|
+
if (versionProperty.runtimeType === 'Date') {
|
|
291
450
|
sql += this.platform.getCurrentTimestampSQL(versionProperty.length);
|
|
292
451
|
}
|
|
293
452
|
else {
|
|
@@ -300,15 +459,29 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
300
459
|
const pks = core_1.Utils.flatten(pkProps.map(pk => meta.properties[pk].fieldNames));
|
|
301
460
|
sql += pks.length > 1 ? `(${pks.map(pk => `${this.platform.quoteIdentifier(pk)}`).join(', ')})` : this.platform.quoteIdentifier(pks[0]);
|
|
302
461
|
const conds = where.map(cond => {
|
|
462
|
+
if (core_1.Utils.isPlainObject(cond) && core_1.Utils.getObjectKeysSize(cond) === 1) {
|
|
463
|
+
cond = Object.values(cond)[0];
|
|
464
|
+
}
|
|
303
465
|
if (pks.length > 1) {
|
|
304
|
-
pkProps.forEach(pk =>
|
|
466
|
+
pkProps.forEach(pk => {
|
|
467
|
+
if (Array.isArray(cond[pk])) {
|
|
468
|
+
params.push(...core_1.Utils.flatten(cond[pk]));
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
params.push(cond[pk]);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
305
474
|
return `(${new Array(pks.length).fill('?').join(', ')})`;
|
|
306
475
|
}
|
|
307
476
|
params.push(cond);
|
|
308
477
|
return '?';
|
|
309
478
|
});
|
|
310
|
-
|
|
311
|
-
|
|
479
|
+
sql += ` in (${conds.join(', ')})`;
|
|
480
|
+
if (this.platform.usesReturningStatement() && returning.size > 0) {
|
|
481
|
+
const returningFields = core_1.Utils.flatten([...returning].map(prop => meta.properties[prop].fieldNames));
|
|
482
|
+
/* istanbul ignore next */
|
|
483
|
+
sql += returningFields.length > 0 ? ` returning ${returningFields.map(field => this.platform.quoteIdentifier(field)).join(', ')}` : '';
|
|
484
|
+
}
|
|
312
485
|
const res = await this.rethrow(this.execute(sql, params, 'run', options.ctx));
|
|
313
486
|
for (let i = 0; i < collections.length; i++) {
|
|
314
487
|
await this.processManyToMany(meta, where[i], collections[i], false, options);
|
|
@@ -321,17 +494,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
321
494
|
if (core_1.Utils.isPrimaryKey(where) && pks.length === 1) {
|
|
322
495
|
where = { [pks[0]]: where };
|
|
323
496
|
}
|
|
324
|
-
const qb = this.createQueryBuilder(entityName, options.ctx,
|
|
497
|
+
const qb = this.createQueryBuilder(entityName, options.ctx, 'write', false).delete(where).withSchema(this.getSchemaName(meta, options));
|
|
325
498
|
return this.rethrow(qb.execute('run', false));
|
|
326
499
|
}
|
|
327
500
|
async syncCollection(coll, options) {
|
|
328
|
-
const wrapped = coll.owner
|
|
501
|
+
const wrapped = (0, core_1.helper)(coll.owner);
|
|
329
502
|
const meta = wrapped.__meta;
|
|
330
503
|
const pks = wrapped.getPrimaryKeys(true);
|
|
331
504
|
const snap = coll.getSnapshot();
|
|
332
505
|
const includes = (arr, item) => !!arr.find(i => core_1.Utils.equals(i, item));
|
|
333
|
-
const snapshot = snap ? snap.map(item => item.
|
|
334
|
-
const current = coll.getItems(false).map(item => item.
|
|
506
|
+
const snapshot = snap ? snap.map(item => (0, core_1.helper)(item).getPrimaryKeys(true)) : [];
|
|
507
|
+
const current = coll.getItems(false).map(item => (0, core_1.helper)(item).getPrimaryKeys(true));
|
|
335
508
|
const deleteDiff = snap ? snapshot.filter(item => !includes(current, item)) : true;
|
|
336
509
|
const insertDiff = current.filter(item => !includes(snapshot, item));
|
|
337
510
|
const target = snapshot.filter(item => includes(current, item)).concat(...insertDiff);
|
|
@@ -343,29 +516,53 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
343
516
|
deleteDiff.push(...snapshot);
|
|
344
517
|
insertDiff.push(...current);
|
|
345
518
|
}
|
|
346
|
-
if (coll.property.
|
|
519
|
+
if (coll.property.kind === core_1.ReferenceKind.ONE_TO_MANY) {
|
|
347
520
|
const cols = coll.property.referencedColumnNames;
|
|
348
|
-
const qb = this.createQueryBuilder(coll.property.type, ctx,
|
|
349
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
350
|
-
|
|
521
|
+
const qb = this.createQueryBuilder(coll.property.type, ctx, 'write')
|
|
522
|
+
.withSchema(this.getSchemaName(meta, options));
|
|
523
|
+
if (coll.getSnapshot() === undefined) {
|
|
524
|
+
if (coll.property.orphanRemoval) {
|
|
525
|
+
const kqb = qb.delete({ [coll.property.mappedBy]: pks })
|
|
526
|
+
.getKnexQuery()
|
|
527
|
+
.whereNotIn(cols, insertDiff);
|
|
528
|
+
return this.rethrow(this.execute(kqb));
|
|
529
|
+
}
|
|
530
|
+
const kqb = qb.update({ [coll.property.mappedBy]: null })
|
|
531
|
+
.getKnexQuery()
|
|
532
|
+
.whereNotIn(cols, insertDiff);
|
|
533
|
+
return this.rethrow(this.execute(kqb));
|
|
534
|
+
}
|
|
535
|
+
const kqb = qb.update({ [coll.property.mappedBy]: pks })
|
|
351
536
|
.getKnexQuery()
|
|
352
537
|
.whereIn(cols, insertDiff);
|
|
353
|
-
return this.rethrow(this.execute(
|
|
538
|
+
return this.rethrow(this.execute(kqb));
|
|
354
539
|
}
|
|
355
540
|
/* istanbul ignore next */
|
|
356
541
|
const ownerSchema = wrapped.getSchema() === '*' ? this.config.get('schema') : wrapped.getSchema();
|
|
357
|
-
const pivotMeta = this.metadata.find(coll.property.
|
|
542
|
+
const pivotMeta = this.metadata.find(coll.property.pivotEntity);
|
|
358
543
|
if (pivotMeta.schema === '*') {
|
|
359
544
|
/* istanbul ignore next */
|
|
360
|
-
options
|
|
545
|
+
options ??= {};
|
|
361
546
|
options.schema = ownerSchema;
|
|
362
547
|
}
|
|
363
548
|
return this.rethrow(this.updateCollectionDiff(meta, coll.property, pks, deleteDiff, insertDiff, options));
|
|
364
549
|
}
|
|
365
550
|
async loadFromPivotTable(prop, owners, where = {}, orderBy, ctx, options) {
|
|
366
551
|
const pivotProp2 = this.getPivotInverseProperty(prop);
|
|
552
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy ?? prop.inversedBy];
|
|
367
553
|
const ownerMeta = this.metadata.find(pivotProp2.type);
|
|
368
|
-
const
|
|
554
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
555
|
+
options = { ...options };
|
|
556
|
+
const qb = this.createQueryBuilder(prop.type, ctx, options.connectionType)
|
|
557
|
+
.withSchema(this.getSchemaName(prop.targetMeta, options))
|
|
558
|
+
.indexHint(options.indexHint)
|
|
559
|
+
.comment(options.comments)
|
|
560
|
+
.hintComment(options.hintComments);
|
|
561
|
+
const pivotAlias = qb.getNextAlias(pivotMeta.tableName);
|
|
562
|
+
const pivotKey = pivotProp2.joinColumns.map(column => `${pivotAlias}.${column}`).join(core_1.Utils.PK_SEPARATOR);
|
|
563
|
+
const cond = {
|
|
564
|
+
[pivotKey]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) },
|
|
565
|
+
};
|
|
369
566
|
/* istanbul ignore if */
|
|
370
567
|
if (!core_1.Utils.isEmpty(where) && Object.keys(where).every(k => core_1.Utils.isOperator(k, false))) {
|
|
371
568
|
where = cond;
|
|
@@ -373,16 +570,34 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
373
570
|
else {
|
|
374
571
|
where = { ...where, ...cond };
|
|
375
572
|
}
|
|
376
|
-
orderBy = this.getPivotOrderBy(prop, orderBy);
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
573
|
+
orderBy = this.getPivotOrderBy(prop, pivotAlias, orderBy);
|
|
574
|
+
const populate = this.autoJoinOneToOneOwner(prop.targetMeta, []);
|
|
575
|
+
const fields = this.buildFields(prop.targetMeta, (options.populate ?? []), [], qb, options.fields);
|
|
576
|
+
const k1 = !prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
577
|
+
const k2 = prop.owner ? 'joinColumns' : 'inverseJoinColumns';
|
|
578
|
+
const cols = [
|
|
579
|
+
...prop[k1].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
580
|
+
...prop[k2].map(col => `${pivotAlias}.${col} as fk__${col}`),
|
|
581
|
+
];
|
|
582
|
+
fields.push(...cols);
|
|
583
|
+
qb.select(fields)
|
|
584
|
+
.join(prop2.name, pivotAlias, {}, 'pivotJoin', undefined, options.schema)
|
|
585
|
+
.populate(populate)
|
|
586
|
+
.where(where)
|
|
587
|
+
.orderBy(orderBy)
|
|
588
|
+
.setLockMode(options.lockMode, options.lockTableAliases);
|
|
589
|
+
if (owners.length === 1 && (options.offset != null || options.limit != null)) {
|
|
384
590
|
qb.limit(options.limit, options.offset);
|
|
385
591
|
}
|
|
592
|
+
const schema = this.config.get('schema');
|
|
593
|
+
if (prop.targetMeta.schema !== '*' && pivotMeta.schema === '*' && options.schema) {
|
|
594
|
+
// eslint-disable-next-line dot-notation
|
|
595
|
+
qb['finalize']();
|
|
596
|
+
// eslint-disable-next-line dot-notation
|
|
597
|
+
Object.values(qb['_joins']).forEach(join => {
|
|
598
|
+
join.schema = schema;
|
|
599
|
+
});
|
|
600
|
+
}
|
|
386
601
|
const items = owners.length ? await this.rethrow(qb.execute('all')) : [];
|
|
387
602
|
const map = {};
|
|
388
603
|
const pkProps = ownerMeta.getPrimaryProps();
|
|
@@ -404,52 +619,62 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
404
619
|
});
|
|
405
620
|
return map;
|
|
406
621
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
getTableName(meta, options) {
|
|
411
|
-
let tableName = this.platform.quoteIdentifier(meta.collection);
|
|
412
|
-
if (options.schema === '*') {
|
|
413
|
-
return this.platform.quoteIdentifier(this.config.get('schema')) + '.' + tableName;
|
|
622
|
+
getPivotOrderBy(prop, pivotAlias, orderBy) {
|
|
623
|
+
if (!core_1.Utils.isEmpty(orderBy)) {
|
|
624
|
+
return orderBy;
|
|
414
625
|
}
|
|
415
|
-
if (
|
|
416
|
-
|
|
626
|
+
if (!core_1.Utils.isEmpty(prop.orderBy)) {
|
|
627
|
+
return core_1.Utils.asArray(prop.orderBy);
|
|
417
628
|
}
|
|
418
|
-
|
|
629
|
+
if (prop.fixedOrder) {
|
|
630
|
+
return [{ [`${pivotAlias}.${prop.fixedOrderColumn}`]: core_1.QueryOrder.ASC }];
|
|
631
|
+
}
|
|
632
|
+
return [];
|
|
633
|
+
}
|
|
634
|
+
async execute(queryOrKnex, params = [], method = 'all', ctx) {
|
|
635
|
+
return this.rethrow(this.connection.execute(queryOrKnex, params, method, ctx));
|
|
419
636
|
}
|
|
420
637
|
/**
|
|
421
638
|
* 1:1 owner side needs to be marked for population so QB auto-joins the owner id
|
|
422
639
|
*/
|
|
423
640
|
autoJoinOneToOneOwner(meta, populate, fields = []) {
|
|
424
|
-
if (!this.config.get('autoJoinOneToOneOwner')
|
|
641
|
+
if (!this.config.get('autoJoinOneToOneOwner')) {
|
|
425
642
|
return populate;
|
|
426
643
|
}
|
|
427
644
|
const relationsToPopulate = populate.map(({ field }) => field);
|
|
428
645
|
const toPopulate = meta.relations
|
|
429
|
-
.filter(prop => prop.
|
|
646
|
+
.filter(prop => prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner && !relationsToPopulate.includes(prop.name))
|
|
647
|
+
.filter(prop => fields.length === 0 || fields.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
|
|
430
648
|
.map(prop => ({ field: prop.name, strategy: prop.strategy }));
|
|
431
649
|
return [...populate, ...toPopulate];
|
|
432
650
|
}
|
|
433
651
|
joinedProps(meta, populate) {
|
|
434
652
|
return populate.filter(p => {
|
|
435
653
|
const prop = meta.properties[p.field] || {};
|
|
436
|
-
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.
|
|
654
|
+
return (p.strategy || prop.strategy || this.config.get('loadStrategy')) === core_1.LoadStrategy.JOINED && prop.kind !== core_1.ReferenceKind.SCALAR;
|
|
437
655
|
});
|
|
438
656
|
}
|
|
439
657
|
/**
|
|
440
658
|
* @internal
|
|
441
659
|
*/
|
|
442
|
-
mergeJoinedResult(rawResults, meta) {
|
|
660
|
+
mergeJoinedResult(rawResults, meta, joinedProps) {
|
|
443
661
|
// group by the root entity primary key first
|
|
444
662
|
const map = {};
|
|
445
663
|
const res = [];
|
|
446
664
|
rawResults.forEach(item => {
|
|
447
665
|
const pk = core_1.Utils.getCompositeKeyHash(item, meta);
|
|
448
666
|
if (map[pk]) {
|
|
449
|
-
|
|
667
|
+
joinedProps.forEach(hint => {
|
|
668
|
+
// Sometimes we might join a M:N relation with additional filter on the target entity, and as a result, we get
|
|
669
|
+
// the first result with `null` for all target values, which is mapped as empty array. When we see that happen,
|
|
670
|
+
// we need to merge the results of the next item.
|
|
671
|
+
if (Array.isArray(map[pk][hint.field]) && Array.isArray(item[hint.field]) && map[pk][hint.field].length === 0) {
|
|
672
|
+
item[hint.field].forEach((el) => map[pk][hint.field].push(el));
|
|
673
|
+
}
|
|
674
|
+
});
|
|
450
675
|
}
|
|
451
676
|
else {
|
|
452
|
-
map[pk] =
|
|
677
|
+
map[pk] = item;
|
|
453
678
|
res.push(item);
|
|
454
679
|
}
|
|
455
680
|
});
|
|
@@ -458,14 +683,17 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
458
683
|
getFieldsForJoinedLoad(qb, meta, explicitFields, populate = [], parentTableAlias, parentJoinPath) {
|
|
459
684
|
const fields = [];
|
|
460
685
|
const joinedProps = this.joinedProps(meta, populate);
|
|
686
|
+
if (explicitFields?.includes('*')) {
|
|
687
|
+
fields.push('*');
|
|
688
|
+
}
|
|
461
689
|
const shouldHaveColumn = (prop, populate, fields) => {
|
|
462
690
|
if (!this.platform.shouldHaveColumn(prop, populate)) {
|
|
463
691
|
return false;
|
|
464
692
|
}
|
|
465
693
|
if (!fields || prop.primary) {
|
|
466
|
-
return
|
|
694
|
+
return !fields?.includes('*');
|
|
467
695
|
}
|
|
468
|
-
return fields.
|
|
696
|
+
return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
|
|
469
697
|
};
|
|
470
698
|
// alias all fields in the primary table
|
|
471
699
|
meta.props
|
|
@@ -492,34 +720,49 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
492
720
|
* @internal
|
|
493
721
|
*/
|
|
494
722
|
mapPropToFieldNames(qb, prop, tableAlias) {
|
|
495
|
-
const
|
|
723
|
+
const knex = this.connection.getKnex();
|
|
724
|
+
const aliased = knex.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
|
|
496
725
|
if (prop.customType?.convertToJSValueSQL && tableAlias) {
|
|
497
|
-
const prefixed =
|
|
498
|
-
return [
|
|
726
|
+
const prefixed = knex.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
|
|
727
|
+
return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
|
|
499
728
|
}
|
|
500
729
|
if (prop.formula) {
|
|
501
|
-
const alias =
|
|
502
|
-
return [`${prop.formula(alias)} as ${aliased}`];
|
|
730
|
+
const alias = knex.ref(tableAlias ?? qb.alias).toString();
|
|
731
|
+
return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
|
|
503
732
|
}
|
|
504
733
|
if (tableAlias) {
|
|
505
|
-
return prop.fieldNames.map(fieldName =>
|
|
734
|
+
return prop.fieldNames.map(fieldName => knex.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
|
|
506
735
|
}
|
|
507
736
|
return prop.fieldNames;
|
|
508
737
|
}
|
|
509
|
-
|
|
510
|
-
|
|
738
|
+
/** @internal */
|
|
739
|
+
createQueryBuilder(entityName, ctx, preferredConnectionType, convertCustomTypes, logging) {
|
|
740
|
+
const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
|
|
741
|
+
const qb = new query_1.QueryBuilder(entityName, this.metadata, this, ctx, undefined, connectionType, undefined, logging);
|
|
511
742
|
if (!convertCustomTypes) {
|
|
512
743
|
qb.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
513
744
|
}
|
|
514
745
|
return qb;
|
|
515
746
|
}
|
|
747
|
+
resolveConnectionType(args) {
|
|
748
|
+
if (args.ctx) {
|
|
749
|
+
return 'write';
|
|
750
|
+
}
|
|
751
|
+
else if (args.connectionType) {
|
|
752
|
+
return args.connectionType;
|
|
753
|
+
}
|
|
754
|
+
else if (this.config.get('preferReadReplicas') === true) {
|
|
755
|
+
return 'read';
|
|
756
|
+
}
|
|
757
|
+
return 'write';
|
|
758
|
+
}
|
|
516
759
|
extractManyToMany(entityName, data) {
|
|
517
760
|
if (!this.metadata.has(entityName)) {
|
|
518
761
|
return {};
|
|
519
762
|
}
|
|
520
763
|
const ret = {};
|
|
521
764
|
this.metadata.find(entityName).relations.forEach(prop => {
|
|
522
|
-
if (prop.
|
|
765
|
+
if (prop.kind === core_1.ReferenceKind.MANY_TO_MANY && data[prop.name]) {
|
|
523
766
|
ret[prop.name] = data[prop.name].map((item) => core_1.Utils.asArray(item));
|
|
524
767
|
delete data[prop.name];
|
|
525
768
|
}
|
|
@@ -540,10 +783,9 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
540
783
|
if (!deleteDiff) {
|
|
541
784
|
deleteDiff = [];
|
|
542
785
|
}
|
|
786
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
543
787
|
if (deleteDiff === true || deleteDiff.length > 0) {
|
|
544
|
-
const qb1 = this.createQueryBuilder(prop.
|
|
545
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
546
|
-
.unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES);
|
|
788
|
+
const qb1 = this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write').withSchema(this.getSchemaName(pivotMeta, options));
|
|
547
789
|
const knex = qb1.getKnex();
|
|
548
790
|
if (Array.isArray(deleteDiff)) {
|
|
549
791
|
knex.whereIn(prop.inverseJoinColumns, deleteDiff);
|
|
@@ -562,23 +804,26 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
562
804
|
});
|
|
563
805
|
/* istanbul ignore else */
|
|
564
806
|
if (this.platform.allowsMultiInsert()) {
|
|
565
|
-
await this.nativeInsertMany(prop.
|
|
807
|
+
await this.nativeInsertMany(prop.pivotEntity, items, {
|
|
808
|
+
...options,
|
|
809
|
+
convertCustomTypes: false,
|
|
810
|
+
processCollections: false,
|
|
811
|
+
});
|
|
566
812
|
}
|
|
567
813
|
else {
|
|
568
814
|
await core_1.Utils.runSerial(items, item => {
|
|
569
|
-
return this.createQueryBuilder(prop.
|
|
570
|
-
.
|
|
571
|
-
.withSchema(this.getSchemaName(meta, options))
|
|
815
|
+
return this.createQueryBuilder(prop.pivotEntity, options?.ctx, 'write')
|
|
816
|
+
.withSchema(this.getSchemaName(pivotMeta, options))
|
|
572
817
|
.insert(item)
|
|
573
818
|
.execute('run', false);
|
|
574
819
|
});
|
|
575
820
|
}
|
|
576
821
|
}
|
|
577
822
|
async lockPessimistic(entity, options) {
|
|
578
|
-
const meta = entity.
|
|
579
|
-
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).
|
|
823
|
+
const meta = (0, core_1.helper)(entity).__meta;
|
|
824
|
+
const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).withSchema(options.schema ?? meta.schema);
|
|
580
825
|
const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
|
|
581
|
-
qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
826
|
+
qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
|
|
582
827
|
await this.rethrow(qb.execute());
|
|
583
828
|
}
|
|
584
829
|
buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
|
|
@@ -590,7 +835,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
590
835
|
const path = `${parentPath ? parentPath : entityName}.${relation.field}`;
|
|
591
836
|
const propAlias = qb.getAliasForJoinPath(path);
|
|
592
837
|
if (propOrderBy) {
|
|
593
|
-
|
|
838
|
+
core_1.Utils.keys(propOrderBy).forEach(field => {
|
|
594
839
|
orderBy.push({ [`${propAlias}.${field}`]: propOrderBy[field] });
|
|
595
840
|
});
|
|
596
841
|
}
|
|
@@ -601,44 +846,92 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
|
|
|
601
846
|
});
|
|
602
847
|
return orderBy;
|
|
603
848
|
}
|
|
849
|
+
normalizeFields(fields, prefix = '') {
|
|
850
|
+
const ret = [];
|
|
851
|
+
for (const field of fields) {
|
|
852
|
+
if (typeof field === 'string') {
|
|
853
|
+
ret.push(prefix + field);
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (core_1.Utils.isPlainObject(field)) {
|
|
857
|
+
for (const key of Object.keys(field)) {
|
|
858
|
+
ret.push(...this.normalizeFields(field[key], key + '.'));
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return ret;
|
|
863
|
+
}
|
|
864
|
+
processField(meta, prop, field, ret, populate, joinedProps, qb) {
|
|
865
|
+
if (!prop || (prop.kind === core_1.ReferenceKind.ONE_TO_ONE && !prop.owner)) {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
if (prop.kind === core_1.ReferenceKind.EMBEDDED) {
|
|
869
|
+
if (prop.object) {
|
|
870
|
+
ret.push(prop.name);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const parts = field.split('.');
|
|
874
|
+
const top = parts.shift();
|
|
875
|
+
for (const key of Object.keys(prop.embeddedProps)) {
|
|
876
|
+
if (!top || key === top) {
|
|
877
|
+
this.processField(meta, prop.embeddedProps[key], parts.join('.'), ret, populate, joinedProps, qb);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
ret.push(prop.name);
|
|
883
|
+
}
|
|
604
884
|
buildFields(meta, populate, joinedProps, qb, fields) {
|
|
605
885
|
const lazyProps = meta.props.filter(prop => prop.lazy && !populate.some(p => p.field === prop.name || p.all));
|
|
606
886
|
const hasLazyFormulas = meta.props.some(p => p.lazy && p.formula);
|
|
607
887
|
const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
|
|
608
888
|
const hasExplicitFields = !!fields;
|
|
609
889
|
const ret = [];
|
|
890
|
+
let addFormulas = false;
|
|
610
891
|
if (joinedProps.length > 0) {
|
|
611
892
|
ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
|
|
612
893
|
}
|
|
613
894
|
else if (fields) {
|
|
614
|
-
for (const field of
|
|
615
|
-
if (
|
|
895
|
+
for (const field of this.normalizeFields(fields)) {
|
|
896
|
+
if (field === '*') {
|
|
897
|
+
ret.push('*');
|
|
616
898
|
continue;
|
|
617
899
|
}
|
|
618
|
-
|
|
900
|
+
const parts = field.split('.');
|
|
901
|
+
const rootPropName = parts.shift(); // first one is the `prop`
|
|
902
|
+
const prop = core_1.QueryHelper.findProperty(rootPropName, {
|
|
903
|
+
metadata: this.metadata,
|
|
904
|
+
platform: this.platform,
|
|
905
|
+
entityName: meta.className,
|
|
906
|
+
where: {},
|
|
907
|
+
aliasMap: qb.getAliasMap(),
|
|
908
|
+
});
|
|
909
|
+
this.processField(meta, prop, parts.join('.'), ret, populate, joinedProps, qb);
|
|
619
910
|
}
|
|
620
911
|
ret.unshift(...meta.primaryKeys.filter(pk => !fields.includes(pk)));
|
|
621
912
|
}
|
|
622
913
|
else if (lazyProps.filter(p => !p.formula).length > 0) {
|
|
623
914
|
const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
|
|
624
915
|
ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
|
|
916
|
+
addFormulas = true;
|
|
625
917
|
}
|
|
626
918
|
else if (hasLazyFormulas || requiresSQLConversion) {
|
|
627
919
|
ret.push('*');
|
|
920
|
+
addFormulas = true;
|
|
628
921
|
}
|
|
629
|
-
if (ret.length > 0 && !hasExplicitFields) {
|
|
922
|
+
if (ret.length > 0 && !hasExplicitFields && addFormulas) {
|
|
630
923
|
meta.props
|
|
631
924
|
.filter(prop => prop.formula && !lazyProps.includes(prop))
|
|
632
925
|
.forEach(prop => {
|
|
633
|
-
const alias =
|
|
634
|
-
const aliased =
|
|
635
|
-
ret.push(`${prop.formula(alias)} as ${aliased}`);
|
|
926
|
+
const alias = this.connection.getKnex().ref(qb.alias).toString();
|
|
927
|
+
const aliased = this.connection.getKnex().ref(prop.fieldNames[0]).toString();
|
|
928
|
+
ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
|
|
636
929
|
});
|
|
637
930
|
meta.props
|
|
638
|
-
.filter(prop => prop.
|
|
931
|
+
.filter(prop => prop.hasConvertToDatabaseValueSQL || prop.hasConvertToJSValueSQL)
|
|
639
932
|
.forEach(prop => ret.push(prop.name));
|
|
640
933
|
}
|
|
641
|
-
return ret.length > 0 ? ret : ['*'];
|
|
934
|
+
return ret.length > 0 ? core_1.Utils.unique(ret) : ['*'];
|
|
642
935
|
}
|
|
643
936
|
}
|
|
644
937
|
exports.AbstractSqlDriver = AbstractSqlDriver;
|