Skip to content

fix(cli): add missing opposite relation fields during db pull when multiple FKs target the same model#2652

Open
svetch wants to merge 1 commit intozenstackhq:devfrom
svetch:dev
Open

fix(cli): add missing opposite relation fields during db pull when multiple FKs target the same model#2652
svetch wants to merge 1 commit intozenstackhq:devfrom
svetch:dev

Conversation

@svetch
Copy link
Copy Markdown
Contributor

@svetch svetch commented May 6, 2026

Back-reference relation fields (the opposite side of a relation, with @relation but no fields arg) were silently skipped during the merge phase of db pull when no matching field existed in the original schema. This caused models like Users that are referenced by many tables (e.g., via user_created/user_updated FKs) to be missing their back-reference fields after pulling.

The fix adds relation-name-based matching as a new step in the field matching algorithm, and removes the blanket early-skip that discarded all unmatched back-references. Named back-references that don't match any existing field are now correctly added as new fields.

Summary by CodeRabbit

  • Bug Fixes

    • Improved database schema synchronization to better handle relations and foreign keys when syncing models.
    • Enhanced back-reference relation field preservation during schema restoration with multiple foreign key relationships.
  • Tests

    • Added test coverage for opposite relation field restoration and preservation scenarios.

…ltiple FKs target the same model

Back-reference relation fields (the opposite side of a relation, with @relation but no `fields` arg) were silently skipped during the merge phase of `db pull` when no matching field existed in the original schema. This caused models like `Users` that are referenced by many tables (e.g., via `user_created`/`user_updated` FKs) to be missing their back-reference fields after pulling.

The fix adds relation-name-based matching as a new step in the field matching algorithm, and removes the blanket early-skip that discarded all unmatched back-references. Named back-references that don't match any existing field are now correctly added as new fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 6, 2026 15:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

The PR modifies relation name handling in the database pull functionality. It replaces getRelationFkName with getRelationName to extract relation names from the @relation attribute, enhances field matching logic to prioritize relation names during model syncing, and adds tests verifying opposite relation field restoration and preservation.

Changes

Relation Name Handling Enhancement

Layer / File(s) Summary
Utility Layer
packages/cli/src/actions/pull/utils.ts
getRelationFkName replaced with getRelationName to extract the first positional string literal argument from @relation attributes instead of the map value.
Core Logic
packages/cli/src/actions/db.ts
Field matching sequence expanded to prioritize exact DB name, relation fields key, relation FK name, then relation name via new getRelationName, then type reference. Deletion/cleanup logic extended to consider relation names when reconciling added/updated/deleted fields and back-reference relations.
Tests
packages/cli/test/db/pull.test.ts
Two new tests verify opposite relation fields are correctly restored and preserved when multiple models reference the same target through foreign keys using createdBy/updatedBy relations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Relations now speak their true names clear,
No more FK whispers—let the bonds appear!
From Post to User, Comment too,
Back-references sing, old and new.
Test by test, the fields align,
A schema dance—oh how divine!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 PR title accurately describes the main change: fixing a bug where opposite relation fields were missing during db pull when multiple foreign keys target the same model.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes db pull merge behavior so that named opposite-side relation fields (back-references) are no longer dropped when multiple foreign keys point to the same target model, ensuring schemas are fully restored/preserved in these multi-FK scenarios.

Changes:

  • Added extraction of @relation positional relation name (getRelationName) to enable reliable matching of back-reference fields.
  • Updated the field matching priority during db pull merge to include relation-name-based matching and removed the blanket skip for unmatched back-references.
  • Added regression tests covering restore/preserve behavior when multiple models reference the same target model via multiple FKs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/cli/test/db/pull.test.ts Adds regression tests to ensure opposite relation fields are restored/preserved when multiple FKs target the same model.
packages/cli/src/actions/pull/utils.ts Introduces getRelationName helper to read the first positional argument of @relation.
packages/cli/src/actions/db.ts Extends merge matching logic to use relation-name matching and allows adding previously skipped named back-references.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@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 (2)
packages/cli/test/db/pull.test.ts (1)

195-230: 💤 Low value

Test placement: "preserve" test sits under "Pull from zero" describe.

This test does not zero out the schema between push and pull, so semantically it belongs to the "Pull with existing schema - preserve schema features" describe block (Line 330) rather than "Pull from zero - restore complete schema from database" (Line 11). The first new test (Lines 155-193) is correctly placed under "Pull from zero". Reorganizing improves discoverability and aligns with the existing pattern (e.g., the analogous "preserve" test for self-referencing models at Lines 132-153 has the same minor placement issue, so this is consistent — but worth fixing for both).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/test/db/pull.test.ts` around lines 195 - 230, Move the "should
preserve opposite relation fields when multiple models have FKs to the same
target" test out of the "Pull from zero" describe and into the "Pull with
existing schema - preserve schema features" describe block (i.e., relocate the
entire it(...) block so it runs alongside the other "preserve" tests); ensure
you also relocate the analogous self-referencing "preserve" test if present so
both preservation tests live under the same describe, keeping the test name and
behavior unchanged (look for the it(...) with that exact description and the
matching test that checks self-referencing models).
packages/cli/src/actions/pull/utils.ts (1)

131-137: 💤 Low value

Minor: redundant as StringLiteral cast.

The check on Line 135 already narrows firstPositionalArg.value to StringLiteral, so the cast on Line 136 is unnecessary. Not a functional issue.

Optional cleanup
 export function getRelationName(decl: DataField): string | undefined {
     const relationAttr = decl?.attributes?.find((a) => a.decl?.ref?.name === '@relation');
     if (!relationAttr) return undefined;
     const firstPositionalArg = relationAttr.args.find((a) => !a.name);
     if (!firstPositionalArg || firstPositionalArg.value?.$type !== 'StringLiteral') return undefined;
-    return (firstPositionalArg.value as StringLiteral).value;
+    return firstPositionalArg.value.value;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/actions/pull/utils.ts` around lines 131 - 137, In
getRelationName, remove the redundant type cast "as StringLiteral" on the return
line: after you already checked firstPositionalArg.value?.$type ===
'StringLiteral', directly access and return firstPositionalArg.value.value (or
assign firstPositionalArg.value to a const typed variable for clarity) so the
explicit cast is unnecessary; update the return accordingly in the
getRelationName function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/cli/src/actions/pull/utils.ts`:
- Around line 131-137: In getRelationName, remove the redundant type cast "as
StringLiteral" on the return line: after you already checked
firstPositionalArg.value?.$type === 'StringLiteral', directly access and return
firstPositionalArg.value.value (or assign firstPositionalArg.value to a const
typed variable for clarity) so the explicit cast is unnecessary; update the
return accordingly in the getRelationName function.

In `@packages/cli/test/db/pull.test.ts`:
- Around line 195-230: Move the "should preserve opposite relation fields when
multiple models have FKs to the same target" test out of the "Pull from zero"
describe and into the "Pull with existing schema - preserve schema features"
describe block (i.e., relocate the entire it(...) block so it runs alongside the
other "preserve" tests); ensure you also relocate the analogous self-referencing
"preserve" test if present so both preservation tests live under the same
describe, keeping the test name and behavior unchanged (look for the it(...)
with that exact description and the matching test that checks self-referencing
models).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dbfb51ad-0e00-4e56-b68a-78e5be718f3b

📥 Commits

Reviewing files that changed from the base of the PR and between f0fa5ea and 0259b70.

📒 Files selected for processing (3)
  • packages/cli/src/actions/db.ts
  • packages/cli/src/actions/pull/utils.ts
  • packages/cli/test/db/pull.test.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants