recursive delete cascade

This commit is contained in:
Jan Prochazka 2021-09-28 19:03:53 +02:00
parent 12c9e638f5
commit 571be4b3f9
3 changed files with 153 additions and 121 deletions

View File

@ -300,7 +300,7 @@ function changeSetUpdateToSql(item: ChangeSetItem): Update {
};
}
export function changeSetDeleteToSql(item: ChangeSetItem): Delete {
function changeSetDeleteToSql(item: ChangeSetItem): Delete {
return {
from: {
name: {

View File

@ -1,23 +1,38 @@
import _ from 'lodash';
import { Command, Insert, Update, Delete, UpdateField, Condition, AllowIdentityInsert } from 'dbgate-sqltree';
import { NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
import { ChangeSet, extractChangeSetCondition, changeSetDeleteToSql } from './ChangeSet';
import { NamedObjectInfo, DatabaseInfo, ForeignKeyInfo, TableInfo } from 'dbgate-types';
import { ChangeSet, ChangeSetItem, extractChangeSetCondition } from './ChangeSet';
export interface ChangeSetDeleteCascade {
title: string;
commands: Command[];
}
export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): ChangeSetDeleteCascade[] {
const res: ChangeSetDeleteCascade[] = [];
const allForeignKeys = _.flatten(dbinfo.tables.map(x => x.foreignKeys));
for (const baseCmd of changeSet.deletes) {
const table = dbinfo.tables.find(x => x.pureName == baseCmd.pureName && x.schemaName == baseCmd.schemaName);
if (!table.primaryKey) continue;
// function getDeleteScript()
function processDependencies(
changeSet: ChangeSet,
result: ChangeSetDeleteCascade[],
allForeignKeys: ForeignKeyInfo[],
fkPath: ForeignKeyInfo[],
table: TableInfo,
baseCmd: ChangeSetItem,
dbinfo: DatabaseInfo
) {
const dependencies = allForeignKeys.filter(
x => x.refSchemaName == table.schemaName && x.refTableName == table.pureName
);
for (const fk of dependencies) {
if (fk.pureName == baseCmd.pureName) continue;
if (result.find(x => x.title == fk.pureName)) continue;
const depTable = dbinfo.tables.find(x => x.pureName == fk.pureName && x.schemaName == fk.schemaName);
const subFkPath = [...fkPath, fk];
if (depTable) {
processDependencies(changeSet, result, allForeignKeys, subFkPath, depTable, baseCmd, dbinfo);
}
const refCmd: Delete = {
commandType: 'delete',
from: {
@ -35,36 +50,34 @@ export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): C
pureName: fk.pureName,
schemaName: fk.schemaName,
},
alias: 't1',
relations: [
{
alias: 't0',
relations: subFkPath.map((fkItem, fkIndex) => ({
joinType: 'INNER JOIN',
alias: 't2',
alias: `t${fkIndex + 1}`,
name: {
pureName: fk.refTableName,
schemaName: fk.refSchemaName,
pureName: fkItem.refTableName,
schemaName: fkItem.refSchemaName,
},
conditions: fk.columns.map(column => ({
conditions: fkItem.columns.map(column => ({
conditionType: 'binary',
operator: '=',
left: {
exprType: 'column',
columnName: column.columnName,
source: { alias: 't1' },
source: { alias: `t${fkIndex}` },
},
right: {
exprType: 'column',
columnName: column.refColumnName,
source: { alias: 't2' },
source: { alias: `t${fkIndex + 1}` },
},
})),
},
],
})),
},
where: {
conditionType: 'and',
conditions: [
extractChangeSetCondition(baseCmd, 't2'),
extractChangeSetCondition(baseCmd, `t${subFkPath.length}`),
// @ts-ignore
...table.primaryKey.columns.map(column => ({
conditionType: 'binary',
@ -72,16 +85,13 @@ export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): C
left: {
exprType: 'column',
columnName: column.columnName,
source: { alias: 't1' },
source: { alias: 't0' },
},
right: {
exprType: 'column',
columnName: column.columnName,
source: {
name: {
pureName: fk.refTableName,
schemaName: fk.refSchemaName,
},
name: fk,
},
},
})),
@ -90,28 +100,37 @@ export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): C
},
},
};
let resItem = res.find(x => x.title == fk.pureName);
let resItem = result.find(x => x.title == fk.pureName);
if (!resItem) {
resItem = {
title: fk.pureName,
commands: [],
};
res.push(resItem);
result.push(resItem);
}
resItem.commands.push(refCmd);
}
let resItem = res.find(x => x.title == baseCmd.pureName);
if (!resItem) {
resItem = {
title: baseCmd.pureName,
commands: [],
};
res.push(resItem);
}
resItem.commands.push(changeSetDeleteToSql(baseCmd));
export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): ChangeSetDeleteCascade[] {
const result: ChangeSetDeleteCascade[] = [];
const allForeignKeys = _.flatten(dbinfo.tables.map(x => x.foreignKeys));
for (const baseCmd of changeSet.deletes) {
const table = dbinfo.tables.find(x => x.pureName == baseCmd.pureName && x.schemaName == baseCmd.schemaName);
if (!table.primaryKey) continue;
processDependencies(changeSet, result, allForeignKeys, [], table, baseCmd, dbinfo);
// let resItem = result.find(x => x.title == baseCmd.pureName);
// if (!resItem) {
// resItem = {
// title: baseCmd.pureName,
// commands: [],
// };
// result.push(resItem);
// }
// resItem.commands.push(changeSetDeleteToSql(baseCmd));
}
return res;
return result;
}

View File

@ -32,10 +32,12 @@
<SqlEditor
{engine}
value={values.deleteReferencesCascade
? deleteCascadesScripts
? [
...deleteCascadesScripts
.filter(({ script, title }) => values[`deleteReferences_${title}`] !== false)
.map(({ script, title }) => script)
.join('\n')
.map(({ script, title }) => script),
sql,
].join('\n')
: sql}
readOnly
/>
@ -43,23 +45,32 @@
</div>
{#if !_.isEmpty(deleteCascadesScripts)}
<div class="mt-2">
<FormCheckboxField
templateProps={{ noMargin: true }}
label="Delete references CASCADE"
name="deleteReferencesCascade"
/>
</div>
{/if}
<FormValues let:values>
{#if values.deleteReferencesCascade}
<!-- <div class="form-margin flex">
</div> -->
<div class="form-margin flex">
{#each _.sortBy(deleteCascadesScripts, 'title') as deleteTable}
<div class="mr-1">
<FormCheckboxField
defaultValue={true}
templateProps={{ noMargin: true }}
label={deleteTable.title}
name={`deleteReferences_${deleteTable.title}`}
/>
</div>
{/each}
</div>
{/if}
</FormValues>
@ -86,10 +97,12 @@
closeCurrentModal();
onConfirm(
e.detail.deleteReferencesCascade
? deleteCascadesScripts
? [
...deleteCascadesScripts
.filter(({ script, title }) => e.detail[`deleteReferences_${title}`] !== false)
.map(({ script, title }) => script)
.join('\n')
.map(({ script, title }) => script),
sql,
].join('\n')
: null
);
}}