Integrate with zodb-pgjsonb#

The zodb-pgjsonb storage backend uses zodb-json-codec to transcode ZODB pickle records into PostgreSQL JSONB. This guide explains the codec functions designed for that integration.

The data pipeline#

When a ZODB object is stored, its pickle bytes flow through the codec into PostgreSQL:

pickle bytes --> Rust decode --> JSON --> PostgreSQL JSONB column

On read, the reverse path reconstructs the original pickle bytes:

PostgreSQL JSONB --> JSON --> Rust encode --> pickle bytes

Two fast paths for decoding#

The codec provides two PG-specific decode functions. Both handle null-byte sanitization and persistent reference extraction in a single pass.

Python dict path#

decode_zodb_record_for_pg() returns a Python dict. The GIL is released during the Rust pickle-parsing phase, then reacquired to build the Python dict.

from zodb_json_codec import decode_zodb_record_for_pg

class_mod, class_name, state_dict, refs = decode_zodb_record_for_pg(pickle_data)
# class_mod:   "persistent.mapping" (str)
# class_name:  "PersistentMapping" (str)
# state_dict:  {"data": {"key": "value"}} (dict)
# refs:        [123456789, ...] (list of int OIDs)

Use this path when you need to inspect or transform the state in Python before writing to the database (for example, extracting extra columns via a state processor).

Direct JSON string path#

decode_zodb_record_for_pg_json() returns a JSON string directly. The entire pipeline – pickle decode, JSON serialization, null-byte sanitization, and ref extraction – runs in Rust with the GIL released.

from zodb_json_codec import decode_zodb_record_for_pg_json

class_mod, class_name, json_str, refs = decode_zodb_record_for_pg_json(pickle_data)
# json_str is a ready-to-insert JSON string

This is the fastest path: no intermediate Python dicts are allocated, and other Python threads can run during the entire operation.

Null-byte sanitization#

PostgreSQL JSONB cannot store \u0000 (null bytes) in strings. Both PG decode functions automatically replace strings containing null bytes with {"@ns:" "<base64>"} markers. On encode, these markers are transparently converted back to the original byte sequences.

Persistent reference extraction#

The refs list returned by both functions contains all persistent reference OIDs found in the object state, as Python integers (big-endian interpretation of the 8-byte ZODB OID). Cross-database references with non-standard OID sizes are silently skipped.

zodb-pgjsonb stores these in a refs column for pure-SQL garbage collection (pack) without needing to deserialize the JSON.

Encoding back to pickle#

To reconstruct ZODB pickle bytes from a JSON record:

from zodb_json_codec import encode_zodb_record

record = {
    "@cls": ["persistent.mapping", "PersistentMapping"],
    "@s": {"data": {"key": "value"}},
}
pickle_bytes = encode_zodb_record(record)

The encoder produces two concatenated pickles (class pickle + state pickle) in protocol 3 format, matching ZODB’s expected record layout.