|
9 | 9 | "net/netip"
|
10 | 10 | "reflect"
|
11 | 11 | "time"
|
12 |
| - |
13 |
| - "github.com/jackc/pgx/v5/internal/anynil" |
14 | 12 | )
|
15 | 13 |
|
16 | 14 | // PostgreSQL oids for common types
|
@@ -1914,8 +1912,17 @@ func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error)
|
1914 | 1912 | // (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data
|
1915 | 1913 | // written.
|
1916 | 1914 | func (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBuf []byte, err error) {
|
1917 |
| - if anynil.Is(value) { |
1918 |
| - return nil, nil |
| 1915 | + if isNil, callNilDriverValuer := isNilDriverValuer(value); isNil { |
| 1916 | + if callNilDriverValuer { |
| 1917 | + newBuf, err = (&encodePlanDriverValuer{m: m, oid: oid, formatCode: formatCode}).Encode(value, buf) |
| 1918 | + if err != nil { |
| 1919 | + return nil, newEncodeError(value, m, oid, formatCode, err) |
| 1920 | + } |
| 1921 | + |
| 1922 | + return newBuf, nil |
| 1923 | + } else { |
| 1924 | + return nil, nil |
| 1925 | + } |
1919 | 1926 | }
|
1920 | 1927 |
|
1921 | 1928 | plan := m.PlanEncode(oid, formatCode, value)
|
@@ -1970,3 +1977,55 @@ func (w *sqlScannerWrapper) Scan(src any) error {
|
1970 | 1977 |
|
1971 | 1978 | return w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)
|
1972 | 1979 | }
|
| 1980 | + |
| 1981 | +// canBeNil returns true if value can be nil. |
| 1982 | +func canBeNil(value any) bool { |
| 1983 | + refVal := reflect.ValueOf(value) |
| 1984 | + kind := refVal.Kind() |
| 1985 | + switch kind { |
| 1986 | + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice: |
| 1987 | + return true |
| 1988 | + default: |
| 1989 | + return false |
| 1990 | + } |
| 1991 | +} |
| 1992 | + |
| 1993 | +// valuerReflectType is a reflect.Type for driver.Valuer. It has confusing syntax because reflect.TypeOf returns nil |
| 1994 | +// when it's argument is a nil interface value. So we use a pointer to the interface and call Elem to get the actual |
| 1995 | +// type. Yuck. |
| 1996 | +// |
| 1997 | +// This can be simplified in Go 1.22 with reflect.TypeFor. |
| 1998 | +// |
| 1999 | +// var valuerReflectType = reflect.TypeFor[driver.Valuer]() |
| 2000 | +var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem() |
| 2001 | + |
| 2002 | +// isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement |
| 2003 | +// driver.Valuer if it is only implemented by T. |
| 2004 | +func isNilDriverValuer(value any) (isNil bool, callNilDriverValuer bool) { |
| 2005 | + if value == nil { |
| 2006 | + return true, false |
| 2007 | + } |
| 2008 | + |
| 2009 | + refVal := reflect.ValueOf(value) |
| 2010 | + kind := refVal.Kind() |
| 2011 | + switch kind { |
| 2012 | + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice: |
| 2013 | + if !refVal.IsNil() { |
| 2014 | + return false, false |
| 2015 | + } |
| 2016 | + |
| 2017 | + if _, ok := value.(driver.Valuer); ok { |
| 2018 | + if kind == reflect.Ptr { |
| 2019 | + // The type assertion will succeed if driver.Valuer is implemented on T or *T. Check if it is implemented on *T |
| 2020 | + // by checking if it is not implemented on *T. |
| 2021 | + return true, !refVal.Type().Elem().Implements(valuerReflectType) |
| 2022 | + } else { |
| 2023 | + return true, true |
| 2024 | + } |
| 2025 | + } |
| 2026 | + |
| 2027 | + return true, false |
| 2028 | + default: |
| 2029 | + return false, false |
| 2030 | + } |
| 2031 | +} |
0 commit comments