Skip to content

fix(entities): stop pushing field projection into DB query (#73)#78

Merged
rrrodzilla merged 1 commit into
mainfrom
fix/73-field-projection-drops-rows
May 28, 2026
Merged

fix(entities): stop pushing field projection into DB query (#73)#78
rrrodzilla merged 1 commit into
mainfrom
fix/73-field-projection-drops-rows

Conversation

@rrrodzilla
Copy link
Copy Markdown
Contributor

Summary

Fixes #73. schemaforge entity list <Schema> --fields ... (and entity query ... --fields) returned entities: [] / count: 0 while total_count stayed non-zero whenever a record-access policy was active (the default CedarRecordPolicy).

Root cause

execute_entity_query pushed the client fields projection down into the backend query as a SQL column selection. Rows came back carrying only the requested columns. Each row is then reconstructed as a Cedar resource entity and validated in strict mode inside filter_visible (Entities::from_entities(.., Some(&schema))).

Required DSL fields are emitted as required Cedar attributes:

// cedar/schema_gen.rs
let optional_marker = if field.is_required() { "" } else { "?" };

A projected row missing a required field (or missing any optional field a custom Cedar policy references) fails strict-mode entity validation → authorize returns Errfilter_visible silently drops the row via its catch-all arm. Every row drops, but the independent COUNT(*) query is unaffected — producing the reported total_count/entities mismatch with a 200 status and no error.

Fix

Stop pushing the user fields projection into the DB query. Authorization now sees complete entities; the projection is applied purely as a presentation concern after filtering, via the already-present fields.retain(...) step. query.projection remains available for internal sub-queries (relation-display resolution, derived collections) that don't run user-facing record authorization.

This also covers entity query ... --fields (issue's open question), since both endpoints share execute_entity_query.

Testing

  • New regression test list_field_projection_omitting_required_field_returns_rows in auth_demo.rs: lists a row with ?fields=name,notes that omits a required category field and asserts the row is still returned (count == 1, total_count == 1). Red before the fix, green after.
  • Full workspace suite: 1727 passed, 0 failed.
  • cargo clippy -p schema-forge-acton --all-targets: clean.

Closes #73.

`entity list --fields ...` (and `entity query ... --fields`) returned
`entities: []` / `count: 0` while `total_count` stayed non-zero whenever a
record-access policy was active.

`execute_entity_query` pushed the client `fields` projection down into the
backend query as a column selection, so rows came back carrying only the
requested columns. Each row is then reconstructed as a Cedar resource entity
and validated in strict mode inside `filter_visible`. Required DSL fields are
emitted as required Cedar attributes, so a projected row missing them (or
missing any optional field a custom policy references) fails strict-mode
validation; `authorize` returns Err and the row is silently dropped. Every
row drops, but the independent COUNT(*) is unaffected — hence the mismatch.

Remove the DB-level push-down. Authorization now sees complete entities, and
the projection is applied purely as a presentation concern after filtering
via the existing `fields.retain` step. `query.projection` stays available for
internal sub-queries (display resolution, derived collections) that don't run
user-facing record authorization.

Adds a regression test that lists a row with a projection omitting a required
field and asserts the row is still returned.
@rrrodzilla rrrodzilla merged commit 51c02f3 into main May 28, 2026
1 check passed
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.

schemaforge entity list --fields returns empty entities[] despite non-zero total_count

1 participant