diff --git a/packages/nocodb/src/lib/noco/common/handlers/xcMetaDiffSync.ts b/packages/nocodb/src/lib/noco/common/handlers/xcMetaDiffSync.ts index 49f26b8b1d..a87283d03f 100644 --- a/packages/nocodb/src/lib/noco/common/handlers/xcMetaDiffSync.ts +++ b/packages/nocodb/src/lib/noco/common/handlers/xcMetaDiffSync.ts @@ -38,16 +38,20 @@ export default async function(this: BaseApiBuilder | any) { { condition: { type: 'table' } } ); + const oldMetasRef = {}; // @ts-ignore const oldMetas = oldModels.map(m => { const meta = JSON.parse(m.meta); XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); meta.id = m.id; + populateParams.oldMetas[meta.tn] = meta; + oldMetasRef[meta.tn] = meta; return meta; }); + const oldQueryParams = oldModels.map(m => JSON.parse(m.query_params)); - const relationTableMetas = new Set(); + const relationTableMetas = new Set(); for (const { tn, detectedChanges } of changes) { if (!detectedChanges?.length) continue; @@ -80,8 +84,16 @@ export default async function(this: BaseApiBuilder | any) { this.projectId, this.dbAlias, 'nc_models', + null, { - title: tn + _or: [ + { + title: { eq: tn } + }, + { + parent_model_title: { eq: tn } + } + ] } ); if (delete this.metas[tn]) delete this.metas[tn]; @@ -142,11 +154,11 @@ export default async function(this: BaseApiBuilder | any) { /* update sort field */ /* const sIndex = (sortList || []).findIndex( - v => v.field === oldColumn._cn - ); - if (sIndex > -1) { - sortList.splice(sIndex, 1); - }*/ + v => v.field === oldColumn._cn + ); + if (sIndex > -1) { + sortList.splice(sIndex, 1); + }*/ for (const sort of sortList || []) { if ( sort?.field === oldColumn.cn || @@ -164,12 +176,12 @@ export default async function(this: BaseApiBuilder | any) { /* update filters */ // todo: remove only corresponding filter and compare field name /* if ( - filters && - (JSON.stringify(filters)?.includes(`"${oldColumn.cn}"`) || - JSON.stringify(filters)?.includes(`"${oldColumn._cn}"`)) - ) { - filters.splice(0, filters.length); - }*/ + filters && + (JSON.stringify(filters)?.includes(`"${oldColumn.cn}"`) || + JSON.stringify(filters)?.includes(`"${oldColumn._cn}"`)) + ) { + filters.splice(0, filters.length); + }*/ for (const filter of filters) { if ( filter?.field === oldColumn.cn || @@ -204,9 +216,9 @@ export default async function(this: BaseApiBuilder | any) { if (oldMeta.belongsTo?.length) { for (const bt of oldMeta.belongsTo) { // filter out lookup columns which maps to current col - this.metas[bt.rtn].v = this.metas[bt.rtn].v?.filter(v => { + oldMetasRef[bt.rtn].v = oldMetasRef[bt.rtn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === oldColumn.cn) { - relationTableMetas.add(this.metas[bt.rtn]); + relationTableMetas.add(oldMetasRef[bt.rtn]); return false; } return true; @@ -218,9 +230,9 @@ export default async function(this: BaseApiBuilder | any) { if (oldMeta.hasMany?.length) { for (const hm of oldMeta.hasMany) { // filter out lookup columns which maps to current col - this.metas[hm.tn].v = this.metas[hm.tn].v?.filter(v => { + oldMetasRef[hm.tn].v = oldMetasRef[hm.tn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === change.cn) { - relationTableMetas.add(this.metas[hm.tn]); + relationTableMetas.add(oldMetasRef[hm.tn]); return false; } return true; @@ -232,9 +244,9 @@ export default async function(this: BaseApiBuilder | any) { if (oldMeta.manyToMany?.length) { for (const mm of oldMeta.manyToMany) { // filter out lookup columns which maps to current col - this.metas[mm.rtn].v = this.metas[mm.rtn].v?.filter(v => { + oldMetasRef[mm.rtn].v = oldMetasRef[mm.rtn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === change.cn) { - relationTableMetas.add(this.metas[mm.rtn]); + relationTableMetas.add(oldMetasRef[mm.rtn]); return false; } return true; @@ -245,7 +257,7 @@ export default async function(this: BaseApiBuilder | any) { break; case XcMetaDiffType.TABLE_RELATION_ADD: { - if (change.tn === tn) + if (change.tn === tn) { // todo : enable // ignore duplicate await this.xcMeta.metaInsert( @@ -263,13 +275,116 @@ export default async function(this: BaseApiBuilder | any) { db_type: this.connectionConfig?.client // todo: get these info /* dr: , - ur: onUpdate, - fkn*/ + ur: onUpdate, + fkn*/ + } + ); + populateParams.tableNames.push({ tn: change.tn }); + populateParams.tableNames.push({ tn: change.rtn }); + } + } + break; + case XcMetaDiffType.TABLE_VIRTUAL_M2M_REMOVE: + { + for (const tn of [change.mm.tn, change.mm.rtn]) { + const { + // @ts-ignore + virtualViews, + virtualViewsParamsArr + } = await this.extractSharedAndVirtualViewsParams(tn); + + const oldMeta = oldMetas.find(m => m.tn === tn); + populateParams.oldMetas[tn] = oldMeta; + populateParams.tableNames.push({ + tn, + _tn: populateParams.oldMetas[tn]?._tn + }); + + // extract alias of relation virtual column + const alias = oldMeta?.v?.find( + v => + v?.mm?.tn === change.mm.tn && + v?.mm?.vtn === change.mm.vtn && + v?.mm?.rtn === change.mm.rtn + )?._cn; + + // virtual views param update + for (const qp of virtualViewsParamsArr) { + // @ts-ignore + const { + showFields = {}, + fieldsOrder, + extraViewParams = {} + } = qp; + + /* update show field */ + if (alias in showFields) { + delete showFields[alias]; + } + + /* update fieldsOrder */ + const index = fieldsOrder.indexOf(alias); + if (index > -1) { + fieldsOrder.splice(index, 1); + } + + /* update formView params */ + if (extraViewParams?.formParams?.fields?.[alias]) { + delete extraViewParams.formParams.fields[alias]; } + } + // todo: enable + await this.updateSharedAndVirtualViewsParams( + virtualViewsParamsArr, + virtualViews ); + } + + const parentMeta = oldMetas.find(m => m.tn === change.mm.tn); + + Object.assign(parentMeta, { + v: parentMeta.v.filter( + ({ mm, lk, rl }) => + (!mm || mm.rtn !== change.mm.rtn || mm.tn !== change.mm.tn) && + !( + lk && + lk.type === 'hm' && + lk.rtn === change.mm.rtn && + lk.tn === change.mm.tn + ) && + !( + rl && + rl.type === 'hm' && + rl.rtn === change.mm.rtn && + rl.tn === change.mm.tn + ) + ) + }); + + const childMeta = oldMetas.find(m => m.tn === change.mm.rtn); + + Object.assign(childMeta, { + v: childMeta.v.filter( + ({ mm, lk, rl }) => + (!mm || mm.rtn !== change.mm.tn || mm.tn !== change.mm.rtn) && + !( + lk && + lk.type === 'hm' && + lk.rtn === change.mm.tn && + lk.tn === change.mm.rtn + ) && + !( + rl && + rl.type === 'hm' && + rl.rtn === change.mm.tn && + rl.tn === change.mm.rtn + ) + ) + }); } break; case XcMetaDiffType.TABLE_RELATION_REMOVE: + case XcMetaDiffType.TABLE_VIRTUAL_RELATION_REMOVE: { // todo: remove from nc_relations // todo:enable @@ -282,7 +397,10 @@ export default async function(this: BaseApiBuilder | any) { cn: change.cn, rtn: change.rtn, rcn: change.rcn, - type: 'real' + type: + XcMetaDiffType.TABLE_RELATION_REMOVE === change.type + ? 'real' + : 'virtual' // db_type: this.connectionConfig?.client } ); @@ -383,12 +501,20 @@ export default async function(this: BaseApiBuilder | any) { } } + // update relation tables metadata + for (const relMeta of relationTableMetas) { + populateParams.tableNames.push({ + tn: relMeta.tn, + _tn: relMeta._tn + }); + populateParams.oldMetas[relMeta.tn] = relMeta; + } + // todo: optimize // remove duplicate from list populateParams.tableNames = populateParams.tableNames?.filter(t => { return t === populateParams.tableNames.find(t1 => t1.tn === t.tn); }); - await this.xcTablesPopulate(populateParams); return populateParams; diff --git a/packages/nocodb/src/lib/noco/meta/handlers/xcMetaDiff.ts b/packages/nocodb/src/lib/noco/meta/handlers/xcMetaDiff.ts index c0f7abf96d..c9f13d7772 100644 --- a/packages/nocodb/src/lib/noco/meta/handlers/xcMetaDiff.ts +++ b/packages/nocodb/src/lib/noco/meta/handlers/xcMetaDiff.ts @@ -9,7 +9,8 @@ enum XcMetaDiffType { TABLE_RELATION_ADD = 'TABLE_RELATION_ADD', TABLE_RELATION_REMOVE = 'TABLE_RELATION_REMOVE', TABLE_VIRTUAL_RELATION_ADD = 'TABLE_VIRTUAL_RELATION_ADD', - TABLE_VIRTUAL_RELATION_DELETE = 'TABLE_VIRTUAL_RELATION_DELETE' + TABLE_VIRTUAL_RELATION_REMOVE = 'TABLE_VIRTUAL_RELATION_REMOVE', + TABLE_VIRTUAL_M2M_REMOVE = 'TABLE_VIRTUAL_M2M_REMOVE' } interface NcMetaDiff { @@ -33,7 +34,7 @@ export default async function( // @ts-ignore const tableList = (await sqlClient.tableList())?.data?.list; - + const colListRef = {}; // @ts-ignore const oldMetas = ( await this.xcMeta.metaList( @@ -44,6 +45,9 @@ export default async function( ) ).map(m => JSON.parse(m.meta)); + // @ts-ignore + const relationList = (await sqlClient.relationListAll())?.data?.list; + for (const table of tableList) { if (table.tn === 'nc_evolutions') continue; @@ -73,10 +77,11 @@ export default async function( changes.push(tableProp); // check for column change - const columnList = (await sqlClient.columnList({ tn: table.tn }))?.data - ?.list; + colListRef[table.tn] = ( + await sqlClient.columnList({ tn: table.tn }) + )?.data?.list; - for (const column of columnList) { + for (const column of colListRef[table.tn]) { const oldColIdx = oldMeta.columns.findIndex(c => c.cn === column.cn); // new table @@ -107,6 +112,59 @@ export default async function( cn }); } + for (const vCol of oldMeta.v) { + if (!vCol.mm) continue; + + // check related tables & columns + + const rTable = tableList.find(t => t.tn === vCol.mm?.rtn); + const m2mTable = tableList.find(t => t.tn === vCol.mm?.vtn); + + if (!rTable) { + tableProp.detectedChanges.push({ + ...vCol, + type: XcMetaDiffType.TABLE_VIRTUAL_M2M_REMOVE, + msg: `Many to many removed(${vCol.mm?.rtn} removed)` + }); + continue; + } + if (!m2mTable) { + tableProp.detectedChanges.push({ + ...vCol, + type: XcMetaDiffType.TABLE_VIRTUAL_M2M_REMOVE, + msg: `Many to many removed(${vCol.mm?.vtn} removed)` + }); + continue; + } + + // verify columns + + const pColumns = (colListRef[vCol.mm.tn] = + colListRef[vCol.mm.tn] || + (await sqlClient.columnList({ tn: vCol.mm.tn }))?.data?.list); + + const cColumns = (colListRef[vCol.mm.rtn] = + colListRef[vCol.mm.rtn] || + (await sqlClient.columnList({ tn: vCol.mm.rtn }))?.data?.list); + + const vColumns = (colListRef[vCol.mm.vtn] = + colListRef[vCol.mm.vtn] || + (await sqlClient.columnList({ tn: vCol.mm.vtn }))?.data?.list); + + if ( + pColumns.every(c => c.cn !== vCol.mm.cn) || + cColumns.every(c => c.cn !== vCol.mm.rcn) || + vColumns.every(c => c.cn !== vCol.mm.vcn) || + vColumns.every(c => c.cn !== vCol.mm.vrcn) + ) { + tableProp.detectedChanges.push({ + ...vCol, + type: XcMetaDiffType.TABLE_VIRTUAL_M2M_REMOVE, + msg: `Many to many removed(One of the relation column removed)` + }); + continue; + } + } } for (const { tn } of oldMetas) { @@ -121,20 +179,30 @@ export default async function( }); } - // @ts-ignore - const relationList = (await sqlClient.relationListAll())?.data?.list; - - // todo: handle virtual - const oldRelations = await this.xcMeta.metaList( - this.getProjectId(args), - this.getDbAlias(args), - 'nc_relations', - { - condition: { - type: 'real' + // extract unique relations + const oldRelations = ( + await this.xcMeta.metaList( + this.getProjectId(args), + this.getDbAlias(args), + 'nc_relations', + { + condition: { + type: 'real' + } } - } - ); + ) + ).filter((r, i, arr) => { + return ( + i === + arr.findIndex( + r1 => + r1.tn === r.tn && + r1.rtn === r.rtn && + r1.cn === r.cn && + r1.rcn === r.rcn + ) + ); + }); // check relations for (const rel of relationList) { @@ -197,6 +265,80 @@ export default async function( }); } + const oldVirtualRelations = await this.xcMeta.metaList( + this.getProjectId(args), + this.getDbAlias(args), + 'nc_relations', + { + condition: { + type: 'virtual' + } + } + ); + + // check relations + for (const vRel of oldVirtualRelations) { + if (tableList.every(t => t.tn !== vRel.tn && t.tn !== vRel.rtn)) { + changes + .find(t => t.tn === vRel.tn) + ?.detectedChanges.push({ + type: XcMetaDiffType.TABLE_VIRTUAL_RELATION_REMOVE, + tn: vRel.tn, + rtn: vRel.rtn, + cn: vRel.cn, + rcn: vRel.rcn, + msg: `Virtual relation removed` + }); + changes + .find(t => t.tn === vRel.rtn) + ?.detectedChanges.push({ + type: XcMetaDiffType.TABLE_VIRTUAL_RELATION_REMOVE, + tn: vRel.tn, + rtn: vRel.rtn, + cn: vRel.cn, + rcn: vRel.rcn, + msg: `Virtual relation removed` + }); + continue; + } + + colListRef[vRel.tn] = ( + await sqlClient.columnList({ tn: vRel.tn }) + )?.data?.list; + colListRef[vRel.rtn] = ( + await sqlClient.columnList({ tn: vRel.rtn }) + )?.data?.list; + + if ( + colListRef[vRel.tn].every(c => c.cn !== vRel.cn) || + colListRef[vRel.rtn].every(c => c.cn !== vRel.rcn) + ) { + changes + .find(t => t.tn === vRel.tn) + ?.detectedChanges.push({ + type: XcMetaDiffType.TABLE_VIRTUAL_RELATION_REMOVE, + tn: vRel.tn, + rtn: vRel.rtn, + cn: vRel.cn, + rcn: vRel.rcn, + msg: `Virtual relation column missing` + }); + changes + .find(t => t.tn === vRel.rtn) + ?.detectedChanges.push({ + type: XcMetaDiffType.TABLE_VIRTUAL_RELATION_REMOVE, + tn: vRel.tn, + rtn: vRel.rtn, + cn: vRel.cn, + rcn: vRel.rcn, + msg: `Virtual relation column missing` + }); + } + + // colListRef[table.tn]= (await sqlClient.columnList({ tn: table.tn }))?.data + // ?.list; + } + return changes; } export { XcMetaDiffType, NcMetaDiff }; diff --git a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts index 9ea6fb231c..ee0b0d383d 100644 --- a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts @@ -376,20 +376,21 @@ export class RestApiBuilder extends BaseApiBuilder { if (args?.tableNames?.length) { const relatedTableList = []; - // extract tables which have relation with the tables in list - for (const r of relations) { - if (args.tableNames.some(t => t.tn === r.tn)) { - if (!relatedTableList.includes(r.rtn)) { - relatedTableList.push(r.rtn); - await this.onTableDelete(r.rtn); - } - } else if (args.tableNames.some(t => t.tn === r.rtn)) { - if (!relatedTableList.includes(r.tn)) { - relatedTableList.push(r.tn); - await this.onTableDelete(r.tn); + if (!args?.oldMetas) + // extract tables which have relation with the tables in list + for (const r of relations) { + if (args.tableNames.some(t => t.tn === r.tn)) { + if (!relatedTableList.includes(r.rtn)) { + relatedTableList.push(r.rtn); + await this.onTableDelete(r.rtn); + } + } else if (args.tableNames.some(t => t.tn === r.rtn)) { + if (!relatedTableList.includes(r.tn)) { + relatedTableList.push(r.tn); + await this.onTableDelete(r.tn); + } } } - } tables = args.tableNames .sort((a, b) => (a.tn || a._tn).localeCompare(b.tn || b._tn)) diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts index a214de0721..52e95151ff 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts @@ -109,8 +109,8 @@ abstract class BaseModelXcMeta extends BaseRender { const oldVirtualCols = this.ctx?.oldMeta?.v || []; for (const oldVCol of oldVirtualCols) { - console.log(oldVCol); - virtualColumns.push(oldVCol); + if (oldVCol.lk || oldVCol.rl || oldVCol.formula) + virtualColumns.push(oldVCol); } return virtualColumns; diff --git a/scripts/metaSync/queries.js b/scripts/metaSync/queries.js deleted file mode 100644 index 06e893d615..0000000000 --- a/scripts/metaSync/queries.js +++ /dev/null @@ -1,54 +0,0 @@ -const init = `CREATE TABLE \`table_col_delete\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`title\` varchar(45) DEFAULT NULL, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_col_add\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_rel_add_child\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_rel_add_parent\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_rel_remove_child\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`table_to_rel_remove_parent_id\` int DEFAULT NULL, - PRIMARY KEY (\`id\`), - KEY \`table_to_rel_remove_parent_fk_idx\` (\`table_to_rel_remove_parent_id\`), - CONSTRAINT \`table_to_rel_remove_parent_fk\` FOREIGN KEY (\`table_to_rel_remove_parent_id\`) REFERENCES \`table_to_rel_remove_parent\` (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_rel_remove_parent\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - -CREATE TABLE \`table_to_remove\` ( - \`id\` int NOT NULL AUTO_INCREMENT, - \`table_to_removecol\` varchar(45) NOT NULL, - PRIMARY KEY (\`id\`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -` - - - -const colDel = `ALTER TABLE \`sakila\`.\`table_col_delete\` -DROP COLUMN \`title\`;` - -const relDel =`ALTER TABLE \`sakila\`.\`table_to_rel_remove_child\` -DROP FOREIGN KEY \`table_to_rel_remove_parent_fk\`; -ALTER TABLE \`sakila\`.\`table_to_rel_remove_child\` -DROP COLUMN \`table_to_rel_remove_parent_id\`, -DROP INDEX \`table_to_rel_remove_parent_fk_idx\` ; -; -` -const tableDel =`DROP TABLE \`sakila\`.\`table_to_remove\`;` \ No newline at end of file diff --git a/scripts/metaSync/queries.md b/scripts/metaSync/queries.md new file mode 100644 index 0000000000..3ce675fc11 --- /dev/null +++ b/scripts/metaSync/queries.md @@ -0,0 +1,93 @@ + + +### Init + +```sql +CREATE TABLE `table_col_delete` ( + `id` int NOT NULL AUTO_INCREMENT, + `title` varchar(45) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_col_add` ( + `id` int NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_rel_add_child` ( + `id` int NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_rel_add_parent` ( + `id` int NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_rel_remove_parent` ( + `id` int NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_rel_remove_child` ( + `id` int NOT NULL AUTO_INCREMENT, + `table_to_rel_remove_parent_id` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `table_to_rel_remove_parent_fk_idx` (`table_to_rel_remove_parent_id`), + CONSTRAINT `table_to_rel_remove_parent_fk` FOREIGN KEY (`table_to_rel_remove_parent_id`) REFERENCES `table_to_rel_remove_parent` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `table_to_remove` ( + `id` int NOT NULL AUTO_INCREMENT, + `table_to_removecol` varchar(45) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +``` + + +## Col delete + +```sql +ALTER TABLE `table_col_delete` +DROP COLUMN `title`; +``` + +## Rel delete + +```sql +ALTER TABLE `table_to_rel_remove_child` +DROP FOREIGN KEY `table_to_rel_remove_parent_fk`; +ALTER TABLE `table_to_rel_remove_child` +DROP COLUMN `table_to_rel_remove_parent_id`, +DROP INDEX `table_to_rel_remove_parent_fk_idx` ; +``` + +## table delete + +```sql +DROP TABLE `table_to_remove`;` +``` + +## column add + +```sql +ALTER TABLE `table_to_col_add` +ADD COLUMN `new_col_1` VARCHAR(45) NULL AFTER `new_col`; +``` + + +## Add rel + +```sql +ALTER TABLE `table_to_rel_add_child` +ADD COLUMN `parent_id` INT NULL AFTER `id`, +ADD INDEX `_p_fk_idx` (`parent_id` ASC) VISIBLE; +; +ALTER TABLE `table_to_rel_add_child` +ADD CONSTRAINT `_p_fk` + FOREIGN KEY (`parent_id`) + REFERENCES `table_to_rel_add_parent` (`id`) + ON DELETE NO ACTION + ON UPDATE NO ACTION; +```