Files
Andrew Valleteau 10eaab766f fix(studio): filter on every column when viewing a composite FK record (#50256)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix

## What is the current behavior?

In the Table Editor, the "View referencing record" arrow on a foreign
key cell builds its filter from the table's `relationships` list. That
list is one entry per source-by-target column combination, and the
formatter takes the first entry whose source column matches the clicked
cell, then filters the referenced table on that single target column.

For a composite foreign key this fails in two ways:

- The value from the clicked column can be applied to the wrong target
column (whichever target column happens to be listed first for that
source column).
- Only one of the key columns is ever filtered on, so the peek and the
"Open table" link return zero rows or too many rows.

This is distinct from #41068 / #41080, which fixed the cartesian
expansion in the shared `tables.ts` introspection query. The bug
reproduces with that fix in place because the mis-pairing happens in
Studio when building the filter. Reported by a customer via support.

### Reproduction

Run this in the SQL editor. The referenced column list `(org_id,
bucket_id)` is deliberately not in the referenced table's physical
column order, which is what exposes the bug.

```sql
create schema if not exists dcs;

-- bucket_id declared first, org_id second
create table dcs.org_metering_buckets (
  bucket_id bigint not null,
  org_id    bigint not null,
  primary key (org_id, bucket_id)
);

create table dcs.machine_storage_usage_buckets (
  org_id                 bigint not null,
  org_metering_bucket_id bigint not null,
  constraint machine_storage_usage_buckets_org_metering_bucket_fkey
    foreign key (org_id, org_metering_bucket_id)
    references dcs.org_metering_buckets (org_id, bucket_id)
);

insert into dcs.org_metering_buckets (bucket_id, org_id) values
  (901, 1), (902, 1), (903, 2);

insert into dcs.machine_storage_usage_buckets (org_id, org_metering_bucket_id) values
  (1, 901), (1, 902), (2, 903);
```

1. Open `dcs.machine_storage_usage_buckets` in the Table Editor (select
the `dcs` schema).
2. Hover the `org_id` cell on the row where `org_id = 2`.
3. Click the "View referencing record" arrow.
4. Click "Open table" in the popover.

**Before this PR:** the popover shows "No results were returned". "Open
table" opens `dcs.org_metering_buckets` with a single filter `bucket_id
= 2`, which matches nothing.

**After this PR:** the popover shows the one row `(bucket_id = 903,
org_id = 2)`. "Open table" opens `dcs.org_metering_buckets` with two
filters, `org_id = 2` and `bucket_id = 903`, and the URL carries two
`filter=` params. Clicking the arrow on the `org_metering_bucket_id`
cell of the same row produces the same result.

Extra checks worth doing while you are there:

- Set one of the two key columns to NULL on a row. The arrow should
disappear for both key cells on that row, since a row with a null key
column does not reference anything under MATCH SIMPLE.
- A single-column foreign key (any existing table) should behave exactly
as before.

## What is the new behavior?

- New `ForeignKeyFormatter.utils.ts` with two pure functions.
`findColumnForeignKeyConstraint` locates the constraint on the current
table that contains the clicked column. `getReferencingRecordFilters`
pairs each source column with the target column at the same ordinal
position and builds one equality filter per pair from the row's values,
keeping the existing bytea-to-hex handling per column. It returns no
filters when any key column is null.
- `ForeignKeyFormatter` now reads the foreign key constraints query,
which returns ordinally paired source and target column arrays, instead
of the `relationships` list. The grid already fetches that query for the
same schema, so it is served from the React Query cache.
- `ReferenceRecordPeek` takes a `filters` array instead of a single
column and value. Both the peek query and the "Open table" link use the
full set, and the link emits one URI-encoded `filter=` param per column.
- Unit tests cover the reproduction above, single-column keys, bytea
values, null and missing values, falsy-but-valid values such as `0`, and
malformed constraints.

## Additional context

The table-editor introspection SQL in
`packages/pg-meta/src/sql/studio/table-editor/table.ts` and
`tables-paginated.ts` still expands composite foreign keys as a
cartesian product. #41080 only fixed the shared `tables.ts` query. The
arrow no longer depends on that data, but it can still mislabel the
referenced column elsewhere in Studio, so that is left for a follow-up
rather than widening this change into pg-meta SQL.

Before:
<img width="836" height="504" alt="Screenshot 2026-09-14 at 11 22 49"
src="https://github.com/user-attachments/assets/4645d64f-9bcb-44b0-b5b1-ab11ba7436db"
/>

After:
<img width="873" height="570" alt="Screenshot 2026-09-14 at 11 23 14"
src="https://github.com/user-attachments/assets/f2c47121-fd75-4efe-a370-28015c1b674e"
/>



🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01SkH86UV1KD4Xs5k9vt43tW

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved foreign-key record previews to correctly identify referenced
records across schemas and tables.
* Added support for composite foreign keys, ensuring previews and “Open
table” links apply all required column filters.
* Improved handling of binary values and incomplete or null foreign-key
data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 19:22:04 +02:00
..