mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
recursive delete cascade
This commit is contained in:
parent
12c9e638f5
commit
571be4b3f9
@ -300,7 +300,7 @@ function changeSetUpdateToSql(item: ChangeSetItem): Update {
|
||||
};
|
||||
}
|
||||
|
||||
export function changeSetDeleteToSql(item: ChangeSetItem): Delete {
|
||||
function changeSetDeleteToSql(item: ChangeSetItem): Delete {
|
||||
return {
|
||||
from: {
|
||||
name: {
|
||||
|
@ -1,117 +1,136 @@
|
||||
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[];
|
||||
}
|
||||
|
||||
// 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: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
conditionType: 'exists',
|
||||
subQuery: {
|
||||
commandType: 'select',
|
||||
from: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
alias: 't0',
|
||||
relations: subFkPath.map((fkItem, fkIndex) => ({
|
||||
joinType: 'INNER JOIN',
|
||||
alias: `t${fkIndex + 1}`,
|
||||
name: {
|
||||
pureName: fkItem.refTableName,
|
||||
schemaName: fkItem.refSchemaName,
|
||||
},
|
||||
conditions: fkItem.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: `t${fkIndex}` },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.refColumnName,
|
||||
source: { alias: `t${fkIndex + 1}` },
|
||||
},
|
||||
})),
|
||||
})),
|
||||
},
|
||||
where: {
|
||||
conditionType: 'and',
|
||||
conditions: [
|
||||
extractChangeSetCondition(baseCmd, `t${subFkPath.length}`),
|
||||
// @ts-ignore
|
||||
...table.primaryKey.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: 't0' },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: {
|
||||
name: fk,
|
||||
},
|
||||
},
|
||||
})),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
let resItem = result.find(x => x.title == fk.pureName);
|
||||
if (!resItem) {
|
||||
resItem = {
|
||||
title: fk.pureName,
|
||||
commands: [],
|
||||
};
|
||||
result.push(resItem);
|
||||
}
|
||||
resItem.commands.push(refCmd);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDeleteCascades(changeSet: ChangeSet, dbinfo: DatabaseInfo): ChangeSetDeleteCascade[] {
|
||||
const res: 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;
|
||||
const dependencies = allForeignKeys.filter(
|
||||
x => x.refSchemaName == table.schemaName && x.refTableName == table.pureName
|
||||
);
|
||||
for (const fk of dependencies) {
|
||||
const refCmd: Delete = {
|
||||
commandType: 'delete',
|
||||
from: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
},
|
||||
where: {
|
||||
conditionType: 'exists',
|
||||
subQuery: {
|
||||
commandType: 'select',
|
||||
from: {
|
||||
name: {
|
||||
pureName: fk.pureName,
|
||||
schemaName: fk.schemaName,
|
||||
},
|
||||
alias: 't1',
|
||||
relations: [
|
||||
{
|
||||
joinType: 'INNER JOIN',
|
||||
alias: 't2',
|
||||
name: {
|
||||
pureName: fk.refTableName,
|
||||
schemaName: fk.refSchemaName,
|
||||
},
|
||||
conditions: fk.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: 't1' },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.refColumnName,
|
||||
source: { alias: 't2' },
|
||||
},
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
where: {
|
||||
conditionType: 'and',
|
||||
conditions: [
|
||||
extractChangeSetCondition(baseCmd, 't2'),
|
||||
// @ts-ignore
|
||||
...table.primaryKey.columns.map(column => ({
|
||||
conditionType: 'binary',
|
||||
operator: '=',
|
||||
left: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: { alias: 't1' },
|
||||
},
|
||||
right: {
|
||||
exprType: 'column',
|
||||
columnName: column.columnName,
|
||||
source: {
|
||||
name: {
|
||||
pureName: fk.refTableName,
|
||||
schemaName: fk.refSchemaName,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
let resItem = res.find(x => x.title == fk.pureName);
|
||||
if (!resItem) {
|
||||
resItem = {
|
||||
title: fk.pureName,
|
||||
commands: [],
|
||||
};
|
||||
res.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);
|
||||
}
|
||||
processDependencies(changeSet, result, allForeignKeys, [], table, baseCmd, dbinfo);
|
||||
|
||||
resItem.commands.push(changeSetDeleteToSql(baseCmd));
|
||||
// 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;
|
||||
}
|
||||
|
@ -32,10 +32,12 @@
|
||||
<SqlEditor
|
||||
{engine}
|
||||
value={values.deleteReferencesCascade
|
||||
? deleteCascadesScripts
|
||||
.filter(({ script, title }) => values[`deleteReferences_${title}`] !== false)
|
||||
.map(({ script, title }) => script)
|
||||
.join('\n')
|
||||
? [
|
||||
...deleteCascadesScripts
|
||||
.filter(({ script, title }) => values[`deleteReferences_${title}`] !== false)
|
||||
.map(({ script, title }) => script),
|
||||
sql,
|
||||
].join('\n')
|
||||
: sql}
|
||||
readOnly
|
||||
/>
|
||||
@ -43,23 +45,32 @@
|
||||
</div>
|
||||
|
||||
{#if !_.isEmpty(deleteCascadesScripts)}
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Delete references CASCADE"
|
||||
name="deleteReferencesCascade"
|
||||
/>
|
||||
<div class="mt-2">
|
||||
<FormCheckboxField
|
||||
templateProps={{ noMargin: true }}
|
||||
label="Delete references CASCADE"
|
||||
name="deleteReferencesCascade"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<FormValues let:values>
|
||||
{#if values.deleteReferencesCascade}
|
||||
{#each _.sortBy(deleteCascadesScripts, 'title') as deleteTable}
|
||||
<FormCheckboxField
|
||||
defaultValue={true}
|
||||
templateProps={{ noMargin: true }}
|
||||
label={deleteTable.title}
|
||||
name={`deleteReferences_${deleteTable.title}`}
|
||||
/>
|
||||
{/each}
|
||||
<!-- <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
|
||||
.filter(({ script, title }) => e.detail[`deleteReferences_${title}`] !== false)
|
||||
.map(({ script, title }) => script)
|
||||
.join('\n')
|
||||
? [
|
||||
...deleteCascadesScripts
|
||||
.filter(({ script, title }) => e.detail[`deleteReferences_${title}`] !== false)
|
||||
.map(({ script, title }) => script),
|
||||
sql,
|
||||
].join('\n')
|
||||
: null
|
||||
);
|
||||
}}
|
||||
|
Loading…
Reference in New Issue
Block a user