Skip to content

Add agtype <-> jsonb bidirectional casts (#350)#2361

Open
gregfelice wants to merge 2 commits intoapache:masterfrom
gregfelice:feature_agtype_jsonb_cast
Open

Add agtype <-> jsonb bidirectional casts (#350)#2361
gregfelice wants to merge 2 commits intoapache:masterfrom
gregfelice:feature_agtype_jsonb_cast

Conversation

@gregfelice
Copy link
Contributor

Summary

Adds explicit casts between agtype and jsonb (issue #350, open since 2022):

-- agtype -> jsonb: use jsonb operators on graph data
SELECT properties(n)::jsonb FROM cypher('g', $$ MATCH (n) RETURN n $$) AS (n agtype);
SELECT (props::jsonb)->>'name' FROM cypher('g', $$ MATCH (n) RETURN properties(n) $$) AS (props agtype);

-- jsonb -> agtype: pass jsonb data into Cypher context
SELECT '{"key": "value"}'::jsonb::agtype;

-- Roundtrip
SELECT ('{"x": 1}'::agtype::jsonb)::agtype;

-- Vertex/edge → jsonb (properties extracted as JSON object)
SELECT n::jsonb FROM cypher('g', $$ MATCH (n:Person) RETURN n $$) AS (n agtype);

Implementation

SQL language functions using proven text-intermediate paths:

  • agtype → jsonb: agtype_to_json($1)::jsonb
  • jsonb → agtype: $1::text::agtype

Why text intermediate? agtype extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT, AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize. Direct binary cast causes "unknown type of jsonb container" errors. The text roundtrip correctly handles all value types — graph types are serialized as JSON objects with their properties.

Files changed: sql/agtype_coercions.sql only (27 lines added). No C code changes.

Test plan

  • Object with integers: '{"name": "Alice", "age": 30}'::agtype::jsonb
  • Arrays: '[1, 2, 3]'::agtype::jsonb
  • Strings: '"hello"'::agtype::jsonb
  • Nested objects: '{"a": {"b": [1, true, null]}}'::agtype::jsonb
  • jsonb → agtype: '{"key": "value"}'::jsonb::agtype
  • Roundtrip: ('{"x": 1}'::agtype::jsonb)::agtype
  • Vertex → jsonb: graph type converted to JSON object
  • properties() → jsonb: direct property extraction
  • JSONB operators on agtype data: (props::jsonb)->>'name'
  • All 31 existing regression tests pass

🤖 Generated with Claude Code

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds explicit, bidirectional casts between agtype and jsonb so users can interoperate with PostgreSQL’s jsonb operators/functions and pass jsonb into Cypher contexts without manual conversion steps.

Changes:

  • Add ag_catalog.agtype_to_jsonb(agtype) SQL wrapper and CREATE CAST (agtype AS jsonb).
  • Add ag_catalog.jsonb_to_agtype(jsonb) SQL wrapper and CREATE CAST (jsonb AS agtype).
  • Document rationale for using a textual/JSON intermediate to avoid binary-format incompatibilities.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jrgemignani
Copy link
Contributor

@gregfelice Please see the above comments by Copilot

@gregfelice
Copy link
Contributor Author

Addressed both Copilot suggestions:

  1. Comment/implementation mismatch — Updated comment to describe the actual json-intermediate conversion path (agtype_to_json() -> json::jsonb) instead of the incorrect "text intermediate".

  2. Regression tests — Added agtype_jsonb_cast test suite registered in Makefile covering:

    • agtype -> jsonb: string, null, arrays (simple/nested/empty), objects (simple/nested/with arrays/empty)
    • jsonb -> agtype: all scalar types (string, int, float, bool, null), arrays, objects
    • Roundtrips: jsonb -> agtype -> jsonb for objects, arrays, null
    • Graph data: vertex and edge -> jsonb with structural key checks (label, properties, jsonb operator access)
    • NULL handling: SQL NULL through both cast directions

All regression tests pass (agtype_jsonb_cast: ok).

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

gregfelice and others added 2 commits March 26, 2026 14:14
Register explicit casts between agtype and jsonb, enabling:

  SELECT properties(n)::jsonb FROM cypher(...) AS (n agtype);
  SELECT '{"key": "value"}'::jsonb::agtype;

  -- Use jsonb operators on graph data:
  SELECT (props::jsonb)->>'name' FROM cypher(...) AS (props agtype);

Implementation uses SQL language functions that go through proven
text-intermediate paths:
  agtype -> jsonb:  agtype_to_json() -> json::jsonb
  jsonb -> agtype:  jsonb::text -> text::agtype

This approach is safe because agtype extends jsonb's binary format
with types (AGTV_INTEGER, AGTV_FLOAT, AGTV_VERTEX, AGTV_EDGE,
AGTV_PATH) that jsonb does not recognize, making direct binary
conversion unreliable.  The text roundtrip handles all value types
correctly including graph types (vertex/edge properties are
extracted as JSON objects).

All 31 regression tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…asts

- Fix comment in agtype_coercions.sql: document json-intermediate path
  (agtype_to_json -> json::jsonb) instead of incorrect "text intermediate"
- Add agtype_jsonb_cast regression test covering: string/null/array/object
  agtype->jsonb, all jsonb scalar types->agtype, roundtrips, vertex/edge
  ->jsonb with structural key checks, NULL handling
- Register agtype_jsonb_cast in Makefile REGRESS list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gregfelice gregfelice force-pushed the feature_agtype_jsonb_cast branch from c1ce37e to 6801d3a Compare March 26, 2026 18:18
@gregfelice
Copy link
Contributor Author

Rebased onto master (force push) — the branch previously included commit cd0aebc from PR #2360 (pattern expressions), which caused Copilot to flag unrelated changes (ExplainStmt comment, %expect, Makefile).

The PR now contains only the 2 cast-specific commits:

The 3 Copilot comments about pattern expression code (cypher_gram.y comment placement, %expect/%expect-rr, Makefile scope) are no longer applicable. The 2 original Copilot suggestions (comment/implementation mismatch, regression test coverage) were already addressed in the previous round.

Regression test: agtype_jsonb_cast OK.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 113 to 117
list_comprehension \
map_projection \
direct_field_access \
agtype_jsonb_cast \
security
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The PR description says only sql/agtype_coercions.sql changed, but this PR also adds a new regression test and updates the REGRESS list. Please update the PR description to reflect the additional test/build changes for accurate review context.

Copilot uses AI. Check for mistakes.
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.

3 participants