From 644a46b3141a6c7880e9a6abdcfbe9c997c0e0b0 Mon Sep 17 00:00:00 2001 From: waterWang Date: Fri, 7 Aug 2026 04:37:27 +0800 Subject: [PATCH] fix: prefer codec over driver.Valuer for typed-nil in array/composite encoding When a typed-nil value in an array or composite field implements driver.Valuer, the array and composite codecs bypass the element type's registered Codec and directly call encodePlanDriverValuer. This is incorrect when the type has a registered Codec that should handle nil values (typically encoding as SQL NULL). Fix: replace the encodePlanDriverValuer shortcut with PlanEncode, which naturally gives precedence to the Codec's PlanEncode before falling back to driver.Valuer encoding. This ensures that types with both a Codec and driver.Valuer use the Codec's nil handling. Fixes #2611 --- pgtype/array_codec.go | 20 ++++++++++++++++++-- pgtype/composite.go | 10 ++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/pgtype/array_codec.go b/pgtype/array_codec.go index cd8c8ce22..19eb56fd3 100644 --- a/pgtype/array_codec.go +++ b/pgtype/array_codec.go @@ -145,7 +145,15 @@ func (p *encodePlanArrayCodecText) Encode(value any, buf []byte) (newBuf []byte, return nil, err } } else if callNilDriverValuer { - elemBuf, err = (&encodePlanDriverValuer{m: p.m, oid: p.ac.ElementType.OID, formatCode: TextFormatCode}).Encode(elem, inElemBuf) + elemType := reflect.TypeOf(elem) + if lastElemType != elemType { + lastElemType = elemType + encodePlan = p.m.PlanEncode(p.ac.ElementType.OID, TextFormatCode, elem) + if encodePlan == nil { + return nil, fmt.Errorf("unable to encode %v", array.Index(i)) + } + } + elemBuf, err = encodePlan.Encode(elem, inElemBuf) if err != nil { return nil, err } @@ -215,7 +223,15 @@ func (p *encodePlanArrayCodecBinary) Encode(value any, buf []byte) (newBuf []byt return nil, err } } else if callNilDriverValuer { - elemBuf, err = (&encodePlanDriverValuer{m: p.m, oid: p.ac.ElementType.OID, formatCode: BinaryFormatCode}).Encode(elem, buf) + elemType := reflect.TypeOf(elem) + if lastElemType != elemType { + lastElemType = elemType + encodePlan = p.m.PlanEncode(p.ac.ElementType.OID, BinaryFormatCode, elem) + if encodePlan == nil { + return nil, fmt.Errorf("unable to encode %v", array.Index(i)) + } + } + elemBuf, err = encodePlan.Encode(elem, buf) if err != nil { return nil, err } diff --git a/pgtype/composite.go b/pgtype/composite.go index bcce0016c..5fda55d5c 100644 --- a/pgtype/composite.go +++ b/pgtype/composite.go @@ -494,7 +494,10 @@ func (b *CompositeBinaryBuilder) AppendValue(oid uint32, field any) { var plan EncodePlan if isNil { - plan = &encodePlanDriverValuer{m: b.m, oid: oid, formatCode: BinaryFormatCode} + plan = b.m.PlanEncode(oid, BinaryFormatCode, field) + if plan == nil { + plan = &encodePlanDriverValuer{m: b.m, oid: oid, formatCode: BinaryFormatCode} + } } else { plan = b.m.PlanEncode(oid, BinaryFormatCode, field) if plan == nil { @@ -555,7 +558,10 @@ func (b *CompositeTextBuilder) AppendValue(oid uint32, field any) { var plan EncodePlan if isNil { - plan = &encodePlanDriverValuer{m: b.m, oid: oid, formatCode: TextFormatCode} + plan = b.m.PlanEncode(oid, TextFormatCode, field) + if plan == nil { + plan = &encodePlanDriverValuer{m: b.m, oid: oid, formatCode: TextFormatCode} + } } else { plan = b.m.PlanEncode(oid, TextFormatCode, field) if plan == nil {