You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
with_array_type is now partial. It errors if the registration would
cause recursive decode (element OID is itself an array OID), collide
with an existing scalar or built-in array OID, use an OID already
registered as a custom element OID, or self-reference. Previously
these misconfigurations were silently accepted and would cause
incorrect behavior or stack overflow at decode time.
Closes#168
`CodecRegistry.with_array_type()` is now partial. It errors if the registration would cause problems at decode time:
4
+
5
+
-`element_oid` is itself an array OID (built-in or custom), which would cause unbounded recursion during decode
6
+
-`array_oid` collides with a registered scalar or built-in array OID
7
+
-`array_oid` is already registered as a custom element OID
8
+
-`array_oid == element_oid`
9
+
10
+
Previously, these misconfigurations were silently accepted and would cause incorrect behavior or stack overflow at decode time. Now the error surfaces at registry construction, where the OID values are visible in the source.
Copy file name to clipboardExpand all lines: CLAUDE.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -131,7 +131,7 @@ Only one operation is in-flight at a time. The queue serializes execution. `quer
131
131
-`StreamingResultReceiver` interface (tag) — `pg_stream_batch(Session, Rows)`, `pg_stream_complete(Session)`, `pg_stream_failed(Session, (PreparedQuery | NamedPreparedQuery), (ErrorResponseMessage | ClientQueryError))`. Pull-based: session delivers batches via `pg_stream_batch`; client calls `fetch_more()` for the next batch or `close_stream()` to end early
132
132
-`PipelineReceiver` interface (tag) — `pg_pipeline_result(Session, USize, Result)`, `pg_pipeline_failed(Session, USize, (PreparedQuery | NamedPreparedQuery), (ErrorResponseMessage | ClientQueryError))`, `pg_pipeline_complete(Session)`. Each query result/failure is delivered with its pipeline index. `pg_pipeline_complete` always fires last
133
133
-`Codec` interface (val) — `format(): U16`, `encode(FieldDataTypes): Array[U8] val ?`, `decode(Array[U8] val): FieldData ?`. Wire format codec for a PostgreSQL type. Encode stays closed (`FieldDataTypes`), decode is open (`FieldData`). Built-in codecs are primitives (zero-allocation singletons)
134
-
-`CodecRegistry` class (val) — maps OIDs to text and binary `Codec` instances. Immutable — `with_codec(oid, codec)` returns a new registry with the codec added or replacing an existing one. `with_array_type(array_oid, element_oid)` registers a custom array type mapping. `array_oid_for(element_oid)` returns the array OID (built-in + custom, 0 if unknown). Supports chaining: `CodecRegistry.with_codec(600, A).with_array_type(1017, 600)`. Default constructor populates all built-in codecs. `decode(oid, format, data)` is partial — returns `FieldData` for known OIDs, fallbacks for unknown OIDs (unknown text→`String`, unknown binary→`RawBytes`), and errors when a registered codec's `decode()` fails. Intercepts array OIDs before normal codec dispatch. `has_binary_codec(oid)` for format selection (includes array OIDs)
134
+
-`CodecRegistry` class (val) — maps OIDs to text and binary `Codec` instances. Immutable — `with_codec(oid, codec)` returns a new registry with the codec added or replacing an existing one. `with_array_type(array_oid, element_oid)?` registers a custom array type mapping (partial — errors if element_oid is an array OID, array_oid collides with a registered OID, array_oid is already a custom element OID, or array_oid == element_oid). `array_oid_for(element_oid)` returns the array OID (built-in + custom, 0 if unknown). Supports chaining: `CodecRegistry.with_codec(600, A).with_array_type(1017, 600)?`. Default constructor populates all built-in codecs. `decode(oid, format, data)` is partial — returns `FieldData` for known OIDs, fallbacks for unknown OIDs (unknown text→`String`, unknown binary→`RawBytes`), and errors when a registered codec's `decode()` fails. Intercepts array OIDs before normal codec dispatch. `has_binary_codec(oid)` for format selection (includes array OIDs)
135
135
-`ClientQueryError` — union type `(SessionNeverOpened | SessionClosed | SessionNotAuthenticated | DataError)`
136
136
-`DatabaseConnectInfo` — val class grouping database authentication parameters (user, password, database). Passed to `Session.create()` alongside `ServerConnectInfo`.
137
137
-`ServerConnectInfo` — val class grouping connection parameters (auth, host, service, ssl_mode). Passed to `Session.create()` as the first parameter. Also used by `_CancelSender`.
- Text codecs (`_text_codecs.pony`): `_BoolTextCodec`, `_ByteaTextCodec`, `_Int2TextCodec`, `_Int4TextCodec`, `_Int8TextCodec`, `_Float4TextCodec`, `_Float8TextCodec`, `_DateTextCodec`, `_TimeTextCodec`, `_TimestampTextCodec`, `_TimestamptzTextCodec`, `_IntervalTextCodec` (supports all four `intervalstyle` formats: `postgres`, `postgres_verbose`, `iso_8601`, `sql_standard` — detected via heuristic in `decode()`), `_TextPassthroughTextCodec`, `_OidTextCodec`, `_NumericTextCodec`, `_UuidTextCodec`, `_JsonbTextCodec`
171
171
-`_ArrayOidMap` (`_array_oid_map.pony`): static bidirectional mapping between element OIDs and array OIDs (23 entries). Methods: `element_oid_for(array_oid)`, `array_oid_for(element_oid)`, `is_array_oid(oid)`
172
172
-`_ArrayEncoder` (`_array_encoder.pony`): encodes `PgArray` to binary array wire format. Dispatches element encoding on Pony runtime types. String elements routed by `element_oid`: uuid → `_UuidBinaryCodec`, jsonb → `_JsonbBinaryCodec`, oid → `_OidBinaryCodec`, numeric → `_NumericBinaryCodec`; all others → raw UTF-8 bytes. Coupling: element encoding must stay in sync with `_FrontendMessage.bind()` and `_binary_codecs.pony`
173
-
-`CodecRegistry` (`codec_registry.pony`): maps OIDs to codecs. Adds `_custom_array_element_oids: Map[U32, U32] val` field for custom array type registrations. Default constructor populates all built-ins. `with_codec(oid, codec)` returns a new registry with the codec added/replaced. `with_array_type(array_oid, element_oid)` returns a new registry with the custom array mapping added. `array_oid_for(element_oid): U32` returns the array OID for the given element OID. `decode()` intercepts array OIDs before normal codec dispatch via two-phase array decoding: `_parse_binary_array`/`_parse_text_array` extract structure and raw element bytes (errors fall back), then `_decode_array_elements` decodes each element via the element codec (errors propagate). `has_binary_codec()` checks array OIDs too. `_with_codec` constructor (type-private) used internally by `with_codec`
173
+
- `CodecRegistry` (`codec_registry.pony`): maps OIDs to codecs. Adds `_custom_array_element_oids: Map[U32, U32] val` field for custom array type registrations. Default constructor populates all built-ins. `with_codec(oid, codec)` returns a new registry with the codec added/replaced. `with_array_type(array_oid, element_oid)?` returns a new registry with the custom array mapping added (errors if element_oid is an array OID, array_oid collides with a registered OID, array_oid is already a custom element OID, or array_oid == element_oid). `array_oid_for(element_oid): U32` returns the array OID for the given element OID. `decode()` intercepts array OIDs before normal codec dispatch via two-phase array decoding: `_parse_binary_array`/`_parse_text_array` extract structure and raw element bytes (errors fall back), then `_decode_array_elements` decodes each element via the element codec (errors propagate). `has_binary_codec()` checks array OIDs too. `_with_codec` constructor (type-private) used internally by `with_codec`
174
174
-`_ParamEncoder` (`_param_encoder.pony`): derives PostgreSQL OIDs from `FieldDataTypes` parameter values for Parse messages. Takes `registry: CodecRegistry` parameter; `PgArray` arm uses `registry.array_oid_for(a.element_oid)`
175
175
176
176
**Encode error handling:**`_FrontendMessage.bind()` is partial — it errors if parameter encoding fails. Takes `registry: CodecRegistry` parameter (with default `CodecRegistry`); pre-encodes `PgArray` parameters via `_ArrayEncoder` before the `recover val` block. `_QueryReady.try_run_query()` uses a build-before-transition pattern: wire messages are constructed before transitioning to an in-flight state, so encode errors deliver `DataError` to the receiver without leaving the state machine inconsistent. Pipeline queries build message parts into an `iso` array in `ref` scope (where error handling has full access to the session and receiver), then consume the array into a `recover val` block for concatenation.
0 commit comments