@strapi/database 4.5.4 → 4.6.0-alpha.1
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.
|
@@ -211,19 +211,22 @@ const createEntityManager = (db) => {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
const dataToInsert = processData(metadata, data, { withDefaults: true });
|
|
214
|
-
|
|
215
|
-
const res = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
216
|
-
|
|
217
|
-
const id = res[0].id || res[0];
|
|
214
|
+
let id;
|
|
218
215
|
|
|
219
216
|
const trx = await strapi.db.transaction();
|
|
220
217
|
try {
|
|
218
|
+
const res = await this.createQueryBuilder(uid)
|
|
219
|
+
.insert(dataToInsert)
|
|
220
|
+
.transacting(trx)
|
|
221
|
+
.execute();
|
|
222
|
+
|
|
223
|
+
id = res[0].id || res[0];
|
|
224
|
+
|
|
221
225
|
await this.attachRelations(uid, id, data, { transaction: trx });
|
|
222
226
|
|
|
223
227
|
await trx.commit();
|
|
224
228
|
} catch (e) {
|
|
225
229
|
await trx.rollback();
|
|
226
|
-
await this.createQueryBuilder(uid).where({ id }).delete().execute();
|
|
227
230
|
throw e;
|
|
228
231
|
}
|
|
229
232
|
|
|
@@ -282,11 +285,7 @@ const createEntityManager = (db) => {
|
|
|
282
285
|
throw new Error('Update requires a where parameter');
|
|
283
286
|
}
|
|
284
287
|
|
|
285
|
-
const entity = await this.createQueryBuilder(uid)
|
|
286
|
-
.select('*')
|
|
287
|
-
.where(where)
|
|
288
|
-
.first()
|
|
289
|
-
.execute({ mapResults: false });
|
|
288
|
+
const entity = await this.createQueryBuilder(uid).select('id').where(where).first().execute();
|
|
290
289
|
|
|
291
290
|
if (!entity) {
|
|
292
291
|
return null;
|
|
@@ -294,19 +293,23 @@ const createEntityManager = (db) => {
|
|
|
294
293
|
|
|
295
294
|
const { id } = entity;
|
|
296
295
|
|
|
297
|
-
const dataToUpdate = processData(metadata, data);
|
|
298
|
-
|
|
299
|
-
if (!isEmpty(dataToUpdate)) {
|
|
300
|
-
await this.createQueryBuilder(uid).where({ id }).update(dataToUpdate).execute();
|
|
301
|
-
}
|
|
302
|
-
|
|
303
296
|
const trx = await strapi.db.transaction();
|
|
304
297
|
try {
|
|
298
|
+
const dataToUpdate = processData(metadata, data);
|
|
299
|
+
|
|
300
|
+
if (!isEmpty(dataToUpdate)) {
|
|
301
|
+
await this.createQueryBuilder(uid)
|
|
302
|
+
.where({ id })
|
|
303
|
+
.update(dataToUpdate)
|
|
304
|
+
.transacting(trx)
|
|
305
|
+
.execute();
|
|
306
|
+
}
|
|
307
|
+
|
|
305
308
|
await this.updateRelations(uid, id, data, { transaction: trx });
|
|
309
|
+
|
|
306
310
|
await trx.commit();
|
|
307
311
|
} catch (e) {
|
|
308
312
|
await trx.rollback();
|
|
309
|
-
await this.createQueryBuilder(uid).where({ id }).update(entity).execute();
|
|
310
313
|
throw e;
|
|
311
314
|
}
|
|
312
315
|
|
|
@@ -369,10 +372,10 @@ const createEntityManager = (db) => {
|
|
|
369
372
|
|
|
370
373
|
const { id } = entity;
|
|
371
374
|
|
|
372
|
-
await this.createQueryBuilder(uid).where({ id }).delete().execute();
|
|
373
|
-
|
|
374
375
|
const trx = await strapi.db.transaction();
|
|
375
376
|
try {
|
|
377
|
+
await this.createQueryBuilder(uid).where({ id }).delete().transacting(trx).execute();
|
|
378
|
+
|
|
376
379
|
await this.deleteRelations(uid, id, { transaction: trx });
|
|
377
380
|
|
|
378
381
|
await trx.commit();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { map, isEmpty } = require('lodash/fp');
|
|
4
|
-
const { randomBytes } = require('crypto');
|
|
5
4
|
|
|
6
5
|
const {
|
|
7
6
|
isBidirectional,
|
|
@@ -198,42 +197,60 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction
|
|
|
198
197
|
return;
|
|
199
198
|
}
|
|
200
199
|
|
|
200
|
+
// Handle databases that don't support window function ROW_NUMBER
|
|
201
|
+
if (!strapi.db.dialect.supportsWindowFunctions()) {
|
|
202
|
+
await cleanOrderColumnsForOldDatabases({ id, attribute, db, inverseRelIds, transaction: trx });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const { joinTable } = attribute;
|
|
207
|
+
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
208
|
+
const update = [];
|
|
209
|
+
const updateBinding = [];
|
|
210
|
+
const select = ['??'];
|
|
211
|
+
const selectBinding = ['id'];
|
|
212
|
+
const where = [];
|
|
213
|
+
const whereBinding = [];
|
|
214
|
+
|
|
215
|
+
if (hasOrderColumn(attribute) && id) {
|
|
216
|
+
update.push('?? = b.src_order');
|
|
217
|
+
updateBinding.push(orderColumnName);
|
|
218
|
+
select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS src_order');
|
|
219
|
+
selectBinding.push(joinColumn.name, orderColumnName);
|
|
220
|
+
where.push('?? = ?');
|
|
221
|
+
whereBinding.push(joinColumn.name, id);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) {
|
|
225
|
+
update.push('?? = b.inv_order');
|
|
226
|
+
updateBinding.push(inverseOrderColumnName);
|
|
227
|
+
select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS inv_order');
|
|
228
|
+
selectBinding.push(inverseJoinColumn.name, inverseOrderColumnName);
|
|
229
|
+
where.push(`?? IN (${inverseRelIds.map(() => '?').join(', ')})`);
|
|
230
|
+
whereBinding.push(inverseJoinColumn.name, ...inverseRelIds);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// raw query as knex doesn't allow updating from a subquery
|
|
234
|
+
// https://github.com/knex/knex/issues/2504
|
|
201
235
|
switch (strapi.db.dialect.client) {
|
|
202
236
|
case 'mysql':
|
|
203
|
-
await
|
|
237
|
+
await db.connection
|
|
238
|
+
.raw(
|
|
239
|
+
`UPDATE
|
|
240
|
+
?? as a,
|
|
241
|
+
(
|
|
242
|
+
SELECT ${select.join(', ')}
|
|
243
|
+
FROM ??
|
|
244
|
+
WHERE ${where.join(' OR ')}
|
|
245
|
+
) AS b
|
|
246
|
+
SET ${update.join(', ')}
|
|
247
|
+
WHERE b.id = a.id`,
|
|
248
|
+
[joinTable.name, ...selectBinding, joinTable.name, ...whereBinding, ...updateBinding]
|
|
249
|
+
)
|
|
250
|
+
.transacting(trx);
|
|
204
251
|
break;
|
|
205
252
|
default: {
|
|
206
|
-
const { joinTable } = attribute;
|
|
207
|
-
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
208
|
-
const update = [];
|
|
209
|
-
const updateBinding = [];
|
|
210
|
-
const select = ['??'];
|
|
211
|
-
const selectBinding = ['id'];
|
|
212
|
-
const where = [];
|
|
213
|
-
const whereBinding = [];
|
|
214
|
-
|
|
215
|
-
if (hasOrderColumn(attribute) && id) {
|
|
216
|
-
update.push('?? = b.src_order');
|
|
217
|
-
updateBinding.push(orderColumnName);
|
|
218
|
-
select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS src_order');
|
|
219
|
-
selectBinding.push(joinColumn.name, orderColumnName);
|
|
220
|
-
where.push('?? = ?');
|
|
221
|
-
whereBinding.push(joinColumn.name, id);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) {
|
|
225
|
-
update.push('?? = b.inv_order');
|
|
226
|
-
updateBinding.push(inverseOrderColumnName);
|
|
227
|
-
select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS inv_order');
|
|
228
|
-
selectBinding.push(inverseJoinColumn.name, inverseOrderColumnName);
|
|
229
|
-
where.push(`?? IN (${inverseRelIds.map(() => '?').join(', ')})`);
|
|
230
|
-
whereBinding.push(inverseJoinColumn.name, ...inverseRelIds);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
253
|
const joinTableName = addSchema(joinTable.name);
|
|
234
|
-
|
|
235
|
-
// raw query as knex doesn't allow updating from a subquery
|
|
236
|
-
// https://github.com/knex/knex/issues/2504
|
|
237
254
|
await db.connection
|
|
238
255
|
.raw(
|
|
239
256
|
`UPDATE ?? as a
|
|
@@ -247,29 +264,24 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction
|
|
|
247
264
|
[joinTableName, ...updateBinding, ...selectBinding, joinTableName, ...whereBinding]
|
|
248
265
|
)
|
|
249
266
|
.transacting(trx);
|
|
250
|
-
|
|
251
|
-
/*
|
|
252
|
-
`UPDATE :joinTable: as a
|
|
253
|
-
SET :orderColumn: = b.src_order, :inverseOrderColumn: = b.inv_order
|
|
254
|
-
FROM (
|
|
255
|
-
SELECT
|
|
256
|
-
id,
|
|
257
|
-
ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS src_order,
|
|
258
|
-
ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order
|
|
259
|
-
FROM :joinTable:
|
|
260
|
-
WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds)
|
|
261
|
-
) AS b
|
|
262
|
-
WHERE b.id = a.id`,
|
|
263
|
-
*/
|
|
264
267
|
}
|
|
268
|
+
/*
|
|
269
|
+
`UPDATE :joinTable: as a
|
|
270
|
+
SET :orderColumn: = b.src_order, :inverseOrderColumn: = b.inv_order
|
|
271
|
+
FROM (
|
|
272
|
+
SELECT
|
|
273
|
+
id,
|
|
274
|
+
ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS src_order,
|
|
275
|
+
ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order
|
|
276
|
+
FROM :joinTable:
|
|
277
|
+
WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds)
|
|
278
|
+
) AS b
|
|
279
|
+
WHERE b.id = a.id`,
|
|
280
|
+
*/
|
|
265
281
|
}
|
|
266
282
|
};
|
|
267
283
|
|
|
268
|
-
|
|
269
|
-
* Ensure that orders are following a 1, 2, 3 sequence, without gap.
|
|
270
|
-
* The use of a temporary table instead of a window function makes the query compatible with MySQL 5 and prevents some deadlocks to happen in innoDB databases
|
|
271
|
-
*/
|
|
272
|
-
const cleanOrderColumnsForInnoDB = async ({
|
|
284
|
+
const cleanOrderColumnsForOldDatabases = async ({
|
|
273
285
|
id,
|
|
274
286
|
attribute,
|
|
275
287
|
db,
|
|
@@ -280,15 +292,14 @@ const cleanOrderColumnsForInnoDB = async ({
|
|
|
280
292
|
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
281
293
|
|
|
282
294
|
const now = new Date().valueOf();
|
|
283
|
-
const randomHex = randomBytes(16).toString('hex');
|
|
284
295
|
|
|
285
296
|
if (hasOrderColumn(attribute) && id) {
|
|
286
|
-
const tempOrderTableName = `
|
|
297
|
+
const tempOrderTableName = `tempOrderTableName_${now}`;
|
|
287
298
|
try {
|
|
288
299
|
await db.connection
|
|
289
300
|
.raw(
|
|
290
301
|
`
|
|
291
|
-
CREATE TABLE :tempOrderTableName:
|
|
302
|
+
CREATE TEMPORARY TABLE :tempOrderTableName:
|
|
292
303
|
SELECT
|
|
293
304
|
id,
|
|
294
305
|
(
|
|
@@ -306,9 +317,6 @@ const cleanOrderColumnsForInnoDB = async ({
|
|
|
306
317
|
}
|
|
307
318
|
)
|
|
308
319
|
.transacting(trx);
|
|
309
|
-
|
|
310
|
-
// raw query as knex doesn't allow updating from a subquery
|
|
311
|
-
// https://github.com/knex/knex/issues/2504
|
|
312
320
|
await db.connection
|
|
313
321
|
.raw(
|
|
314
322
|
`UPDATE ?? as a, (SELECT * FROM ??) AS b
|
|
@@ -318,12 +326,14 @@ const cleanOrderColumnsForInnoDB = async ({
|
|
|
318
326
|
)
|
|
319
327
|
.transacting(trx);
|
|
320
328
|
} finally {
|
|
321
|
-
await db.connection
|
|
329
|
+
await db.connection
|
|
330
|
+
.raw(`DROP TEMPORARY TABLE IF EXISTS ??`, [tempOrderTableName])
|
|
331
|
+
.transacting(trx);
|
|
322
332
|
}
|
|
323
333
|
}
|
|
324
334
|
|
|
325
335
|
if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) {
|
|
326
|
-
const tempInvOrderTableName = `
|
|
336
|
+
const tempInvOrderTableName = `tempInvOrderTableName_${now}`;
|
|
327
337
|
try {
|
|
328
338
|
await db.connection
|
|
329
339
|
.raw(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/database",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0-alpha.1",
|
|
4
4
|
"description": "Strapi's database layer",
|
|
5
5
|
"homepage": "https://strapi.io",
|
|
6
6
|
"bugs": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"date-fns": "2.29.2",
|
|
35
|
-
"debug": "4.3.
|
|
35
|
+
"debug": "4.3.1",
|
|
36
36
|
"fs-extra": "10.0.0",
|
|
37
37
|
"knex": "1.0.7",
|
|
38
38
|
"lodash": "4.17.21",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"node": ">=14.19.1 <=18.x.x",
|
|
44
44
|
"npm": ">=6.0.0"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "9171c48104548f5f6da21abf2a8098009f1a40e9"
|
|
47
47
|
}
|