Skip to content

Conversation

@Lancear
Copy link

@Lancear Lancear commented Feb 1, 2026

Feature: Drag and Drop Tables with Relations in ERD

This feature allows tables to be dragged into the ERD tool along with their foreign key relationships automatically.

Closes #5578
Closes #8198

Key Decisions

  1. Keep original oid and foreign_key in nodes
    Preserves table identity and allows checking future references. Only the first node with each oid retains the oid.

  2. Added references_oid
    New field to track which tables are referenced by foreign keys. Foreign keys without the original references field point to missing tables.

  3. First instance priority
    Only the first insert of a table is checked for incoming references from other nodes, preventing duplicate foreign key links.

  4. SQL generation cleanup
    Foreign keys pointing to missing tables are deleted from the ER diagram during SQL generation to keep output valid.

  5. File save preserves all foreign keys
    Missing table references are kept when saving, allowing users to add the referenced tables later.

  6. User preference added
    New preference "Drag and Drop table with relations" to control this behavior, default acts like before.

Co-authored-by: Christian P. pirnichristian@gmail.com

Summary by CodeRabbit

  • New Features

    • Added a preference to automatically insert related tables when dragging a table into the ERD diagram.
  • Bug Fixes

    • Improved handling of incomplete foreign key references when adding tables to the diagram.
    • Enhanced relationship linking logic for better accuracy in diagram generation.

✏️ Tip: You can customize this high-level summary in your review settings.

Lancear and others added 2 commits February 1, 2026 13:00
Co-authored-by: Christian P. <pirnichristian@gmail.com>
Co-authored-by: Christian P. <pirnichristian@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

Walkthrough

This PR enhances the ERD tool to properly display foreign key relationships when tables are individually added to diagrams. It introduces a user preference to optionally include related tables, refactors node linking logic into dedicated methods, and improves foreign key reference tracking using object identifiers (OIDs) to handle incomplete schema references.

Changes

Cohort / File(s) Summary
ERD Core Infrastructure
web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js, web/pgadmin/tools/erd/__init__.py
Refactored deserialization flow with two-phase node and link creation. Added public methods addNodeWithLinks() and addLinksBetweenNodes() to centralize link wiring. Introduced new user preference insert_table_with_relations (boolean, defaults to False) to control whether related tables are included when dragging tables into the diagram.
Foreign Key & Data Handling
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js, web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
Enhanced foreign key data extraction: added 'oid' to supported keys, introduced references_oid copying for incomplete references, and initialized autoindex logic. Modified serializeData() to filter out incomplete foreign keys by checking column references.
ERD UI Integration
web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx
Refactored onDropNode() method to use the new preference flag, conditionally choosing between addNodeWithLinks() (when insert_table_with_relations is enabled) and standard addNode(). Simplified control flow by removing Promise wrapping for immediate node creation post-API call.

Sequence Diagram

