@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 cleanOrderColumnsForInnoDB({ id, attribute, db, inverseRelIds, transaction: trx });
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 = `orderTable_${now}_${randomHex}`;
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.raw(`DROP TABLE IF EXISTS ??`, [tempOrderTableName]).transacting(trx);
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 = `invOrderTable_${now}_${randomHex}`;
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.5.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.4",
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": "8716ecc920130db5341b0904cf868c6e6b581a5d"
46
+ "gitHead": "9171c48104548f5f6da21abf2a8098009f1a40e9"
47
47
  }