Skip to content

Add jsonb[] array type pushdown support#286

Draft
sfc-gh-dachristensen wants to merge 6 commits into
mainfrom
pgguru/jsonb-array-pushdown
Draft

Add jsonb[] array type pushdown support#286
sfc-gh-dachristensen wants to merge 6 commits into
mainfrom
pgguru/jsonb-array-pushdown

Conversation

@sfc-gh-dachristensen
Copy link
Copy Markdown
Collaborator

Remove the JSONB[] exclusion in GetDuckDBTypeForPGType() that returned DUCKDB_TYPE_INVALID for arrays with JSONB elements, preventing pushdown. JSONB[] now maps to DUCKDB_TYPE_LIST like all other array types.

The existing infrastructure already handles this correctly:

  • ArrayOutForPGDuck serializes jsonb elements via jsonb_out
  • pgduck_server's list_to_text handles JSON (VARCHAR) child elements
  • The jsonb() macro in DuckDB maps jsonb casts to JSON

sfc-gh-dachristensen and others added 5 commits March 27, 2026 14:38
Remove the JSONB[] exclusion in GetDuckDBTypeForPGType() that returned
DUCKDB_TYPE_INVALID for arrays with JSONB elements, preventing pushdown.
JSONB[] now maps to DUCKDB_TYPE_LIST like all other array types.

The existing infrastructure already handles this correctly:
- ArrayOutForPGDuck serializes jsonb elements via jsonb_out
- pgduck_server's list_to_text handles JSON (VARCHAR) child elements
- The jsonb() macro in DuckDB maps jsonb casts to JSON

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
DuckDB does not have a jsonb type, only json. When the query rewriter
encounters JSONBARRAYOID in expression nodes (Const, CoerceViaIO,
RelabelType, Param, ArrayExpr), rewrite to JSONARRAYOID so the
deparsed query emits "json[]" instead of "jsonb[]".

Also handle JSONBARRAYOID -> JSONARRAYOID in insert_select.c for
null column construction, matching the existing JSONBOID -> JSONOID
handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
Revert the JSONBARRAYOID→JSONARRAYOID OID rewriting in rewrite_query.c
which broke operator resolution (operators resolved for jsonb[] args
became inconsistent after type OIDs were changed to json[], causing
DuckDB to receive unparseable OPERATOR(pg_catalog.=) syntax).

Instead, map jsonb/jsonb[] to json/json[] in deparse_type_name() which
only affects the SQL text sent to DuckDB without disturbing the
expression tree's operator and function OIDs.

Also revert the test assertion for col::jsonb[] cast back to expecting
no pushdown, since CoerceViaIO casts are not shippable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
Scalar jsonb is already handled by rewrite_query.c which wraps jsonb
expressions in __lake__internal__nsp__.jsonb() function calls. Mapping
JSONBOID to JSONOID in deparse_type_name caused DuckDB to receive
::json casts that broke operator resolution (json = json doesn't exist
in DuckDB). Only JSONBARRAYOID needs remapping since there's no
equivalent array rewrite mechanism.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
* DuckDB does not have a jsonb[] type. Map it to json[] which DuckDB
* understands. We do this at deparse time rather than rewriting OIDs
* in the expression tree, because changing OIDs would break operator
* and function lookup.
Copy link
Copy Markdown
Collaborator

@sfc-gh-mslot sfc-gh-mslot Mar 27, 2026

Choose a reason for hiding this comment

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

This isn't the real deparse though, we still parse the output of this (foreignscan case). Not sure I understand the effect of this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm just giving Claude a chance to bang its head against this for a bit, so it could definitely be a bit offbase here. (I guess I'll make this a draft so you don't waste your time on it until things are happy.)

The full query pushdown path uses PostgreSQL's pg_get_querydef
(ruleutils) for deparsing, which has no knowledge of our type mappings.
When RewriteConst creates RelabelType nodes with JSONBARRAYOID, or
when NULL constants retain JSONBARRAYOID type, ruleutils emits
::jsonb[] which DuckDB doesn't understand.

Fix by remapping JSONBARRAYOID to JSONARRAYOID directly in the
expression tree at the end of RewriteQueryTreeForPGDuckMutator. Only
value/output nodes (Const, RelabelType, Param, ArrayExpr) are changed
to avoid breaking operator resolution which depends on type OIDs.

This runs after RewriteConst/RewriteParam so their output nodes are
also caught. Combined with the deparse_type_name fix in deparse.c,
both deparse paths now emit json[] instead of jsonb[].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: David Christensen <david.christensen@snowflake.com>
@sfc-gh-dachristensen sfc-gh-dachristensen marked this pull request as draft March 27, 2026 20:16
else if (IsA(node, RelabelType) && ((RelabelType *) node)->resulttype == JSONBARRAYOID)
((RelabelType *) node)->resulttype = JSONARRAYOID;
else if (IsA(node, Param) && ((Param *) node)->paramtype == JSONBARRAYOID)
((Param *) node)->paramtype = JSONARRAYOID;
Copy link
Copy Markdown
Collaborator

@sfc-gh-mslot sfc-gh-mslot Apr 1, 2026

Choose a reason for hiding this comment

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

this looks messy. How do we map JSONB to JSON? Seems like we'd want to do the same thing here?

Iirc we create an alias in DuckDB, so do we actually need to do anything special for arrays w.r.t. deparsing/rewriting?

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