mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 19:36:00 +00:00
257 lines
6.5 KiB
TypeScript
257 lines
6.5 KiB
TypeScript
import _ from 'lodash';
|
|
import { Command, Insert, Update, Delete, UpdateField, Condition } from '@dbgate/sqltree';
|
|
import { NamedObjectInfo } from '@dbgate/types';
|
|
|
|
export interface ChangeSetItem {
|
|
pureName: string;
|
|
schemaName: string;
|
|
insertedRowIndex?: number;
|
|
condition?: { [column: string]: string };
|
|
fields?: { [column: string]: string };
|
|
}
|
|
|
|
export interface ChangeSet {
|
|
inserts: ChangeSetItem[];
|
|
updates: ChangeSetItem[];
|
|
deletes: ChangeSetItem[];
|
|
}
|
|
|
|
export function createChangeSet(): ChangeSet {
|
|
return {
|
|
inserts: [],
|
|
updates: [],
|
|
deletes: [],
|
|
};
|
|
}
|
|
|
|
export interface ChangeSetRowDefinition {
|
|
pureName: string;
|
|
schemaName: string;
|
|
insertedRowIndex?: number;
|
|
condition?: { [column: string]: string };
|
|
}
|
|
|
|
export interface ChangeSetFieldDefinition extends ChangeSetRowDefinition {
|
|
uniqueName: string;
|
|
columnName: string;
|
|
}
|
|
|
|
export function findExistingChangeSetItem(
|
|
changeSet: ChangeSet,
|
|
definition: ChangeSetRowDefinition
|
|
): [keyof ChangeSet, ChangeSetItem] {
|
|
if (definition.insertedRowIndex != null) {
|
|
return [
|
|
'inserts',
|
|
changeSet.inserts.find(
|
|
x =>
|
|
x.pureName == definition.pureName &&
|
|
x.schemaName == definition.schemaName &&
|
|
x.insertedRowIndex == definition.insertedRowIndex
|
|
),
|
|
];
|
|
} else {
|
|
const inUpdates = changeSet.updates.find(
|
|
x =>
|
|
x.pureName == definition.pureName &&
|
|
x.schemaName == definition.schemaName &&
|
|
_.isEqual(x.condition, definition.condition)
|
|
);
|
|
if (inUpdates) return ['updates', inUpdates];
|
|
|
|
const inDeletes = changeSet.deletes.find(
|
|
x =>
|
|
x.pureName == definition.pureName &&
|
|
x.schemaName == definition.schemaName &&
|
|
_.isEqual(x.condition, definition.condition)
|
|
);
|
|
if (inDeletes) return ['deletes', inDeletes];
|
|
|
|
return ['updates', null];
|
|
}
|
|
}
|
|
|
|
export function setChangeSetValue(
|
|
changeSet: ChangeSet,
|
|
definition: ChangeSetFieldDefinition,
|
|
value: string
|
|
): ChangeSet {
|
|
let [fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
|
if (fieldName == 'deletes') {
|
|
changeSet = revertChangeSetRowChanges(changeSet, definition);
|
|
[fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
|
}
|
|
if (existingItem) {
|
|
return {
|
|
...changeSet,
|
|
[fieldName]: changeSet[fieldName].map(item =>
|
|
item == existingItem
|
|
? {
|
|
...item,
|
|
fields: {
|
|
...item.fields,
|
|
[definition.uniqueName]: value,
|
|
},
|
|
}
|
|
: item
|
|
),
|
|
};
|
|
}
|
|
|
|
return {
|
|
...changeSet,
|
|
[fieldName]: [
|
|
...changeSet[fieldName],
|
|
{
|
|
pureName: definition.pureName,
|
|
schemaName: definition.schemaName,
|
|
condition: definition.condition,
|
|
insertedRowIndex: definition.insertedRowIndex,
|
|
fields: {
|
|
[definition.uniqueName]: value,
|
|
},
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function extractFields(item: ChangeSetItem): UpdateField[] {
|
|
return _.keys(item.fields).map(targetColumn => ({
|
|
targetColumn,
|
|
exprType: 'value',
|
|
value: item.fields[targetColumn],
|
|
}));
|
|
}
|
|
|
|
function insertToSql(item: ChangeSetItem): Insert {
|
|
return {
|
|
targetTable: {
|
|
pureName: item.pureName,
|
|
schemaName: item.schemaName,
|
|
},
|
|
commandType: 'insert',
|
|
fields: extractFields(item),
|
|
};
|
|
}
|
|
|
|
function extractCondition(item: ChangeSetItem): Condition {
|
|
return {
|
|
conditionType: 'and',
|
|
conditions: _.keys(item.condition).map(columnName => ({
|
|
conditionType: 'binary',
|
|
operator: '=',
|
|
left: {
|
|
exprType: 'column',
|
|
columnName,
|
|
source: {
|
|
name: {
|
|
pureName: item.pureName,
|
|
schemaName: item.schemaName,
|
|
},
|
|
},
|
|
},
|
|
right: {
|
|
exprType: 'value',
|
|
value: item.condition[columnName],
|
|
},
|
|
})),
|
|
};
|
|
}
|
|
|
|
function updateToSql(item: ChangeSetItem): Update {
|
|
return {
|
|
from: {
|
|
name: {
|
|
pureName: item.pureName,
|
|
schemaName: item.schemaName,
|
|
},
|
|
},
|
|
commandType: 'update',
|
|
fields: extractFields(item),
|
|
where: extractCondition(item),
|
|
};
|
|
}
|
|
|
|
function deleteToSql(item: ChangeSetItem): Delete {
|
|
return {
|
|
from: {
|
|
name: {
|
|
pureName: item.pureName,
|
|
schemaName: item.schemaName,
|
|
},
|
|
},
|
|
commandType: 'delete',
|
|
where: extractCondition(item),
|
|
};
|
|
}
|
|
|
|
export function changeSetToSql(changeSet: ChangeSet): Command[] {
|
|
return [
|
|
...changeSet.inserts.map(insertToSql),
|
|
...changeSet.updates.map(updateToSql),
|
|
...changeSet.deletes.map(deleteToSql),
|
|
];
|
|
}
|
|
|
|
export function revertChangeSetRowChanges(changeSet: ChangeSet, definition: ChangeSetRowDefinition): ChangeSet {
|
|
const [field, item] = findExistingChangeSetItem(changeSet, definition);
|
|
if (item)
|
|
return {
|
|
...changeSet,
|
|
[field]: changeSet[field].filter(x => x != item),
|
|
};
|
|
return changeSet;
|
|
}
|
|
|
|
export function deleteChangeSetRows(changeSet: ChangeSet, definition: ChangeSetRowDefinition): ChangeSet {
|
|
let [fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
|
if (fieldName == 'updates') {
|
|
changeSet = revertChangeSetRowChanges(changeSet, definition);
|
|
[fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
|
|
}
|
|
if (fieldName == 'inserts') {
|
|
return revertChangeSetRowChanges(changeSet, definition);
|
|
} else {
|
|
if (existingItem && fieldName == 'deletes') return changeSet;
|
|
return {
|
|
...changeSet,
|
|
deletes: [
|
|
...changeSet.deletes,
|
|
{
|
|
pureName: definition.pureName,
|
|
schemaName: definition.schemaName,
|
|
condition: definition.condition,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
}
|
|
|
|
export function getChangeSetInsertedRows(changeSet: ChangeSet, name?: NamedObjectInfo) {
|
|
if (!name) return [];
|
|
if (!changeSet) return [];
|
|
const rows = changeSet.inserts.filter(x => x.pureName == name.pureName && x.schemaName == name.schemaName);
|
|
const maxIndex = _.maxBy(rows, x => x.insertedRowIndex)?.insertedRowIndex;
|
|
if (maxIndex == null) return [];
|
|
const res = Array(maxIndex + 1).fill({});
|
|
for (const row of rows) {
|
|
res[row.insertedRowIndex] = row.fields;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
export function changeSetInsertNewRow(changeSet: ChangeSet, name?: NamedObjectInfo): ChangeSet {
|
|
const insertedRows = getChangeSetInsertedRows(changeSet, name);
|
|
return {
|
|
...changeSet,
|
|
inserts: [
|
|
...changeSet.inserts,
|
|
{
|
|
...name,
|
|
insertedRowIndex: insertedRows.length,
|
|
fields: {},
|
|
},
|
|
],
|
|
};
|
|
}
|