Skip to content

fix: handle interleave constraints as foreign key to visualize relations for Cloud Spanner#4076

Open
KaoruMuta wants to merge 2 commits intoliam-hq:mainfrom
KaoruMuta:fix/#2411-constraint-relationship
Open

fix: handle interleave constraints as foreign key to visualize relations for Cloud Spanner#4076
KaoruMuta wants to merge 2 commits intoliam-hq:mainfrom
KaoruMuta:fix/#2411-constraint-relationship

Conversation

@KaoruMuta
Copy link
Copy Markdown

@KaoruMuta KaoruMuta commented Apr 7, 2026

Issue

Why is this change needed?

I created #2548 to fix this issue, but this change affects common schema not only tbls but prisma..etc.
I strongly agree with solution no.3 (#2411 (comment)) while we don't fully support cloud spanner in the perspective of maintaining liam.

This PR implements solution no.3 by handling tbls interleave constraint as foreign key.

Evidence

I put schema.json by tbls which reflects cloud spanner schema information and check the behavior after rendering.

image
Open with Devin

Summary by CodeRabbit

  • New Features

    • Schema parser now recognizes and processes INTERLEAVE constraints as foreign key relationships, with proper handling of delete and update actions.
  • Tests

    • Added test coverage for INTERLEAVE constraint parsing to verify correct conversion to foreign key format.

@KaoruMuta KaoruMuta requested a review from a team as a code owner April 7, 2026 14:44
@KaoruMuta KaoruMuta requested review from NoritakaIkeda, Copilot and sasamuku and removed request for a team April 7, 2026 14:44
@giselles-ai
Copy link
Copy Markdown

giselles-ai bot commented Apr 7, 2026

Finished running flow.

Step 1
🟢
On Pull Request OpenedStatus: Success Updated: Apr 7, 2026 2:44pm
Step 2
🟢
gpt-5Status: Success Updated: Apr 7, 2026 2:44pm
Step 3
🟢
Create Pull Request CommentStatus: Success Updated: Apr 7, 2026 2:44pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 81e8d37c-4522-47a2-b3a2-6aec4ad0b5f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9990e2d and 1286175.

📒 Files selected for processing (2)
  • frontend/packages/schema/src/parser/tbls/index.test.ts
  • frontend/packages/schema/src/parser/tbls/parser.ts

📝 Walkthrough

Walkthrough

Added support for parsing Cloud Spanner INTERLEAVE constraints in the schema parser. The implementation converts INTERLEAVE constraints into FOREIGN KEY constraint objects, enabling proper schema relationship parsing. Includes test coverage for the new constraint type handling.

Changes

Cohort / File(s) Summary
INTERLEAVE Constraint Parsing
frontend/packages/schema/src/parser/tbls/parser.ts
Introduced processInterleaveConstraint helper function to recognize and convert INTERLEAVE constraints into FOREIGN KEY objects, extracting action constraints from the original definition. Updated processConstraints control flow to dispatch INTERLEAVE type constraints to the new processor.
INTERLEAVE Constraint Test
frontend/packages/schema/src/parser/tbls/index.test.ts
Added new test case validating that INTERLEAVE constraints on child tables are parsed and converted to FOREIGN KEY constraints with correct column mappings, target table references, and delete/update actions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

bug, Review effort 3/5

Suggested reviewers

  • NoritakaIkeda
  • hoshinotsuyoshi

Poem

🐰 A spanner that interleaves its ways,
Now parsed through constraint's new phase,
Where parent-child bonds find their place,
In foreign keys' welcoming embrace,
Relations bloom in the ERD's grace! 🌱

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: implementing handling of INTERLEAVE constraints as foreign keys for Cloud Spanner visualization.
Description check ✅ Passed The PR description follows the template with Issue section (resolve: #2411) and explains why the change is needed with detailed context and evidence.
Linked Issues check ✅ Passed The PR addresses issue #2411 by implementing solution no.3 to treat tbls INTERLEAVE constraints as foreign keys, enabling Cloud Spanner table relations visualization.
Out of Scope Changes check ✅ Passed All changes are scoped to tbls parser logic: a new INTERLEAVE constraint handler and corresponding test case, directly addressing the linked issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

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

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.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 7, 2026

@KaoruMuta is attempting to deploy a commit to the Liam Team on Vercel.

A member of the Team first needs to authorize it.

@giselles-ai
Copy link
Copy Markdown

giselles-ai bot commented Apr 7, 2026

Check changeset necessity

Status: REQUIRED

Reason:

  • Affects target package @liam-hq/schema (listed in the Changeset Guide as requiring changesets).
  • User-facing behavior change: the tbls parser now treats Cloud Spanner INTERLEAVE constraints as foreign keys, altering parsed output and enabling relation visualization.
  • Resolves a user-visible bug (issue [Cloud Spanner] Table relations are not visualized in generated ERD by @liam-hq/cli (>= 0.5.9). #2411) where relations were not shown for Spanner schemas.
  • Scope is a bug fix with no API surface changes; appropriate for a patch release.

Changeset (copy & paste):

---
"@liam-hq/schema": patch
---
- 🐛 Treat Cloud Spanner INTERLEAVE constraints (from tbls) as foreign keys to correctly visualize parent–child relations
  - Normalize INTERLEAVE to a FOREIGN KEY-shaped constraint in the tbls parser, preserving delete/update actions for relation generation

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

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

This PR fixes missing ERD relations for Cloud Spanner tbls inputs by treating INTERLEAVE constraints as a foreign-key-shaped constraint so the existing relationship generation can visualize parent/child table relations.

Changes:

  • Add INTERLEAVE constraint handling in the tbls parser by normalizing it into a FOREIGN KEY constraint shape.
  • Add a unit test covering INTERLEAVE constraint parsing and action extraction (ON DELETE CASCADE).

Reviewed changes

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

File Description
frontend/packages/schema/src/parser/tbls/parser.ts Normalizes INTERLEAVE constraints into FOREIGN KEY-shaped constraints so relationships can be derived.
frontend/packages/schema/src/parser/tbls/index.test.ts Adds coverage asserting how INTERLEAVE constraints are converted into internal constraint objects.

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

Comment on lines +238 to +260
if (
constraint.type === 'INTERLEAVE' &&
constraint.columns &&
constraint.columns.length > 0 &&
constraint.referenced_columns &&
constraint.referenced_columns.length > 0 &&
constraint.referenced_table
) {
const { updateConstraint, deleteConstraint } = extractForeignKeyActions(
constraint.def,
)

return [
constraint.name,
{
type: 'FOREIGN KEY',
name: constraint.name,
columnNames: constraint.columns,
targetTableName: constraint.referenced_table,
targetColumnNames: constraint.referenced_columns,
updateConstraint,
deleteConstraint,
},
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

INTERLEAVE constraints can list the child table’s full primary key columns (e.g. [parent_id, child_id]) while referenced_columns only contains the parent key columns. Normalizing this directly into a FOREIGN KEY with mismatched columnNames/targetColumnNames can (1) produce invalid FK definitions for any downstream consumer (e.g. deparsers) and (2) skew relationship cardinality calculation because determineCardinalityForForeignKey uses columnNames as-is. Consider mapping only the parent-key portion, e.g. columnNames: constraint.columns.slice(0, constraint.referenced_columns.length) (and guard that the slice length is > 0).

Copilot uses AI. Check for mistakes.
Comment on lines +654 to +663
const expected = {
albums_interleave: {
type: 'FOREIGN KEY',
name: 'albums_interleave',
columnNames: ['singer_id', 'album_id'],
targetTableName: 'singers',
targetColumnNames: ['singer_id'],
updateConstraint: 'NO_ACTION',
deleteConstraint: 'CASCADE',
},
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The expected normalized FOREIGN KEY currently asserts columnNames: ['singer_id', 'album_id'] while targetColumnNames only has ['singer_id']. If processInterleaveConstraint is updated to slice the local columns to the referenced column count (to avoid mismatches / cardinality issues), update this expectation accordingly (likely columnNames: ['singer_id']).

Copilot uses AI. Check for mistakes.
Comment on lines +239 to +264
constraint.type === 'INTERLEAVE' &&
constraint.columns &&
constraint.columns.length > 0 &&
constraint.referenced_columns &&
constraint.referenced_columns.length > 0 &&
constraint.referenced_table
) {
const { updateConstraint, deleteConstraint } = extractForeignKeyActions(
constraint.def,
)

return [
constraint.name,
{
type: 'FOREIGN KEY',
name: constraint.name,
columnNames: constraint.columns,
targetTableName: constraint.referenced_table,
targetColumnNames: constraint.referenced_columns,
updateConstraint,
deleteConstraint,
},
]
}

return null
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

processInterleaveConstraint duplicates most of processForeignKeyConstraint. To reduce maintenance risk (e.g. future changes to FK normalization not being applied here), consider reusing processForeignKeyConstraint by transforming the input (type: 'FOREIGN KEY', possibly slicing columns) and delegating to the existing handler.

Suggested change
constraint.type === 'INTERLEAVE' &&
constraint.columns &&
constraint.columns.length > 0 &&
constraint.referenced_columns &&
constraint.referenced_columns.length > 0 &&
constraint.referenced_table
) {
const { updateConstraint, deleteConstraint } = extractForeignKeyActions(
constraint.def,
)
return [
constraint.name,
{
type: 'FOREIGN KEY',
name: constraint.name,
columnNames: constraint.columns,
targetTableName: constraint.referenced_table,
targetColumnNames: constraint.referenced_columns,
updateConstraint,
deleteConstraint,
},
]
}
return null
constraint.type !== 'INTERLEAVE' ||
!constraint.columns ||
constraint.columns.length === 0 ||
!constraint.referenced_columns ||
constraint.referenced_columns.length === 0 ||
!constraint.referenced_table
) {
return null
}
return processForeignKeyConstraint({
...constraint,
type: 'FOREIGN KEY',
})

Copilot uses AI. Check for mistakes.
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.

[Cloud Spanner] Table relations are not visualized in generated ERD by @liam-hq/cli (>= 0.5.9).

2 participants