Skip to content

Bind JSON values as their JSON text - #39

Merged
quinnj-bot[bot] merged 1 commit into
mainfrom
claude/json-param-text
Oct 30, 2026
Merged

quinnj-bot[bot] merged 1 commit into
mainfrom
claude/json-param-text

Conversation

@quinnj-bot

@quinnj-bot quinnj-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

json and jsonb columns decode to JSON.LazyValue. Binding one back as a parameter sent Julia's display of it (LazyObject{String} with 1 entry: ...). json and jsonb targets rejected that text (Token "LazyObject" is invalid), and text targets stored it silently. JSON.JSONText had the same problem.

Both now bind as their JSON text, as scalars and as array elements. The text comes from JSON.jl's public JSON.parse(x, JSONText), which returns the value's exact source text, so large numbers keep every digit.

j = only(DBInterface.execute(conn, """SELECT '{"a": 1}'::jsonb AS j""")).j
DBInterface.execute(conn, raw"INSERT INTO docs (body) VALUES ($1)", (j,))
# main: jsonb column -> error; text column -> stores "LazyObject{String} with 1 entry: ..."

This also matters for Postgres.load! in #37. On that branch, copying a query result that has a jsonb column creates a text column filled with "LazyObject{String}()". With this change applied on top of #37, the column holds the JSON text. The column type still comes from #37's type mapping.

Found by a differential check of Postgres.jl against psycopg 3 on PostgreSQL 16 (bound parameters of every type, read back through the binary protocol).

Verification

  • New assertions next to the array type tests: they error on main and pass with this change.
  • Write-path harness: lazy values, nested lazy values and JSONText, bound into json, jsonb and jsonb[], through unnamed execute, prepared execute, executemany and explicit casts. All are stored byte-for-byte as expected. WHERE v = $1 on jsonb matches the stored row.
  • Full local Pkg.test() (Julia 1.12.6, Docker PostgreSQL 16): 9244/9244 passed.

🤖 Generated with Claude Code

json and jsonb columns decode to JSON.LazyValue, but binding one back as a
parameter sent Julia's display of it ("LazyObject{String} with 1 entry:
..."). json and jsonb targets rejected that text; text targets stored it
silently. LazyValue and JSONText parameters now send their JSON text, in
scalars and array elements alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@quinnj-bot
quinnj-bot Bot merged commit def9dca into main Oct 30, 2026
16 checks passed
quinnj-bot Bot pushed a commit that referenced this pull request Oct 30, 2026
JSON values read from json/jsonb columns, and JSON.JSONText, now get a
jsonb column instead of text when load! creates the table. Their COPY
text is the JSON itself (#39).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
quinnj-bot Bot added a commit that referenced this pull request Oct 30, 2026
* Add Postgres.load! to load a Tables.jl source into a table

MySQL.jl, SQLite.jl, ODBC.jl and LibPQ.jl can load a Tables.jl source;
Postgres.jl needed a hand-written CREATE TABLE plus executemany, or
hand-formatted COPY text.

`Postgres.load!(table, conn, name; append, coltypes, quoteidentifiers)`
creates the table from the source schema when it is missing and sends
the rows with COPY FROM STDIN in text format, in 4 MiB batches, each
value as its bind-parameter text with COPY escapes. The load is one
transaction, so a failure adds no rows and removes a table it created.
1M rows x 4 columns: 2.6 s, against 20.9 s for pipelined executemany.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Create JSON columns as jsonb in load!

JSON values read from json/jsonb columns, and JSON.JSONText, now get a
jsonb column instead of text when load! creates the table. Their COPY
text is the JSON itself (#39).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Jacob Quinn <quinn.jacobd@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@quinnj-bot quinnj-bot Bot mentioned this pull request Oct 30, 2026
quinnj referenced this pull request Oct 30, 2026
Co-authored-by: Jacob Quinn <quinn.jacobd@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant