| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  | const { map, isEmpty } = require('lodash/fp'); | 
					
						
							| 
									
										
										
										
											2022-12-06 14:29:16 +01:00
										 |  |  | const { randomBytes } = require('crypto'); | 
					
						
							| 
									
										
										
										
											2022-11-21 14:28:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | const { | 
					
						
							|  |  |  |   isBidirectional, | 
					
						
							|  |  |  |   isOneToAny, | 
					
						
							|  |  |  |   isManyToAny, | 
					
						
							|  |  |  |   isAnyToOne, | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |   hasOrderColumn, | 
					
						
							|  |  |  |   hasInverseOrderColumn, | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | } = require('../metadata/relations'); | 
					
						
							|  |  |  | const { createQueryBuilder } = require('../query'); | 
					
						
							| 
									
										
										
										
											2022-11-19 00:01:10 +01:00
										 |  |  | const { addSchema } = require('../utils/knex'); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * If some relations currently exist for this oneToX relation, on the one side, this function removes them and update the inverse order if needed. | 
					
						
							|  |  |  |  * @param {Object} params | 
					
						
							|  |  |  |  * @param {string} params.id - entity id on which the relations for entities relIdsToadd are created | 
					
						
							|  |  |  |  * @param {string} params.attribute - attribute of the relation | 
					
						
							|  |  |  |  * @param {string} params.inverseRelIds - entity ids of the inverse side for which the current relations will be deleted | 
					
						
							|  |  |  |  * @param {string} params.db - database instance | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  | const deletePreviousOneToAnyRelations = async ({ | 
					
						
							|  |  |  |   id, | 
					
						
							|  |  |  |   attribute, | 
					
						
							|  |  |  |   relIdsToadd, | 
					
						
							|  |  |  |   db, | 
					
						
							|  |  |  |   transaction: trx, | 
					
						
							|  |  |  | }) => { | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |   if (!(isBidirectional(attribute) && isOneToAny(attribute))) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'deletePreviousOneToAnyRelations can only be called for bidirectional oneToAny relations' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |   const { joinTable } = attribute; | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   const { joinColumn, inverseJoinColumn } = joinTable; | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |   await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |     .delete() | 
					
						
							|  |  |  |     .where({ | 
					
						
							|  |  |  |       [inverseJoinColumn.name]: relIdsToadd, | 
					
						
							|  |  |  |       [joinColumn.name]: { $ne: id }, | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |     .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |     .execute(); | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |   await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToadd, transaction: trx }); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * If a relation currently exists for this xToOne relations, this function removes it and update the inverse order if needed. | 
					
						
							|  |  |  |  * @param {Object} params | 
					
						
							|  |  |  |  * @param {string} params.id - entity id on which the relation for entity relIdToadd is created | 
					
						
							|  |  |  |  * @param {string} params.attribute - attribute of the relation | 
					
						
							|  |  |  |  * @param {string} params.relIdToadd - entity id of the new relation | 
					
						
							|  |  |  |  * @param {string} params.db - database instance | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  | const deletePreviousAnyToOneRelations = async ({ | 
					
						
							|  |  |  |   id, | 
					
						
							|  |  |  |   attribute, | 
					
						
							|  |  |  |   relIdToadd, | 
					
						
							|  |  |  |   db, | 
					
						
							|  |  |  |   transaction: trx, | 
					
						
							|  |  |  | }) => { | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |   const { joinTable } = attribute; | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   const { joinColumn, inverseJoinColumn } = joinTable; | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 18:16:57 +02:00
										 |  |  |   if (!isAnyToOne(attribute)) { | 
					
						
							|  |  |  |     throw new Error('deletePreviousAnyToOneRelations can only be called for anyToOne relations'); | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   // handling manyToOne
 | 
					
						
							|  |  |  |   if (isManyToAny(attribute)) { | 
					
						
							|  |  |  |     // if the database integrity was not broken relsToDelete is supposed to be of length 1
 | 
					
						
							|  |  |  |     const relsToDelete = await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |       .select(inverseJoinColumn.name) | 
					
						
							|  |  |  |       .where({ | 
					
						
							|  |  |  |         [joinColumn.name]: id, | 
					
						
							|  |  |  |         [inverseJoinColumn.name]: { $ne: relIdToadd }, | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |       .execute(); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |     const relIdsToDelete = map(inverseJoinColumn.name, relsToDelete); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |     await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |       .delete() | 
					
						
							|  |  |  |       .where({ | 
					
						
							|  |  |  |         [joinColumn.name]: id, | 
					
						
							|  |  |  |         [inverseJoinColumn.name]: { $in: relIdsToDelete }, | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |       .execute(); | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |     await cleanOrderColumns({ attribute, db, inverseRelIds: relIdsToDelete, transaction: trx }); | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |     // handling oneToOne
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |       .delete() | 
					
						
							|  |  |  |       .where({ | 
					
						
							|  |  |  |         [joinColumn.name]: id, | 
					
						
							|  |  |  |         [inverseJoinColumn.name]: { $ne: relIdToadd }, | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-27 16:07:00 +02:00
										 |  |  |       .execute(); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Delete all or some relations of entity field | 
					
						
							|  |  |  |  * @param {Object} params | 
					
						
							|  |  |  |  * @param {string} params.id - entity id for which the relations will be deleted | 
					
						
							|  |  |  |  * @param {string} params.attribute - attribute of the relation | 
					
						
							|  |  |  |  * @param {string} params.db - database instance | 
					
						
							|  |  |  |  * @param {string} params.relIdsToDelete - ids of entities to remove from the relations. Also accepts 'all' | 
					
						
							|  |  |  |  * @param {string} params.relIdsToNotDelete - ids of entities to not remove from the relation when relIdsToDelete equals 'all' | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const deleteRelations = async ({ | 
					
						
							|  |  |  |   id, | 
					
						
							|  |  |  |   attribute, | 
					
						
							|  |  |  |   db, | 
					
						
							|  |  |  |   relIdsToNotDelete = [], | 
					
						
							|  |  |  |   relIdsToDelete = [], | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |   transaction: trx, | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  | }) => { | 
					
						
							|  |  |  |   const { joinTable } = attribute; | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   const { joinColumn, inverseJoinColumn } = joinTable; | 
					
						
							| 
									
										
										
										
											2022-09-20 15:53:17 +02:00
										 |  |  |   const all = relIdsToDelete === 'all'; | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |   if (hasOrderColumn(attribute) || hasInverseOrderColumn(attribute)) { | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |     let lastId = 0; | 
					
						
							|  |  |  |     let done = false; | 
					
						
							|  |  |  |     const batchSize = 100; | 
					
						
							|  |  |  |     while (!done) { | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |       const batchToDelete = await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |         .select(inverseJoinColumn.name) | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |         .where({ | 
					
						
							|  |  |  |           [joinColumn.name]: id, | 
					
						
							|  |  |  |           id: { $gt: lastId }, | 
					
						
							| 
									
										
										
										
											2022-09-20 15:53:17 +02:00
										 |  |  |           [inverseJoinColumn.name]: { $notIn: relIdsToNotDelete }, | 
					
						
							|  |  |  |           ...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }), | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  |         .where(joinTable.on || {}) | 
					
						
							|  |  |  |         .orderBy('id') | 
					
						
							|  |  |  |         .limit(batchSize) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |         .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |         .execute(); | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |       done = batchToDelete.length < batchSize; | 
					
						
							|  |  |  |       lastId = batchToDelete[batchToDelete.length - 1]?.id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const batchIds = map(inverseJoinColumn.name, batchToDelete); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |         .delete() | 
					
						
							|  |  |  |         .where({ | 
					
						
							|  |  |  |           [joinColumn.name]: id, | 
					
						
							|  |  |  |           [inverseJoinColumn.name]: { $in: batchIds }, | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |         .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |         .execute(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       await cleanOrderColumns({ attribute, db, id, inverseRelIds: batchIds, transaction: trx }); | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     await createQueryBuilder(joinTable.name, db) | 
					
						
							|  |  |  |       .delete() | 
					
						
							|  |  |  |       .where({ | 
					
						
							|  |  |  |         [joinColumn.name]: id, | 
					
						
							|  |  |  |         [inverseJoinColumn.name]: { $notIn: relIdsToNotDelete }, | 
					
						
							|  |  |  |         ...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }), | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .where(joinTable.on || {}) | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       .transacting(trx) | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |       .execute(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Clean the order columns by ensuring the order value are continuous (ex: 1, 2, 3 and not 1, 5, 10) | 
					
						
							|  |  |  |  * @param {Object} params | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |  * @param {string} params.id - entity id for which the clean will be done | 
					
						
							|  |  |  |  * @param {string} params.attribute - attribute of the relation | 
					
						
							|  |  |  |  * @param {string} params.db - database instance | 
					
						
							|  |  |  |  * @param {string} params.inverseRelIds - entity ids of the inverse side for which the clean will be done | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  | const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction: trx }) => { | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   if ( | 
					
						
							| 
									
										
										
										
											2022-09-27 11:56:46 +02:00
										 |  |  |     !(hasOrderColumn(attribute) && id) && | 
					
						
							|  |  |  |     !(hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   ) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-25 11:31:23 +01:00
										 |  |  |   // Handle databases that don't support window function ROW_NUMBER (here it's MySQL 5)
 | 
					
						
							|  |  |  |   if (!strapi.db.dialect.supportsWindowFunctions()) { | 
					
						
							|  |  |  |     await cleanOrderColumnsForOldDatabases({ id, attribute, db, inverseRelIds, transaction: trx }); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { joinTable } = attribute; | 
					
						
							|  |  |  |   const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable; | 
					
						
							|  |  |  |   const update = []; | 
					
						
							|  |  |  |   const updateBinding = []; | 
					
						
							|  |  |  |   const select = ['??']; | 
					
						
							|  |  |  |   const selectBinding = ['id']; | 
					
						
							|  |  |  |   const where = []; | 
					
						
							|  |  |  |   const whereBinding = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (hasOrderColumn(attribute) && id) { | 
					
						
							|  |  |  |     update.push('?? = b.src_order'); | 
					
						
							|  |  |  |     updateBinding.push(orderColumnName); | 
					
						
							|  |  |  |     select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS src_order'); | 
					
						
							|  |  |  |     selectBinding.push(joinColumn.name, orderColumnName); | 
					
						
							|  |  |  |     where.push('?? = ?'); | 
					
						
							|  |  |  |     whereBinding.push(joinColumn.name, id); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) { | 
					
						
							|  |  |  |     update.push('?? = b.inv_order'); | 
					
						
							|  |  |  |     updateBinding.push(inverseOrderColumnName); | 
					
						
							|  |  |  |     select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS inv_order'); | 
					
						
							|  |  |  |     selectBinding.push(inverseJoinColumn.name, inverseOrderColumnName); | 
					
						
							|  |  |  |     where.push(`?? IN (${inverseRelIds.map(() => '?').join(', ')})`); | 
					
						
							|  |  |  |     whereBinding.push(inverseJoinColumn.name, ...inverseRelIds); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 15:11:11 +02:00
										 |  |  |   switch (strapi.db.dialect.client) { | 
					
						
							|  |  |  |     case 'mysql': | 
					
						
							| 
									
										
										
										
											2023-01-25 11:31:23 +01:00
										 |  |  |       // Here it's MariaDB and MySQL 8
 | 
					
						
							|  |  |  |       await db | 
					
						
							|  |  |  |         .getConnection() | 
					
						
							|  |  |  |         .raw( | 
					
						
							|  |  |  |           `UPDATE
 | 
					
						
							|  |  |  |             ?? as a, | 
					
						
							|  |  |  |             ( | 
					
						
							|  |  |  |               SELECT ${select.join(', ')} | 
					
						
							|  |  |  |               FROM ?? | 
					
						
							|  |  |  |               WHERE ${where.join(' OR ')} | 
					
						
							|  |  |  |             ) AS b | 
					
						
							|  |  |  |           SET ${update.join(', ')} | 
					
						
							|  |  |  |           WHERE b.id = a.id`,
 | 
					
						
							|  |  |  |           [joinTable.name, ...selectBinding, joinTable.name, ...whereBinding, ...updateBinding] | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .transacting(trx); | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2023-01-25 14:12:31 +01:00
										 |  |  |     /* | 
					
						
							|  |  |  |       UPDATE | 
					
						
							|  |  |  |         :joinTable: as a, | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |           SELECT | 
					
						
							|  |  |  |             id, | 
					
						
							|  |  |  |             ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS src_order, | 
					
						
							|  |  |  |             ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order | 
					
						
							|  |  |  |           FROM :joinTable: | 
					
						
							|  |  |  |           WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds) | 
					
						
							|  |  |  |         ) AS b | 
					
						
							|  |  |  |       SET :orderColumn: = b.src_order, :inverseOrderColumn: = b.inv_order | 
					
						
							|  |  |  |       WHERE b.id = a.id; | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2022-11-17 10:08:49 +01:00
										 |  |  |     default: { | 
					
						
							|  |  |  |       const joinTableName = addSchema(joinTable.name); | 
					
						
							| 
									
										
										
										
											2022-12-09 15:57:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // raw query as knex doesn't allow updating from a subquery
 | 
					
						
							|  |  |  |       // https://github.com/knex/knex/issues/2504
 | 
					
						
							| 
									
										
										
										
											2022-11-16 09:34:08 +00:00
										 |  |  |       await db.connection | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |         .raw( | 
					
						
							|  |  |  |           `UPDATE ?? as a
 | 
					
						
							| 
									
										
										
										
											2022-11-11 12:16:55 +00:00
										 |  |  |               SET ${update.join(', ')} | 
					
						
							|  |  |  |               FROM ( | 
					
						
							|  |  |  |                 SELECT ${select.join(', ')} | 
					
						
							|  |  |  |                 FROM ?? | 
					
						
							|  |  |  |                 WHERE ${where.join(' OR ')} | 
					
						
							|  |  |  |               ) AS b | 
					
						
							|  |  |  |               WHERE b.id = a.id`,
 | 
					
						
							| 
									
										
										
										
											2022-11-17 10:08:49 +01:00
										 |  |  |           [joinTableName, ...updateBinding, ...selectBinding, joinTableName, ...whereBinding] | 
					
						
							| 
									
										
										
										
											2022-10-06 18:03:10 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         .transacting(trx); | 
					
						
							| 
									
										
										
										
											2022-12-09 15:57:23 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       /* | 
					
						
							| 
									
										
										
										
											2023-01-25 14:12:31 +01:00
										 |  |  |         UPDATE :joinTable: as a | 
					
						
							|  |  |  |         SET :orderColumn: = b.src_order, :inverseOrderColumn: = b.inv_order | 
					
						
							|  |  |  |         FROM ( | 
					
						
							|  |  |  |           SELECT | 
					
						
							|  |  |  |             id, | 
					
						
							|  |  |  |             ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS src_order, | 
					
						
							|  |  |  |             ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order | 
					
						
							|  |  |  |           FROM :joinTable: | 
					
						
							|  |  |  |           WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds) | 
					
						
							|  |  |  |         ) AS b | 
					
						
							|  |  |  |         WHERE b.id = a.id; | 
					
						
							| 
									
										
										
										
											2022-12-09 15:57:23 +01:00
										 |  |  |       */ | 
					
						
							| 
									
										
										
										
											2022-11-17 10:08:49 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-27 15:11:11 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 15:57:23 +01:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Ensure that orders are following a 1, 2, 3 sequence, without gap. | 
					
						
							| 
									
										
										
										
											2023-01-25 11:31:23 +01:00
										 |  |  |  * The use of a session variable instead of a window function makes the query compatible with MySQL 5 | 
					
						
							| 
									
										
										
										
											2022-12-09 15:57:23 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-25 11:31:23 +01:00
										 |  |  | const cleanOrderColumnsForOldDatabases = async ({ | 
					
						
							| 
									
										
										
										
											2022-11-15 16:43:04 +01:00
										 |  |  |   id, | 
					
						
							|  |  |  |   attribute, | 
					
						
							|  |  |  |   db, | 
					
						
							|  |  |  |   inverseRelIds, | 
					
						
							|  |  |  |   transaction: trx, | 
					
						
							|  |  |  | }) => { | 
					
						
							|  |  |  |   const { joinTable } = attribute; | 
					
						
							|  |  |  |   const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |   const randomSuffix = `${new Date().valueOf()}_${randomBytes(16).toString('hex')}`; | 
					
						
							| 
									
										
										
										
											2022-11-15 16:43:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (hasOrderColumn(attribute) && id) { | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |     // raw query as knex doesn't allow updating from a subquery
 | 
					
						
							|  |  |  |     // https://github.com/knex/knex/issues/2504
 | 
					
						
							|  |  |  |     const orderVar = `order_${randomSuffix}`; | 
					
						
							|  |  |  |     await db.connection.raw(`SET @${orderVar} = 0;`).transacting(trx); | 
					
						
							|  |  |  |     await db.connection | 
					
						
							|  |  |  |       .raw( | 
					
						
							|  |  |  |         `UPDATE :joinTableName: as a, (
 | 
					
						
							|  |  |  |           SELECT id, (@${orderVar}:=@${orderVar} + 1) AS src_order | 
					
						
							|  |  |  |           FROM :joinTableName: | 
					
						
							|  |  |  | 	        WHERE :joinColumnName: = :id | 
					
						
							|  |  |  | 	        ORDER BY :orderColumnName: | 
					
						
							|  |  |  |         ) AS b | 
					
						
							|  |  |  |         SET :orderColumnName: = b.src_order | 
					
						
							| 
									
										
										
										
											2023-01-24 17:44:31 +01:00
										 |  |  |         WHERE a.id = b.id | 
					
						
							|  |  |  |         AND a.:joinColumnName: = :id`,
 | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |         { | 
					
						
							|  |  |  |           joinTableName: joinTable.name, | 
					
						
							|  |  |  |           orderColumnName, | 
					
						
							|  |  |  |           joinColumnName: joinColumn.name, | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .transacting(trx); | 
					
						
							| 
									
										
										
										
											2022-11-15 16:43:04 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-11-15 18:57:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-15 16:43:04 +01:00
										 |  |  |   if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) { | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |     const orderVar = `order_${randomSuffix}`; | 
					
						
							|  |  |  |     const columnVar = `col_${randomSuffix}`; | 
					
						
							|  |  |  |     await db.connection.raw(`SET @${orderVar} = 0;`).transacting(trx); | 
					
						
							|  |  |  |     await db.connection | 
					
						
							|  |  |  |       .raw( | 
					
						
							|  |  |  |         `UPDATE ?? as a, (
 | 
					
						
							|  |  |  |           SELECT | 
					
						
							|  |  |  |           	id, | 
					
						
							| 
									
										
										
										
											2023-01-24 17:44:31 +01:00
										 |  |  |             @${orderVar}:=CASE WHEN @${columnVar} = ?? THEN @${orderVar} + 1 ELSE 1 END AS inv_order, | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |         	  @${columnVar}:=?? ?? | 
					
						
							| 
									
										
										
										
											2023-01-24 18:03:36 +01:00
										 |  |  |         	FROM ?? a | 
					
						
							|  |  |  |         	WHERE ?? IN(${inverseRelIds.map(() => '?').join(', ')}) | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |         	ORDER BY ??, ?? | 
					
						
							|  |  |  |         ) AS b | 
					
						
							|  |  |  |         SET ?? = b.inv_order | 
					
						
							| 
									
										
										
										
											2023-01-24 17:44:31 +01:00
										 |  |  |         WHERE a.id = b.id | 
					
						
							| 
									
										
										
										
											2023-01-24 18:03:36 +01:00
										 |  |  |         AND a.?? IN(${inverseRelIds.map(() => '?').join(', ')})`,
 | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |         [ | 
					
						
							|  |  |  |           joinTable.name, | 
					
						
							|  |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           joinTable.name, | 
					
						
							|  |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           ...inverseRelIds, | 
					
						
							|  |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           joinColumn.name, | 
					
						
							|  |  |  |           inverseOrderColumnName, | 
					
						
							| 
									
										
										
										
											2023-01-24 17:44:31 +01:00
										 |  |  |           inverseJoinColumn.name, | 
					
						
							|  |  |  |           ...inverseRelIds, | 
					
						
							| 
									
										
										
										
											2023-01-24 17:29:15 +01:00
										 |  |  |         ] | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |       .transacting(trx); | 
					
						
							| 
									
										
										
										
											2022-11-15 16:43:04 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | module.exports = { | 
					
						
							|  |  |  |   deletePreviousOneToAnyRelations, | 
					
						
							|  |  |  |   deletePreviousAnyToOneRelations, | 
					
						
							| 
									
										
										
										
											2022-09-20 11:18:32 +02:00
										 |  |  |   deleteRelations, | 
					
						
							| 
									
										
										
										
											2022-09-26 16:22:22 +02:00
										 |  |  |   cleanOrderColumns, | 
					
						
							| 
									
										
										
										
											2022-09-16 18:38:46 +02:00
										 |  |  | }; |