Skip to content

ERD Tool: Insert table with relations via drag-and-drop#9581

Merged
akshay-joshi merged 3 commits intopgadmin-org:masterfrom
Lancear:erd-insert-table-with-relations
Feb 24, 2026
Merged

ERD Tool: Insert table with relations via drag-and-drop#9581
akshay-joshi merged 3 commits intopgadmin-org:masterfrom
Lancear:erd-insert-table-with-relations

Conversation

@Lancear
Copy link
Copy Markdown
Contributor

@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 a copy of foreign_key in nodes
    Preserves table identity and allows checking future references. Only the first node with each oid retains the oid.

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

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

  4. 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

    • New preference to optionally insert related tables when dragging a table into the ERD diagram.
    • Drag-and-drop now supports adding tables with their relationships when enabled.
  • Bug Fixes

    • Better handling of incomplete or missing foreign-key references when adding tables.
    • Improved relationship-linking logic for more accurate diagram generation.
  • Tests

    • Updated tests to align with the ERD data shape used during deserialization.

Co-authored-by: Christian P. <pirnichristian@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 1, 2026

Walkthrough

Adds optional insertion of related tables when dragging a table into the ERD, extends table ERD data with oid and original_foreign_keys, and centralizes FK link creation via new ERDCore methods and a two-phase deserialization flow.

Changes

Cohort / File(s) Summary
Table Schema Data Extraction
web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
Added oid to ERD-supported keys; extract original_foreign_keys from foreign_key with autoindex/coveringindex handling; clear foreign_key after extraction.
ERD Tool Configuration
web/pgadmin/tools/erd/__init__.py
Added boolean user preference insert_table_with_relations (default False) to control dragging behavior for related tables.
ERDCore Link Management
web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
Implemented two-phase deserialization: collect nodes, build oid→uid map, prune missing refs, and create links centrally. Added addNodeWithLinks(nodeData, position, metadata) and addLinksBetweenNodes(oidUidMap, newNodesUids); updated cloneTableData to skip oid and original_foreign_keys.
ERDTool Drag-Drop Handler
web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx
Replaced Promise-wrapper flow with direct API call; transform API data via TableSchema.getErdSupportedData; conditionally call diagram.addNodeWithLinks(...) when insert_table_with_relations is enabled, otherwise diagram.addNode(...).
Tests
web/regression/javascript/erd/erd_core_spec.js
Updated test data to use TableSchema.getErdSupportedData(table) so deserialization uses the new ERD data shape.

Sequence Diagram

sequenceDiagram
    participant User
    participant ERDTool as ERDTool.jsx
    participant API as Backend API
    participant ERDCore
    participant Canvas

    User->>ERDTool: Drag & drop table
    ERDTool->>API: Fetch table ERD data
    API-->>ERDTool: Return table data (includes oid + original_foreign_keys)

    alt insert_table_with_relations enabled
        ERDTool->>ERDCore: addNodeWithLinks(data, position, metadata)
        ERDCore->>ERDCore: Create node(s) and collect oid→uid map
        ERDCore->>ERDCore: Prune missing refs & resolve FKs
        ERDCore->>ERDCore: addLinksBetweenNodes(oidUidMap, newNodesUids)
        ERDCore->>Canvas: Render nodes and FK links
    else
        ERDTool->>ERDCore: addNode(cloneTableData(data), position, metadata)
        ERDCore->>Canvas: Render node only
    end

    ERDTool->>Canvas: Select created node
    Canvas-->>User: Display diagram
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • yogeshmahajan-1903
🚥 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 summarizes the main change: inserting tables with their relations via drag-and-drop in the ERD tool.
Linked Issues check ✅ Passed The PR implements automatic FK relationship display for drag-dropped tables and introduces a user preference to control the behavior, addressing both issue objectives.
Out of Scope Changes check ✅ Passed All changes are scoped to ERD drag-drop functionality and related table insertion; no unrelated modifications detected.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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
Copy Markdown

@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 thread web/pgadmin/tools/erd/static/js/erd_tool/components/ERDTool.jsx
Comment thread web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx Outdated
@Lancear
Copy link
Copy Markdown
Contributor Author

Lancear commented Feb 1, 2026

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

@pravesh-sharma
Copy link
Copy Markdown
Contributor

Hi @Lancear,

I tested the PR and identified the following issues:

  1. If a table contains a Foreign Key (FK) referencing a missing table, generating the SQL and returning to the ERD does not resolve the link. Even after adding the missing table back to the ERD, the relationship is not automatically generated.
  2. When a table is cloned using the "Clone Table" toolbar button, dragging and dropping a third table that has an FK referencing the original table incorrectly draws the relationship to the cloned table instead (attached screenshot).
Screenshot 2026-02-09 at 1 26 21 PM

@Lancear
Copy link
Copy Markdown
Contributor Author

Lancear commented Feb 10, 2026

1 was intentional so the ERD diagram matches the generated SQL and has no additional foreign keys.
2 should be easy to fix, will check.

I think we can change 1 by separating the original foreign keys from the schema, from the ones drawn in the ER diagram, will check it out.

Will do both of these on Thursday when I have more time in the evening.

@Lancear Lancear force-pushed the erd-insert-table-with-relations branch 2 times, most recently from ccd894a to 2cace1c Compare February 12, 2026 18:52
@Lancear
Copy link
Copy Markdown
Contributor Author

Lancear commented Feb 12, 2026

Addressed both issues

Copy link
Copy Markdown

@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: 3

