| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | const _ = require('lodash'); | 
					
						
							| 
									
										
										
										
											2019-07-05 17:17:39 +02:00
										 |  |  | const { singular } = require('pluralize'); | 
					
						
							| 
									
										
										
										
											2020-08-27 18:11:16 +02:00
										 |  |  | const { contentTypes: contentTypesUtils } = require('strapi-utils'); | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-16 17:43:41 +01:00
										 |  |  | const { | 
					
						
							|  |  |  |   getDefinitionFromStore, | 
					
						
							|  |  |  |   storeDefinition, | 
					
						
							|  |  |  |   getColumnsWhereDefinitionChanged, | 
					
						
							|  |  |  | } = require('./utils/store-definition'); | 
					
						
							| 
									
										
										
										
											2020-10-13 14:30:17 +02:00
										 |  |  | const { getManyRelations } = require('./utils/associations'); | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  | const migrateSchemas = async ({ ORM, loadedModel, definition, connection, model }, context) => { | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   // Add created_at and updated_at field if timestamp option is true
 | 
					
						
							| 
									
										
										
										
											2020-09-23 17:49:28 +02:00
										 |  |  |   if (loadedModel.hasTimestamps) { | 
					
						
							|  |  |  |     definition.attributes[loadedModel.hasTimestamps[0]] = { type: 'currentTimestamp' }; | 
					
						
							|  |  |  |     definition.attributes[loadedModel.hasTimestamps[1]] = { type: 'currentTimestamp' }; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Equilize tables
 | 
					
						
							|  |  |  |   if (connection.options && connection.options.autoMigration !== false) { | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |     await createOrUpdateTable( | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         table: loadedModel.tableName, | 
					
						
							|  |  |  |         attributes: definition.attributes, | 
					
						
							|  |  |  |         definition, | 
					
						
							|  |  |  |         ORM, | 
					
						
							|  |  |  |         model, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       context | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   // Equilize polymorphic relations
 | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   const morphRelations = definition.associations.filter(association => { | 
					
						
							|  |  |  |     return association.nature.toLowerCase().includes('morphto'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (const morphRelation of morphRelations) { | 
					
						
							|  |  |  |     const attributes = { | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |       [`${loadedModel.tableName}_id`]: { type: definition.primaryKeyType }, | 
					
						
							|  |  |  |       [`${morphRelation.alias}_id`]: { type: definition.primaryKeyType }, | 
					
						
							|  |  |  |       [`${morphRelation.alias}_type`]: { type: 'text' }, | 
					
						
							|  |  |  |       [definition.attributes[morphRelation.alias].filter]: { type: 'text' }, | 
					
						
							|  |  |  |       order: { type: 'integer' }, | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (connection.options && connection.options.autoMigration !== false) { | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |       await createOrUpdateTable( | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           table: `${loadedModel.tableName}_morph`, | 
					
						
							|  |  |  |           attributes, | 
					
						
							|  |  |  |           definition, | 
					
						
							|  |  |  |           ORM, | 
					
						
							|  |  |  |           model, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         context | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 17:41:16 +01:00
										 |  |  |   // Equilize many to many relations
 | 
					
						
							| 
									
										
										
										
											2020-10-13 14:30:17 +02:00
										 |  |  |   const manyRelations = getManyRelations(definition); | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (const manyRelation of manyRelations) { | 
					
						
							| 
									
										
										
										
											2019-07-05 17:17:39 +02:00
										 |  |  |     const { plugin, collection, via, dominant, alias } = manyRelation; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dominant) { | 
					
						
							| 
									
										
										
										
											2020-05-12 16:38:06 +02:00
										 |  |  |       const targetCollection = strapi.db.getModel(collection, plugin); | 
					
						
							| 
									
										
										
										
											2019-07-05 17:17:39 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       const targetAttr = via | 
					
						
							|  |  |  |         ? targetCollection.attributes[via] | 
					
						
							|  |  |  |         : { | 
					
						
							|  |  |  |             attribute: singular(definition.collectionName), | 
					
						
							|  |  |  |             column: definition.primaryKey, | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const defAttr = definition.attributes[alias]; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-23 23:34:28 +01:00
										 |  |  |       const targetCol = `${targetAttr.attribute}_${targetAttr.column}`; | 
					
						
							|  |  |  |       let rootCol = `${defAttr.attribute}_${defAttr.column}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // manyWay with same CT
 | 
					
						
							|  |  |  |       if (rootCol === targetCol) { | 
					
						
							|  |  |  |         rootCol = `related_${rootCol}`; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |       const attributes = { | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |         [targetCol]: { type: targetCollection.primaryKeyType }, | 
					
						
							|  |  |  |         [rootCol]: { type: definition.primaryKeyType }, | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 17:17:39 +02:00
										 |  |  |       const table = manyRelation.tableCollectionName; | 
					
						
							| 
									
										
										
										
											2019-11-06 10:46:52 -03:00
										 |  |  |       if (connection.options && connection.options.autoMigration !== false) { | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |         await createOrUpdateTable({ table, attributes, definition, ORM, model }, context); | 
					
						
							| 
									
										
										
										
											2019-11-06 10:46:52 -03:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-18 18:42:28 +01:00
										 |  |  |   // Remove from attributes (auto handled by bookshelf and not displayed on ctb)
 | 
					
						
							| 
									
										
										
										
											2020-09-23 17:49:28 +02:00
										 |  |  |   if (loadedModel.hasTimestamps) { | 
					
						
							|  |  |  |     delete definition.attributes[loadedModel.hasTimestamps[0]]; | 
					
						
							|  |  |  |     delete definition.attributes[loadedModel.hasTimestamps[1]]; | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  | const getColumnInfo = async (columnName, tableName, ORM) => { | 
					
						
							|  |  |  |   const exists = await ORM.knex.schema.hasColumn(tableName, columnName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     columnName, | 
					
						
							|  |  |  |     exists, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  | const isColumn = ({ definition, attribute, name }) => { | 
					
						
							|  |  |  |   if (!_.has(attribute, 'type')) { | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |     const relation = definition.associations.find(association => { | 
					
						
							|  |  |  |       return association.alias === name; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  |     if (!relation) return false; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  |     if (['oneToOne', 'manyToOne', 'oneWay'].includes(relation.nature)) { | 
					
						
							|  |  |  |       return true; | 
					
						
							| 
									
										
										
										
											2019-12-04 15:29:17 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-12-04 15:29:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  |   if (['component', 'dynamiczone'].includes(attribute.type)) { | 
					
						
							|  |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-12-05 11:37:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | const uniqueColName = (table, key) => `${table}_${key}_unique`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const buildColType = ({ name, attribute, table, tableExists = false, definition, ORM }) => { | 
					
						
							|  |  |  |   if (!attribute.type) { | 
					
						
							|  |  |  |     const relation = definition.associations.find(association => association.alias === name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (['oneToOne', 'manyToOne', 'oneWay'].includes(relation.nature)) { | 
					
						
							|  |  |  |       return buildColType({ | 
					
						
							|  |  |  |         name, | 
					
						
							|  |  |  |         attribute: { type: definition.primaryKeyType }, | 
					
						
							|  |  |  |         table, | 
					
						
							|  |  |  |         tableExists, | 
					
						
							|  |  |  |         definition, | 
					
						
							|  |  |  |         ORM, | 
					
						
							| 
									
										
										
										
											2020-01-17 10:42:32 +01:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return null; | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   // allow custom data type for a column
 | 
					
						
							|  |  |  |   if (_.has(attribute, 'columnType')) { | 
					
						
							|  |  |  |     return table.specificType(name, attribute.columnType); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (attribute.type) { | 
					
						
							|  |  |  |     case 'uuid': | 
					
						
							|  |  |  |       return table.uuid(name); | 
					
						
							|  |  |  |     case 'uid': { | 
					
						
							|  |  |  |       table.unique(name); | 
					
						
							|  |  |  |       return table.string(name); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'richtext': | 
					
						
							|  |  |  |     case 'text': | 
					
						
							|  |  |  |       return table.text(name, 'longtext'); | 
					
						
							|  |  |  |     case 'json': | 
					
						
							|  |  |  |       return definition.client === 'pg' ? table.jsonb(name) : table.text(name, 'longtext'); | 
					
						
							|  |  |  |     case 'enumeration': | 
					
						
							|  |  |  |     case 'string': | 
					
						
							|  |  |  |     case 'password': | 
					
						
							|  |  |  |     case 'email': | 
					
						
							|  |  |  |       return table.string(name); | 
					
						
							|  |  |  |     case 'integer': | 
					
						
							|  |  |  |       return table.integer(name); | 
					
						
							|  |  |  |     case 'biginteger': | 
					
						
							|  |  |  |       return table.bigInteger(name); | 
					
						
							|  |  |  |     case 'float': | 
					
						
							|  |  |  |       return table.double(name); | 
					
						
							|  |  |  |     case 'decimal': | 
					
						
							|  |  |  |       return table.decimal(name, 10, 2); | 
					
						
							|  |  |  |     case 'date': | 
					
						
							|  |  |  |       return table.date(name); | 
					
						
							|  |  |  |     case 'time': | 
					
						
							|  |  |  |       return table.time(name, 3); | 
					
						
							|  |  |  |     case 'datetime': | 
					
						
							|  |  |  |       return table.datetime(name); | 
					
						
							|  |  |  |     case 'timestamp': | 
					
						
							|  |  |  |       return table.timestamp(name); | 
					
						
							|  |  |  |     case 'currentTimestamp': { | 
					
						
							|  |  |  |       const col = table.timestamp(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (definition.client !== 'sqlite3' && tableExists) { | 
					
						
							|  |  |  |         return col; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return col.defaultTo(ORM.knex.fn.now()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'boolean': | 
					
						
							|  |  |  |       return table.boolean(name); | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-19 17:24:56 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2019-07-16 15:31:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | // Equilize database tables
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  | const createOrUpdateTable = async ({ table, attributes, definition, ORM, model }, context) => { | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   const tableExists = await ORM.knex.schema.hasTable(table); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const createIdType = table => { | 
					
						
							|  |  |  |     if (definition.primaryKeyType === 'uuid' && definition.client === 'pg') { | 
					
						
							|  |  |  |       return table | 
					
						
							|  |  |  |         .specificType('id', 'uuid DEFAULT uuid_generate_v4()') | 
					
						
							|  |  |  |         .notNullable() | 
					
						
							|  |  |  |         .primary(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return table.increments('id'); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const createColumns = (tbl, columns, opts = {}) => { | 
					
						
							|  |  |  |     const { tableExists, alter = false } = opts; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Object.keys(columns).forEach(key => { | 
					
						
							|  |  |  |       const attribute = columns[key]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const col = buildColType({ | 
					
						
							|  |  |  |         name: key, | 
					
						
							|  |  |  |         attribute, | 
					
						
							|  |  |  |         table: tbl, | 
					
						
							|  |  |  |         tableExists, | 
					
						
							|  |  |  |         definition, | 
					
						
							|  |  |  |         ORM, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       if (!col) return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (attribute.required === true) { | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |           (definition.client !== 'sqlite3' || !tableExists) && | 
					
						
							|  |  |  |           !contentTypesUtils.hasDraftAndPublish(model) && // no require constraint to allow drafts
 | 
					
						
							|  |  |  |           definition.modelType !== 'component' // no require constraint to allow components in drafts
 | 
					
						
							|  |  |  |         ) { | 
					
						
							|  |  |  |           col.notNullable(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         col.nullable(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (attribute.unique === true) { | 
					
						
							|  |  |  |         if (definition.client !== 'sqlite3' || !tableExists) { | 
					
						
							|  |  |  |           tbl.unique(key, uniqueColName(table, key)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (alter) { | 
					
						
							|  |  |  |         col.alter(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const alterColumns = (tbl, columns, opts = {}) => { | 
					
						
							|  |  |  |     return createColumns(tbl, columns, { ...opts, alter: true }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const createTable = (table, { trx = ORM.knex, ...opts } = {}) => { | 
					
						
							|  |  |  |     return trx.schema.createTable(table, tbl => { | 
					
						
							|  |  |  |       createIdType(tbl); | 
					
						
							|  |  |  |       createColumns(tbl, attributes, { ...opts, tableExists: false }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!tableExists) { | 
					
						
							|  |  |  |     await createTable(table); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |   const attributesNames = Object.keys(attributes); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Fetch existing column
 | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  |   const columnsInfo = await Promise.all( | 
					
						
							|  |  |  |     attributesNames.map(attributeName => getColumnInfo(attributeName, table, ORM)) | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  |   const nameOfColumnsToAdd = columnsInfo.filter(info => !info.exists).map(info => info.columnName); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  |   const columnsToAdd = _.pick(attributes, nameOfColumnsToAdd); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Generate and execute query to add missing column
 | 
					
						
							|  |  |  |   if (Object.keys(columnsToAdd).length > 0) { | 
					
						
							|  |  |  |     await ORM.knex.schema.table(table, tbl => { | 
					
						
							|  |  |  |       createColumns(tbl, columnsToAdd, { tableExists }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  |   const attrsNameWithoutTimestamps = attributesNames.filter( | 
					
						
							|  |  |  |     columnName => !(definition.options.timestamps || []).includes(columnName) | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 14:27:57 +01:00
										 |  |  |   const columnsToAlter = await getColumnsWhereDefinitionChanged( | 
					
						
							| 
									
										
										
										
											2020-10-16 18:53:11 +02:00
										 |  |  |     attrsNameWithoutTimestamps, | 
					
						
							|  |  |  |     definition, | 
					
						
							|  |  |  |     ORM | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |   const shouldRebuild = | 
					
						
							|  |  |  |     columnsToAlter.length > 0 || (definition.client === 'sqlite3' && context.recreateSqliteTable); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |   if (shouldRebuild) { | 
					
						
							|  |  |  |     switch (definition.client) { | 
					
						
							|  |  |  |       case 'sqlite3': { | 
					
						
							|  |  |  |         const tmpTable = `tmp_${table}`; | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |         const rebuildTable = async trx => { | 
					
						
							|  |  |  |           await trx.schema.renameTable(table, tmpTable); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |           // drop possible conflicting indexes
 | 
					
						
							|  |  |  |           await Promise.all( | 
					
						
							|  |  |  |             attributesNames.map(key => | 
					
						
							|  |  |  |               trx.raw('DROP INDEX IF EXISTS ??', uniqueColName(table, key)) | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |           // create the table
 | 
					
						
							|  |  |  |           await createTable(table, { trx }); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |           const attrs = attributesNames.filter(attributeName => | 
					
						
							|  |  |  |             isColumn({ | 
					
						
							|  |  |  |               definition, | 
					
						
							|  |  |  |               attribute: attributes[attributeName], | 
					
						
							|  |  |  |               name: attributeName, | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |           ); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |           const allAttrs = ['id', ...attrs]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           await trx.insert(qb => qb.select(allAttrs).from(tmpTable)).into(table); | 
					
						
							|  |  |  |           await trx.schema.dropTableIfExists(tmpTable); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           await ORM.knex.transaction(trx => rebuildTable(trx)); | 
					
						
							|  |  |  |         } catch (err) { | 
					
						
							|  |  |  |           if (err.message.includes('UNIQUE constraint failed')) { | 
					
						
							|  |  |  |             strapi.log.error( | 
					
						
							|  |  |  |               `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${err.stack}` | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             strapi.log.error(`Migration failed`); | 
					
						
							|  |  |  |             strapi.log.error(err); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |       default: { | 
					
						
							|  |  |  |         const alterTable = async trx => { | 
					
						
							|  |  |  |           await Promise.all( | 
					
						
							|  |  |  |             columnsToAlter.map(col => { | 
					
						
							|  |  |  |               return ORM.knex.schema | 
					
						
							|  |  |  |                 .alterTable(table, tbl => { | 
					
						
							|  |  |  |                   tbl.dropUnique(col, uniqueColName(table, col)); | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 .catch(() => {}); | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |           ); | 
					
						
							| 
									
										
										
										
											2020-12-17 12:33:00 +01:00
										 |  |  |           await trx.schema.alterTable(table, tbl => { | 
					
						
							|  |  |  |             alterColumns(tbl, _.pick(attributes, columnsToAlter), { | 
					
						
							|  |  |  |               tableExists, | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           await ORM.knex.transaction(trx => alterTable(trx)); | 
					
						
							|  |  |  |         } catch (err) { | 
					
						
							|  |  |  |           if (err.code === '23505' && definition.client === 'pg') { | 
					
						
							|  |  |  |             strapi.log.error( | 
					
						
							|  |  |  |               `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${err.message}\n\t- ${err.detail}` | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           } else if (definition.client === 'mysql' && err.errno === 1062) { | 
					
						
							|  |  |  |             strapi.log.error( | 
					
						
							|  |  |  |               `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${err.sqlMessage}` | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             strapi.log.error(`Migration failed`); | 
					
						
							|  |  |  |             strapi.log.error(err); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return false; | 
					
						
							| 
									
										
										
										
											2020-10-14 19:04:22 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-11-06 14:27:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | module.exports = async ({ ORM, loadedModel, definition, connection, model }) => { | 
					
						
							| 
									
										
										
										
											2021-03-25 14:59:44 +01:00
										 |  |  |   const previousDefinition = await getDefinitionFromStore(definition, ORM); | 
					
						
							| 
									
										
										
										
											2021-02-16 17:43:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-06 14:27:57 +01:00
										 |  |  |   // run migrations
 | 
					
						
							| 
									
										
										
										
											2021-02-16 12:08:35 +01:00
										 |  |  |   await strapi.db.migrations.run(migrateSchemas, { | 
					
						
							| 
									
										
										
										
											2021-02-12 12:52:29 +01:00
										 |  |  |     ORM, | 
					
						
							|  |  |  |     loadedModel, | 
					
						
							| 
									
										
										
										
											2021-02-16 17:43:41 +01:00
										 |  |  |     previousDefinition, | 
					
						
							| 
									
										
										
										
											2021-02-12 12:52:29 +01:00
										 |  |  |     definition, | 
					
						
							|  |  |  |     connection, | 
					
						
							|  |  |  |     model, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-11-06 14:27:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // store new definitions
 | 
					
						
							|  |  |  |   await storeDefinition(definition, ORM); | 
					
						
							|  |  |  | }; |