fix: composite PK detection + filter type-safety from schema source of truth#879
Conversation
…f truth Issue 1: buildTableFilterProperties now uses the schema's filter input type as the sole source of truth (via TypeRegistry), capturing plugin-injected filter fields (bm25Body, tsvTsv, trgmName, vectorEmbedding, geom, etc.) that are not present on the entity type. Same pattern as buildOrderByValues. Adds collectFilterExtraInputTypes to discover custom filter types referenced by plugin fields. Issue 2: inferPrimaryKeyFromInputObject now returns all candidate key fields instead of null when multiple exist, enabling composite PK detection for junction tables like PostTag(postId, tagId). Prefers DeleteInput over UpdateInput as signal source (fewer non-PK fields to filter).
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| const keyFields = | ||
| inferPrimaryKeyFromInputObject(deleteInput).length > 0 | ||
| ? inferPrimaryKeyFromInputObject(deleteInput) | ||
| : inferPrimaryKeyFromInputObject(updateInput); |
There was a problem hiding this comment.
🟡 Redundant double invocation of inferPrimaryKeyFromInputObject(deleteInput)
The ternary expression calls inferPrimaryKeyFromInputObject(deleteInput) twice — once to check .length > 0 and again to obtain the result. The function iterates through inputFields with .find() and .filter() each time. While the function is pure and this doesn't cause incorrect behavior, it unnecessarily performs the work twice. The result of the first call should be stored in a variable.
| const keyFields = | |
| inferPrimaryKeyFromInputObject(deleteInput).length > 0 | |
| ? inferPrimaryKeyFromInputObject(deleteInput) | |
| : inferPrimaryKeyFromInputObject(updateInput); | |
| const deleteKeyFields = inferPrimaryKeyFromInputObject(deleteInput); | |
| const keyFields = deleteKeyFields.length > 0 | |
| ? deleteKeyFields | |
| : inferPrimaryKeyFromInputObject(updateInput); |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Two targeted fixes in the ORM codegen introspection pipeline:
1. Composite primary key detection (
infer-tables.ts)inferPrimaryKeyFromInputObjectpreviously returnednullwhen multiple candidate key fields existed (e.g., junction tablePostTagwithpostId + tagId). Now returns all candidate fields, enabling composite PK detection. PrefersDeleteInputoverUpdateInputas signal source since it has fewer non-PK fields to filter out.2. Plugin filter type-safety (
input-types-generator.ts)buildTableFilterPropertiespreviously only generated filter fields fromtable.fields(entity columns), missing plugin-injected fields likebm25Body,tsvTsv,trgmName,vectorEmbedding,geom, etc. Now uses the schema's{Entity}Filterinput type from the TypeRegistry as sole source of truth when available — same pattern asbuildOrderByValues(). AddscollectFilterExtraInputTypes()to discover custom filter types (e.g.,Bm25BodyFilter) that need generation.Both changes are backward compatible: composite PK detection is additive, and filter generation falls back to the existing
table.fields-based approach when TypeRegistry is absent.Review & Testing Checklist for Human
typeRefToTsresolves plugin filter types correctly — the schema path usestypeRefToTs(field.type)for ALL filter fields (includingand/or/notself-referential types and plugin types likeBm25BodyFilter). Confirm the generated TypeScript matches what the old fallback path would produce for standard fields, and that plugin types resolve to valid type names.clientMutationIdand patch fields fromDeleteInput, ALL remaining fields are PK fields. This is correct for PostGraphile's standard Delete inputs, but verify no plugins inject extra non-PK fields into Delete input types.hasValidPrimaryKey()behavior — it returnsfalsefor composite keys (pk.fields.length === 1check atutils.ts:456). This means single-row query hooks still won't generate for junction tables. Verify this is intentional and that the M:N add/remove path doesn't depend onhasValidPrimaryKey().orm-testsuite against a real schema with plugins to verify both fixes end-to-end: plugin filter fields appear typed in generatedinput-types.ts, and junction table composite PKs show up inconstraints.primaryKey.Notes
inferPrimaryKeyFromInputObject(deleteInput)is called twice in the ternary on lines 728-730. Minor inefficiency — could cache the result, but not a correctness issue.pnpm build(all packages compile). Integration testing against a live schema with plugins would provide stronger confidence.Link to Devin session: https://app.devin.ai/sessions/745d2d10b699452091e24131ba5edef2
Requested by: @pyramation