Custom Codec Registry: Phase 3 Design #146
Closed
SeanTAllen
started this conversation in
Research
Replies: 1 comment
-
|
replaced with #147 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Custom Codec Registry: Phase 3 Design
Design for Phase 3 of the codec work (discussion #139). Phases 1 (binary parameters, PR #141) and 2 (binary results, PR #144) are complete. Phase 3 makes the codec registry user-extensible so users can register custom codecs for PostgreSQL types not covered by built-ins.
The
FieldDataTypesExtensibility ProblemThe original Phase 3 plan (in #139) assumed custom codecs would "just work" by making
_with_codecpublic and adding aCodecRegistryparameter toSession. But there's a fundamental tension:FieldDataTypesis a closed union type.Codec.decode()returnsFieldDataTypes. A custom codec for PostgreSQLpoint(OID 600) can only return one of these existing variants — it can't return a customPointclass. The best it can do is decode toString(e.g.,"(1.5,2.3)") orArray[U8] val. Same limitation on the encode side:Codec.encode()takesFieldDataTypes, so users can't send custom Pony types as parameters.This means custom codecs without
FieldDataTypesextensibility have narrow value:Stringinstead of opaqueArray[U8] valFor comparison, other drivers solve this with open-world type systems:
any(interface{})FromSql/ToSqltrait objectsPony's type system makes this harder. The options:
Option A: Add a wrapper variant to the union
Users match on
CustomFieldDataand cast the inner value. The union stays closed (one more variant), exhaustive matching still works. But the inner value requires runtime type knowledge — users need to know what their custom codec put in there. Breaking change: all existing exhaustivematchexpressions onFieldDataTypesneed a new arm.Option B: Keep
FieldDataTypesclosed, accept the limitationCustom codecs can only decode to existing variants. Phase 3's value is "decode unknown OIDs to readable strings instead of opaque bytes" plus overriding built-in codecs. Simpler, no breaking change beyond making the API public.
Option C: Defer Phase 3 entirely
If the narrow scope isn't worth shipping and the open-world design needs more thought, park it. The internal
_with_codecinfrastructure is already there for when the design is ready. Ship Phase 3 when there's a concrete use case driving the requirements.The
encodeMethod QuestionThe original plan adds
fun encode(oid: U32, value: FieldDataTypes): Array[U8] val ?toCodecRegistry. But the current encode path in_FrontendMessage.bind()matches onFieldDataTypesvariants (Pony types) to select encoding, not on OIDs. A registry-based encode lookup by OID doesn't fit the architecture without refactoring.Questions:
encodeon the registry needed for Phase 3, or can it be deferred?CustomFieldData), how does the bind path know which codec to use? The OID isn't derivable from the Pony type — it would need to be specified somehow.Phase 3 Scope (from #139 v3 plan)
For reference, the original Phase 3 steps:
CodecRegistryuser-configurable — rename_with_codectowith_codec(public), addencodemethodregistryparameter toSessionconstructorexamples/custom-codec/example + updateexamples/README.mdFieldDataTypesexpansion costadded)Design Questions
FieldDataTypesbe extended with aCustomFieldDatawrapper (Option A), kept closed (Option B), or should Phase 3 be deferred (Option C)?CustomFieldDatawrap?Any valis the most flexible but requires runtime casting. A trait withstring(): Stringwould give basic formatting.encodeonCodecRegistrybe part of Phase 3 or deferred? The decode side (results) is straightforward; the encode side (parameters) has architectural friction.Beta Was this translation helpful? Give feedback.
All reactions