sequenceDiagram
    participant User
    participant ERDTool as ERDTool.jsx
    participant API
    participant ERDCore
    participant DB as Database
    
    User->>ERDTool: Drag table from tree to diagram
    ERDTool->>API: Fetch table schema data
    API->>DB: Query table metadata
    DB-->>API: Return table/FK data
    API-->>ERDTool: Provide table data
    
    alt insert_table_with_relations = true
        ERDTool->>ERDCore: addNodeWithLinks(data, position, metadata)
        ERDCore->>ERDCore: Create node from data
        ERDCore->>ERDCore: Build oid→uid map
        ERDCore->>ERDCore: addLinksBetweenNodes(oidUidMap)
        ERDCore->>ERDCore: Resolve FK references & create links
        ERDCore-->>ERDTool: Node + Related nodes with FK links
    else insert_table_with_relations = false
        ERDTool->>ERDCore: addNode(cloned data, position, metadata)
        ERDCore->>ERDCore: Create single node only
        ERDCore-->>ERDTool: Single node without related tables
    end
    
    ERDTool->>User: Display diagram with table(s) and FK relationships
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: enabling insertion of tables with their foreign-key relationships via drag-and-drop in the ERD tool.
Linked Issues check ✅ Passed The changes implement the core requirements from both linked issues: showing FK links when related tables are dragged (#5578) and enabling user-controlled selection of tables with their relations (#8198).
Out of Scope Changes check ✅ Passed All changes are directly related to the PR objectives of enabling drag-and-drop table insertion with foreign-key relationships and adding user preference controls.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx`:
- Around line 599-618: The catch block for the API call in the apiObj.get(...)
chain currently only logs and rethrows, causing silent failures; replace the
console.error/throw in that catch with a call to the component's existing error
handler (e.g. this.handleAxiosCatch(err) or the notifier used elsewhere) to
surface the failure to users and remove the rethrow so the promise is handled;
update the catch for apiObj.get(...) where TableSchema.getErdSupportedData and
addNode/addNodeWithLinks are invoked to call handleAxiosCatch(err) instead of
console.error/throw.

In `@web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx`:
- Around line 161-165: serializeData() is mutating the node's live data by
filtering data.foreign_key in-place which can permanently remove FKs; fix by
cloning the data (or at least the foreign_key array) returned from getData()
before applying the filter so the node's persisted state remains unchanged —
e.g., obtain const data = this.getData(); create a shallow copy (or copy of
data.foreign_key) and run the .filter on that copy, then return the
cloned/modified copy while leaving the original getData() object intact.
🧹 Nitpick comments (1)
web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js (1)

686-724: Guard against missing FK column metadata during link creation.

addLinksBetweenNodes assumes fk.columns[0] and column lookups always resolve. A malformed FK or a column removed in the ERD could throw here and break drag‑and‑drop. A small guard keeps this path resilient.

🛡️ Suggested defensive checks
-        const theFkColumn = theFk.columns[0];
+        const theFkColumn = theFk.columns?.[0];
+        if (!theFkColumn) return;
         let referencesUid = oidUidMap[theFkColumn.references_oid];
@@
-        const newData = {
-          local_table_uid: uid,
-          local_column_attnum: _.find(
-            tableNodesDict[uid].getColumns(), 
-            (col) => col.name == theFkColumn.local_column
-          ).attnum,
-          referenced_table_uid: referencesUid,
-          referenced_column_attnum: _.find(
-            tableNodesDict[referencesUid].getColumns(), 
-            (col) => col.name == theFkColumn.referenced
-          ).attnum,
-        };
+        const localCol = _.find(
+          tableNodesDict[uid].getColumns(), 
+          (col) => col.name == theFkColumn.local_column
+        );
+        const refCol = _.find(
+          tableNodesDict[referencesUid].getColumns(), 
+          (col) => col.name == theFkColumn.referenced
+        );
+        if (!localCol || !refCol) return;
+        const newData = {
+          local_table_uid: uid,
+          local_column_attnum: localCol.attnum,
+          referenced_table_uid: referencesUid,
+          referenced_column_attnum: refCol.attnum,
+        };

Comment on lines +599 to +618
this.apiObj.get(nodeDropData.objUrl)
.then((res)=>{
const data = TableSchema.getErdSupportedData(res.data);
const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e);
const position = [x,y];
const metadata = {
fillColor: this.state.fill_color,
textColor: this.state.text_color,
};

const newNode = this.state.preferences.insert_table_with_relations
? this.diagram.addNodeWithLinks(data, position, metadata)
: this.diagram.addNode(this.diagram.cloneTableData(data), position, metadata);

newNode.setSelected(true);
})
.catch((err)=>{
console.error(err);
throw (err instanceof Error ? err : Error(gettext('Something went wrong')));
});
Copy link

@coderabbitai coderabbitai bot Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Surface drag‑and‑drop API failures to users.

The current catch logs and rethrows, but nothing handles the rejection, so drops can fail silently. Consider using the existing handleAxiosCatch (or notifier) and avoid rethrowing.

💡 Suggested fix
           .catch((err)=>{
             console.error(err);
-            throw (err instanceof Error ? err : Error(gettext('Something went wrong')));
+            this.handleAxiosCatch(err);
           });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.apiObj.get(nodeDropData.objUrl)
.then((res)=>{
const data = TableSchema.getErdSupportedData(res.data);
const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e);
const position = [x,y];
const metadata = {
fillColor: this.state.fill_color,
textColor: this.state.text_color,
};
const newNode = this.state.preferences.insert_table_with_relations
? this.diagram.addNodeWithLinks(data, position, metadata)
: this.diagram.addNode(this.diagram.cloneTableData(data), position, metadata);
newNode.setSelected(true);
})
.catch((err)=>{
console.error(err);
throw (err instanceof Error ? err : Error(gettext('Something went wrong')));
});
this.apiObj.get(nodeDropData.objUrl)
.then((res)=>{
const data = TableSchema.getErdSupportedData(res.data);
const {x, y} = this.diagram.getEngine().getRelativeMousePoint(e);
const position = [x,y];
const metadata = {
fillColor: this.state.fill_color,
textColor: this.state.text_color,
};
const newNode = this.state.preferences.insert_table_with_relations
? this.diagram.addNodeWithLinks(data, position, metadata)
: this.diagram.addNode(this.diagram.cloneTableData(data), position, metadata);
newNode.setSelected(true);
})
.catch((err)=>{
console.error(err);
this.handleAxiosCatch(err);
});
🤖 Prompt for AI Agents
In `@web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx` around lines
599 - 618, The catch block for the API call in the apiObj.get(...) chain
currently only logs and rethrows, causing silent failures; replace the
console.error/throw in that catch with a call to the component's existing error
handler (e.g. this.handleAxiosCatch(err) or the notifier used elsewhere) to
surface the failure to users and remove the rethrow so the promise is handled;
update the catch for apiObj.get(...) where TableSchema.getErdSupportedData and
addNode/addNodeWithLinks are invoked to call handleAxiosCatch(err) instead of
console.error/throw.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unchanged

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lancear, understood. I've noted your decision to keep the error handling as-is.

(´・ω・`)

@Lancear
Copy link
Author

Lancear commented Feb 1, 2026

We were unsure how to update the translations, thus skipped that for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant