8000 fix(mapper): type hstore inside json as an object by colinhacks · Pull Request #70 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

fix(mapper): type hstore inside json as an object - #70

Merged
colinhacks merged 3 commits into
mainfrom
fix-hstore-in-json
Oct 23, 2026
Merged

colinhacks merged 3 commits into
mainfrom
fix-hstore-in-json

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

Postgres writes an hstore inside a JSON document as an object of its keys, each value text or null: json_build_object('h', 'a=>1, b=>NULL'::hstore) is {"h" : {"a": "1", "b": null}}, through the extension's cast to JSON, and json_agg of an hstore column is an array of such objects. The mapper typed it string there, and so did the JSON Schema writer and the oracle's soundness check.

  • src/render.ts: an hstore inside a document is Record<string, string | null>; outside one it stays its text.
  • src/schema.ts: the same as JSON Schema, { type: "object", additionalProperties: { type: ["string", "null"] } }.
  • oracle/src/seed.rs: the soundness check admits that object, and an hstore column is seeded with real values (no key, one key, a NULL value, a quoted key) instead of the fallback literals, which hstore refuses.
  • oracle/cases/json_hstore: an hstore read plain, inside json_build_object, and through json_agg.

Verified: nub run typecheck, tests/render.test.ts (with a new test), cargo build --workspace --all-targets with no warning, cargo test -p reylo-oracle, nub run build in site/, and the new oracle case on Postgres 18, which passes with the new check and fails with the old one (column j holds {"a":"1","b":null} at .prefs, typed as hstore).

Copilot AI balanced review requested due to automatic review settings October 23, 2026 16:19
@vercel
vercel Bot commented Oct 23, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 23, 2026 5:04pm UTC

Request Review

Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues. Two small gaps outside the diff are worth a look.

Reviewed changes

I reviewed the full diff: an hstore inside a JSON document is now typed as an object of nullable text in the TypeScript renderer, the JSON Schema writer and the oracle's soundness check.

  • Renderer: src/render.ts maps hstore in its json table to Record<string, string | null>. Outside a document it stays string, and the array path wraps it as (Record<string, string | null>)[].
  • JSON Schema: src/schema.ts writes { type: "object", additionalProperties: { type: ["string", "null"] } } for an hstore inside a document.
  • Oracle: json_admits in oracle/src/seed.rs now accepts an hstore only as an object whose values are all strings or null. The seed pool also gets real hstore literals, since hstore rejects the fallback '1'/'a'.
  • Coverage: there is a new oracle/cases/json_hstore case (plain, json_build_object, json_agg) and a new tests/render.test.ts test. The test passes locally.

The rule is correct. Postgres's JSON writers (json_build_object, json_agg, to_json, row_to_json) use a type's cast to json/jsonb when one exists, and the hstore extension creates both casts, so each of these writes an object. Other oracle cases already use contrib extensions (btree_gist, file_fdw), so create extension hstore will work on CI.

ℹ️ engine/core/src/schema.rs still writes an hstore inside a document as a string

The header of src/schema.ts says its spellings are the engine's (engine/core/src/sch 10000 ema.rs). The record case was added to both files together. After this PR, Type::json_schema in Rust still falls through to {"type": "string", "description": "pg type hstore"} when in_json is set, so the two tables disagree. Nothing ships through the Rust function today, since only its own tests call it. But engine/postgres/src/jsonshape.rs:1902 points to it as the place the renderer's rule is stated, so the two tables are now out of sync. Fixing it is a core edit, which means rebuilding all three engine.wasm. That may be better as a follow-up than as part of this PR.

Technical details
# Keep the Rust JSON Schema table in step with src/schema.ts for hstore

## Affected sites
- engine/core/src/schema.rs:173 — next to `"record" if opts.in_json`, there is no `hstore` arm, so an hstore inside a document falls to the `pg type {name}` string default at line 180

## Required outcome
- `Type::Scalar { name: "hstore" }.json_schema(IN_JSON)` returns `{"type": "object", "additionalProperties": {"type": ["string", "null"]}}`, the same as `typeSchema(scalar("hstore"), "postgres", false, true)`, and the `WIRE` spelling is unchanged
- a test in the existing `tests` module covers it
- all three `packages/<database>/engine.wasm` are rebuilt in one `engine/build.sh` run, since this is a change under `engine/core/`

ℹ️ The Postgres docs page doesn't show the new hstore rule

AGENTS.md asks that a change users would notice also update the docs in the same PR. Users will see this one as a different type on hover. The Values inside a document rule in site/content/docs/postgres.mdx lists how values change once they're inside JSON (bigint, numeric, timestamptz, bytea, a row). Its <Pairs> are written in the page itself rather than captured, so a single line covers it, e.g. - `json_build_object('h', 'a=>1, b=>NULL'::hstore)` → `{ h: { a: "1", b: null } }`, plus a few words in the paragraph above it.

Pullfrog  | Fix it ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
Copy link
Copy Markdown
Owner Author

Both findings are fixed in this PR:

  • Rust schema table: engine/core/src/schema.rs now has an hstore arm inside a document that writes the same object of nullable text as src/schema.ts, and a test covers it, with the wire spelling left as it was. All three engine.wasm are rebuilt in one run (31561a0).
  • Docs: the "Values inside a document" rule in postgres.mdx now says an hstore is an object of its keys, each value text or null, and has a <Pairs> line for json_build_object('h', 'a=>1, b=>NULL'::hstore) (8f724b9).

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found. Both points from the last review are fixed.

Reviewed changes

This run covers the two commits added since the last review. One updates the Rust JSON Schema table to match src/schema.ts; the other documents the new rule on the Postgres page.

  • Rust JSON Schema: engine/core/src/schema.rs now has a "hstore" if opts.in_json arm. Inside a document it writes the same object of nullable text as src/schema.ts. A new test checks this and also checks that the WIRE spelling stays a string.
  • Engine artifacts: all three engine.wasm files were rebuilt for the core edit. tests/fingerprint.test.ts, tests/engines.test.ts and tests/render.test.ts pass locally at 31561a0.
  • Docs: the "Values inside a document" paragraph in site/content/docs/postgres.mdx now covers hstore, and a new <Pairs> line shows json_build_object('h', 'a=>1, b=>NULL'::hstore) → { h: { a: "1", b: null } }.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks merged commit 4448916 into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the fix-hstore-in-json branch October 23, 2026 17:09
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.

2 participants

0