Skip to content

Commit ccd894a

Browse files
LancearPirni
andcommitted
Insert tables with relations on drag and drop
Co-authored-by: Christian P. <pirnichristian@gmail.com>
1 parent cc5b47c commit ccd894a

4 files changed

Lines changed: 111 additions & 46 deletions

File tree

web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ export default class TableSchema extends BaseUISchema {
416416
static getErdSupportedData(data) {
417417
let newData = {...data};
418418
const SUPPORTED_KEYS = [
419-
'name', 'schema', 'description', 'rlspolicy', 'forcerlspolicy', 'fillfactor',
419+
'oid', 'name', 'schema', 'description', 'rlspolicy', 'forcerlspolicy', 'fillfactor',
420420
'toast_tuple_target', 'parallel_workers', 'relhasoids', 'relpersistence',
421421
'columns', 'primary_key', 'foreign_key', 'unique_constraint',
422422
];
@@ -428,12 +428,20 @@ export default class TableSchema extends BaseUISchema {
428428
return c;
429429
});
430430

431-
/* Make autoindex as true if there is coveringindex since ERD works in create mode */
432431
newData.foreign_key = (newData.foreign_key||[]).map((fk)=>{
432+
/* Make autoindex as true if there is coveringindex since ERD works in create mode */
433433
fk.autoindex = false;
434+
434435
if(fk.coveringindex) {
435436
fk.autoindex = true;
436437
}
438+
439+
/* Copy references oid to references_oid for incomplete references to missing tables */
440+
if (fk.columns?.[0]) {
441+
fk.columns[0].references_oid = fk.columns[0].references;
442+
fk.columns[0].references = null;
443+
}
444+
437445
return fk;
438446
});
439447
return newData;

web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ export default class ERDCore {
400400

401401
const addLink = (theFk)=>{
402402
if(!theFk) return;
403+
403404
let newData = {
404405
local_table_uid: tableNode.getID(),
405406
local_column_attnum: undefined,
@@ -635,43 +636,91 @@ export default class ERDCore {
635636

636637
deserializeData(data){
637638
let oidUidMap = {};
639+
let newNodes = [];
638640

639641
/* Add the nodes */
640642
data.forEach((nodeData)=>{
641-
let newNode = this.addNode(TableSchema.getErdSupportedData(nodeData));
643+
const newNode = this.addNode(TableSchema.getErdSupportedData(nodeData));
642644
oidUidMap[nodeData.oid] = newNode.getID();
645+
newNodes.push(newNode);
643646
});
644647

645-
/* Lets use the oidUidMap for creating the links */
646-
let tableNodesDict = this.getModel().getNodesDict();
648+
// When generating for schema, there may be a reference to another schema table
649+
// We'll remove the FK completely in such cases
650+
newNodes.forEach((node) => {
651+
const nodeData = node.getData();
652+
nodeData.foreign_key = nodeData.foreign_key?.filter(fk =>
653+
fk.columns?.[0]?.references_oid && oidUidMap[fk.columns[0].references_oid]
654+
);
655+
});
656+
657+
this.addLinksBetweenNodes(oidUidMap);
658+
}
659+
660+
addNodeWithLinks(nodeData, position=[50,50], metadata={}){
661+
const tableNodesDict = this.getModel().getNodesDict();
662+
const oidExists = Object.values(tableNodesDict).some(node => node.getData().oid === nodeData.oid);
663+
664+
if (oidExists) {
665+
delete nodeData.oid;
666+
}
667+
668+
let oidUidMap = {};
669+
const newNode = this.addNode(nodeData, position, metadata);
670+
671+
if (!oidExists) {
672+
oidUidMap[nodeData.oid] = newNode.getID();
673+
}
674+
647675
_.forIn(tableNodesDict, (node, uid)=>{
648-
let nodeData = node.getData();
649-
if(nodeData.foreign_key) {
650-
nodeData.foreign_key = nodeData.foreign_key.filter((theFk)=>{
651-
delete theFk.oid;
652-
theFk = theFk.columns[0];
653-
theFk.references = oidUidMap[theFk.references];
654-
let newData = {
655-
local_table_uid: uid,
656-
local_column_attnum: undefined,
657-
referenced_table_uid: theFk.references,
658-
referenced_column_attnum: undefined,
659-
};
660-
let sourceNode = tableNodesDict[newData.referenced_table_uid];
661-
let targetNode = tableNodesDict[newData.local_table_uid];
662-
// When generating for schema, there may be a reference to another schema table
663-
// We'll remove the FK completely in such cases.
664-
if(!sourceNode || !targetNode) {
665-
return false;
666-
}
676+
const oid = node.getData().oid;
677+
if (!oid) return;
667678

668-
newData.local_column_attnum = _.find(targetNode.getColumns(), (col)=>col.name==theFk.local_column).attnum;
669-
newData.referenced_column_attnum = _.find(sourceNode.getColumns(), (col)=>col.name==theFk.referenced).attnum;
679+
oidUidMap[oid] = uid;
680+
});
670681

671-
this.addLink(newData, 'onetomany');
672-
return true;
673-
});
674-
}
682+
this.addLinksBetweenNodes(oidUidMap, [newNode.getID()]);
683+
return newNode;
684+
}
685+
686+
addLinksBetweenNodes(oidUidMap, newNodesUids = null) {
687+
const tableNodesDict = this.getModel().getNodesDict();
688+
689+
_.forIn(tableNodesDict, (node, uid)=>{
690+
node.getData().foreign_key?.forEach((theFk)=>{
691+
const theFkColumn = theFk.columns[0];
692+
let referencesUid = oidUidMap[theFkColumn.references_oid];
693+
694+
/* Incomplete reference to missing table */
695+
if (!referencesUid) {
696+
return;
697+
}
698+
699+
/* Avoid creating duplicate links */
700+
if (
701+
newNodesUids
702+
&& !newNodesUids.includes(uid)
703+
&& !newNodesUids.includes(referencesUid)
704+
) {
705+
return;
706+
}
707+
708+
const newData = {
709+
local_table_uid: uid,
710+
local_column_attnum: _.find(
711+
tableNodesDict[uid].getColumns(),
712+
(col) => col.name == theFkColumn.local_column
713+
).attnum,
714+
referenced_table_uid: referencesUid,
715+
referenced_column_attnum: _.find(
716+
tableNodesDict[referencesUid].getColumns(),
717+
(col) => col.name == theFkColumn.referenced
718+
).attnum,
719+
};
720+
721+
theFkColumn.references = referencesUid;
722+
this.addLink(newData, 'onetomany');
723+
});
675724
});
676725
}
677726

web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -596,21 +596,26 @@ export default class ERDTool extends React.Component {
596596
if(nodeDropData.objUrl.indexOf(matchUrl) == -1) {
597597
pgAdmin.Browser.notifier.error(gettext('Cannot drop table from outside of the current database.'));
598598
} else {
599-
let dataPromise = new Promise((resolve, reject)=>{
600-
this.apiObj.get(nodeDropData.objUrl)
601-
.then((res)=>{
602-
resolve(this.diagram.cloneTableData(TableSchema.getErdSupportedData(res.data)));
603-
})
604-
.catch((err)=>{
605-
console.error(err);
606-
reject(err instanceof Error ? err : Error(gettext('Something went wrong')));
607-
});
608-
});
609-
const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e);
610-
this.diagram.addNode(dataPromise, [x, y], {
611-
fillColor: this.state.fill_color,
612-
textColor: this.state.text_color,
613-
}).setSelected(true);
599+
this.apiObj.get(nodeDropData.objUrl)
600+
.then((res)=>{
601+
const data = TableSchema.getErdSupportedData(res.data);
602+
const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e);
603+
const position = [x,y];
604+
const metadata = {
605+
fillColor: this.state.fill_color,
606+
textColor: this.state.text_color,
607+
};
608+
609+
const newNode = this.state.preferences.insert_table_with_relations
610+
? this.diagram.addNodeWithLinks(data, position, metadata)
611+
: this.diagram.addNode(this.diagram.cloneTableData(data), position, metadata);
612+
613+
newNode.setSelected(true);
614+
})
615+
.catch((err)=>{
616+
console.error(err);
617+
throw (err instanceof Error ? err : Error(gettext('Something went wrong')));
618+
});
614619
}
615620
}
616621
}

web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,10 @@ export class TableNodeModel extends DefaultNodeModel {
159159
}
160160

161161
serializeData() {
162-
return this.getData();
162+
const data = this.getData();
163+
// Remove incomplete foreign keys
164+
data.foreign_key = data.foreign_key?.filter((theFk) => theFk.columns?.[0].references);
165+
return data;
163166
}
164167

165168
serialize() {

0 commit comments

Comments
 (0)