🤖 Fix all issues with AI agents
In `@web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js`:
- Around line 702-708: The duplicate-link guard in addNodeWithLinks only checks
newNodesUids and misses existing visual links, so update the check (around the
block using newNodesUids, uid and referencesUid) to also detect and skip if a
link between uid and referencesUid already exists before calling addLink;
specifically, query the current links collection (e.g., this.links /
graph.getLinks() or the structure that stores links) and if any link has source
=== uid and target === referencesUid (or vice‑versa if links are undirected),
return/skip adding; ensure the check uses the same identifier fields that
addLink expects so duplicates are never created.
- Around line 692-726: The loop that processes nodeData.original_foreign_keys
must guard against missing pieces: ensure theFk.columns && theFk.columns[0]
exists, initialize nodeData.foreign_key if falsy, and check the _.find(...)
results before accessing .attnum; if either the local column lookup or the
referenced column lookup returns undefined (or referencesUid is missing) then
skip this FK (do not push to nodeData.foreign_key or call this.addLink).
Concretely, in the block handling theFk use a guard like: if (!theFk.columns ||
!theFk.columns[0]) return; set nodeData.foreign_key = nodeData.foreign_key ||
[]; resolve localCol = _.find(tableNodesDict[uid].getColumns(), ...) and refCol
= _.find(tableNodesDict[referencesUid].getColumns(), ...); if (!localCol ||
!refCol) return; then build newData using localCol.attnum and refCol.attnum,
clone theFk, set newForeignKey.columns[0].references = referencesUid, push to
nodeData.foreign_key and call this.addLink.
- Around line 660-666: The function addNodeWithLinks currently calls delete
nodeData.oid which mutates the caller's object; instead make a shallow clone or
destructure at the start (e.g., const node = { ...nodeData } or const { oid,
...node } = nodeData) and operate on that local copy (delete node.oid or use the
destructured node) so you never modify the original nodeData; update all
subsequent references in addNodeWithLinks to use the local cloned variable
(node) and leave nodeData untouched.

Comment thread web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
Comment thread web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
Comment thread web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
@pravesh-sharma
Copy link
Copy Markdown
Contributor

@Lancear, Please fix the javascript test case failure.

@Lancear Lancear force-pushed the erd-insert-table-with-relations branch from 9a1426e to 2cace1c Compare February 20, 2026 14:03
Lancear and others added 2 commits February 20, 2026 15:31
Co-authored-by: Christian P. <pirnichristian@gmail.com>
Co-authored-by: Christian P. <pirnichristian@gmail.com>
@Lancear Lancear force-pushed the erd-insert-table-with-relations branch from 2cace1c to 08699f1 Compare February 20, 2026 14:33
@Lancear
Copy link
Copy Markdown
Contributor Author

Lancear commented Feb 20, 2026

Fixed

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
web/regression/javascript/erd/erd_core_spec.js (1)

234-273: Consider adding dedicated tests for the new addNodeWithLinks and addLinksBetweenNodes public APIs.

The deserializeData test now exercises addLinksBetweenNodes indirectly, but addNodeWithLinks has no coverage at all. Given these are the key entry points for the new "insert with relations" feature, direct tests would guard against regressions and clarify the expected FK-deduplication, oid-existence, and cross-schema-pruning behaviors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/regression/javascript/erd/erd_core_spec.js` around lines 234 - 273, Add
explicit unit tests for the new public APIs addNodeWithLinks and
addLinksBetweenNodes: create focused Jest specs that call
erdCoreObj.addNodeWithLinks and erdCoreObj.addLinksBetweenNodes directly
(instead of only via deserializeData), mock dependencies the same way as the
existing test (erdCoreObj.getNewPort, getNewLink, addNode, addLink,
erdEngine.getModel().getNodesDict, etc.), and assert the expected behaviors —
that addNodeWithLinks deduplicates foreign keys, respects existing OIDs (doesn't
recreate nodes for existing ids), and prunes cross-schema relationships, and
that addLinksBetweenNodes creates the correct number of links and sets
source/target ports appropriately; use spies to verify call counts and that
ports/links are created with the intended names/ids to guard these specific
corner cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js`:
- Around line 692-725: In addLinksBetweenNodes, guard against three unguarded
dereferences: ensure theFk.columns is an array and has an element before
accessing theFk.columns[0]; when computing local_column_attnum and
referenced_column_attnum, check the result of _.find(...) is not undefined
before accessing .attnum and skip this FK if either column is missing; and
ensure nodeData.foreign_key is initialized to an array (e.g., set
nodeData.foreign_key = nodeData.foreign_key || []) before pushing newForeignKey;
in each early-skip case return or continue the loop so invalid/missing data does
not throw and prevents other links from being created.

---

Nitpick comments:
In `@web/regression/javascript/erd/erd_core_spec.js`:
- Around line 234-273: Add explicit unit tests for the new public APIs
addNodeWithLinks and addLinksBetweenNodes: create focused Jest specs that call
erdCoreObj.addNodeWithLinks and erdCoreObj.addLinksBetweenNodes directly
(instead of only via deserializeData), mock dependencies the same way as the
existing test (erdCoreObj.getNewPort, getNewLink, addNode, addLink,
erdEngine.getModel().getNodesDict, etc.), and assert the expected behaviors —
that addNodeWithLinks deduplicates foreign keys, respects existing OIDs (doesn't
recreate nodes for existing ids), and prunes cross-schema relationships, and
that addLinksBetweenNodes creates the correct number of links and sets
source/target ports appropriately; use spies to verify call counts and that
ports/links are created with the intended names/ids to guard these specific
corner cases.

@akshay-joshi akshay-joshi merged commit 6d0d387 into pgadmin-org:master Feb 24, 2026
13 of 37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants