You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the database browse view, a plain array column can be mis-rendered as a to-many relationship (a Table → reverse-link chip) when it has no element type in describe_tableand happens to be named like a sibling table. Flagged by @DavidCockerill in review of #1438 as low-priority / non-blocking.
Where
src/features/instance/databases/functions/relationshipAttributes.ts → relatedTableNameOf() → findSiblingTableByName(). For an attribute reported by describe_table as { type: 'array' } with no elements, we match its name (and simple singular/plural variants) against sibling table names and treat it as a to-many relationship. Regression coverage that pins the current behavior: the "legacy elements-less relationships and reverse foreign keys" block in relationshipAttributes.test.ts.
Only elements-less arrays are affected. Typed scalar arrays (elements: 'String') and typed to-many relationships (elements: '<Table>') are unambiguous and unaffected.
Why it's a false positive (analysis)
Walking the two server regimes, an elements-less array in describe_table is never a Harper-modeled relationship:
Harper 5.1+: relationship attributes are omitted from describe_table entirely (see feat(databases): render, protect, and filter relationship attributes in browse #1438 / the describe regression). So anything that does appear as { type: 'array' } is a persisted plain/legacy array column, not a relationship → name match = false positive.
Harper 4.x: to-many relationships appear in describe with elementstyped (e.g. reviews: [Review] → { type: 'array', elements: 'Review' }, verified against 4.7.28). A real to-many is therefore caught by the typed path, not the elements-less name heuristic.
So the elements-less name heuristic has no regime where it catches a relationship Harper actually models — it only ever fires on plain array data. The data.Albums.tracks case that motivated it "worked" by luck: Tracks.albumId lined up by convention, but tracks was not a declared @relationship.
Real relationships are already covered without the heuristic: schema-declared ones via schemaRelationships.ts (exact from:/to:, the only source on 5.1) and typed to-many via describe on 4.x.
Options
Remove the elements-less name heuristic (findSiblingTableByName) — rely solely on schema-declared relationships + elements-typed describe. Removes the false positive at the root. Only behavior change: an un-declared elements-less array named like a table renders as plain JSON instead of a guessed reverse-link. Schema-declared to-many (e.g. Category.products) is unaffected (detected via schema, not this heuristic). Recommended.
Gate on schema presence — apply the heuristic only to tables absent from all parsed component schemas. More conservative but keeps a guess for truly schema-less data.
Corroborate — only treat an elements-less array as a relationship if the sibling table has a plausible reverse foreign key back-referencing this table. Narrows but doesn't eliminate false positives; couples detection to reverse-key inference.
Recommendation: option 1. If we want to keep a best-effort link for schema-less legacy data, option 2.
Not urgent
The column def, header, and data are all still present either way; the only impact is a plain array being shown as a link chip instead of JSON. No data correctness or write-path impact.
Summary
In the database browse view, a plain array column can be mis-rendered as a to-many relationship (a
Table →reverse-link chip) when it has no element type indescribe_tableand happens to be named like a sibling table. Flagged by @DavidCockerill in review of #1438 as low-priority / non-blocking.Where
src/features/instance/databases/functions/relationshipAttributes.ts→relatedTableNameOf()→findSiblingTableByName(). For an attribute reported bydescribe_tableas{ type: 'array' }with noelements, we match its name (and simple singular/plural variants) against sibling table names and treat it as a to-many relationship. Regression coverage that pins the current behavior: the "legacy elements-less relationships and reverse foreign keys" block inrelationshipAttributes.test.ts.Only elements-less arrays are affected. Typed scalar arrays (
elements: 'String') and typed to-many relationships (elements: '<Table>') are unambiguous and unaffected.Why it's a false positive (analysis)
Walking the two server regimes, an elements-less array in
describe_tableis never a Harper-modeled relationship:describe_tableentirely (see feat(databases): render, protect, and filter relationship attributes in browse #1438 / the describe regression). So anything that does appear as{ type: 'array' }is a persisted plain/legacy array column, not a relationship → name match = false positive.elementstyped (e.g.reviews: [Review]→{ type: 'array', elements: 'Review' }, verified against 4.7.28). A real to-many is therefore caught by the typed path, not the elements-less name heuristic.So the elements-less name heuristic has no regime where it catches a relationship Harper actually models — it only ever fires on plain array data. The
data.Albums.trackscase that motivated it "worked" by luck:Tracks.albumIdlined up by convention, buttrackswas not a declared@relationship.Real relationships are already covered without the heuristic: schema-declared ones via
schemaRelationships.ts(exactfrom:/to:, the only source on 5.1) and typed to-many via describe on 4.x.Options
findSiblingTableByName) — rely solely on schema-declared relationships + elements-typed describe. Removes the false positive at the root. Only behavior change: an un-declared elements-less array named like a table renders as plain JSON instead of a guessed reverse-link. Schema-declared to-many (e.g.Category.products) is unaffected (detected via schema, not this heuristic). Recommended.Recommendation: option 1. If we want to keep a best-effort link for schema-less legacy data, option 2.
Not urgent
The column def, header, and data are all still present either way; the only impact is a plain array being shown as a link chip instead of JSON. No data correctness or write-path impact.