@@ -159,6 +159,160 @@ create type point3d as (
159159 })
160160}
161161
162+ type parent struct {
163+ Name string
164+ Location * point3d
165+ }
166+
167+ func (p parent ) IsNull () bool {
168+ return false
169+ }
170+
171+ func (p parent ) Index (i int ) any {
172+ switch i {
173+ case 0 :
174+ return p .Name
175+ case 1 :
176+ return p .Location
177+ default :
178+ panic ("invalid index" )
179+ }
180+ }
181+
182+ func (p * parent ) ScanNull () error {
183+ return fmt .Errorf ("cannot scan NULL into parent" )
184+ }
185+
186+ func (p * parent ) ScanIndex (i int ) any {
187+ switch i {
188+ case 0 :
189+ return & p .Name
190+ case 1 :
191+ return & p .Location
192+ default :
193+ panic ("invalid index" )
194+ }
195+ }
196+
197+ // https://github.com/jackc/pgx/issues/2453
198+ func TestCompositeCodecTranscodeStructWithNilPointer (t * testing.T ) {
199+ defaultConnTestRunner .RunTest (context .Background (), t , func (ctx context.Context , t testing.TB , conn * pgx.Conn ) {
200+ _ , err := conn .Exec (ctx , `drop type if exists parent;
201+ drop type if exists point3d;
202+
203+ create type point3d as (
204+ x float8,
205+ y float8,
206+ z float8
207+ );
208+
209+ create type parent as (
210+ name text,
211+ location point3d
212+ );` )
213+ require .NoError (t , err )
214+ defer conn .Exec (ctx , "drop type parent" )
215+ defer conn .Exec (ctx , "drop type point3d" )
216+
217+ dataTypes , err := conn .LoadTypes (ctx , []string {"point3d" , "parent" })
218+ require .NoError (t , err )
219+ conn .TypeMap ().RegisterTypes (dataTypes )
220+
221+ formats := []struct {
222+ name string
223+ code int16
224+ }{
225+ {name : "TextFormat" , code : pgx .TextFormatCode },
226+ {name : "BinaryFormat" , code : pgx .BinaryFormatCode },
227+ }
228+
229+ for _ , format := range formats {
230+ input := parent {Name : "test" , Location : nil }
231+ var output parent
232+ err := conn .QueryRow (ctx , "select $1::parent" , pgx.QueryResultFormats {format .code }, input ).Scan (& output )
233+ require .NoErrorf (t , err , "%v" , format .name )
234+ require .Equalf (t , input , output , "%v" , format .name )
235+ }
236+ })
237+ }
238+
239+ type parentWithSlice struct {
240+ Name string
241+ Locations []* point3d
242+ }
243+
244+ func (p parentWithSlice ) IsNull () bool {
245+ return false
246+ }
247+
248+ func (p parentWithSlice ) Index (i int ) any {
249+ switch i {
250+ case 0 :
251+ return p .Name
252+ case 1 :
253+ return p .Locations
254+ default :
255+ panic ("invalid index" )
256+ }
257+ }
258+
259+ func (p * parentWithSlice ) ScanNull () error {
260+ return fmt .Errorf ("cannot scan NULL into parent" )
261+ }
262+
263+ func (p * parentWithSlice ) ScanIndex (i int ) any {
264+ switch i {
265+ case 0 :
266+ return & p .Name
267+ case 1 :
268+ return & p .Locations
269+ default :
270+ panic ("invalid index" )
271+ }
272+ }
273+
274+ // https://github.com/jackc/pgx/issues/2453
275+ func TestCompositeCodecTranscodeStructWithSliceOfNilPointer (t * testing.T ) {
276+ defaultConnTestRunner .RunTest (context .Background (), t , func (ctx context.Context , t testing.TB , conn * pgx.Conn ) {
277+ _ , err := conn .Exec (ctx , `drop type if exists parent;
278+ drop type if exists point3d;
279+
280+ create type point3d as (
281+ x float8,
282+ y float8,
283+ z float8
284+ );
285+
286+ create type parent_with_slice as (
287+ name text,
288+ locations point3d[]
289+ );` )
290+ require .NoError (t , err )
291+ defer conn .Exec (ctx , "drop type parent_with_slice" )
292+ defer conn .Exec (ctx , "drop type point3d" )
293+
294+ dataTypes , err := conn .LoadTypes (ctx , []string {"point3d" , "parent_with_slice" })
295+ require .NoError (t , err )
296+ conn .TypeMap ().RegisterTypes (dataTypes )
297+
298+ formats := []struct {
299+ name string
300+ code int16
301+ }{
302+ {name : "TextFormat" , code : pgx .TextFormatCode },
303+ {name : "BinaryFormat" , code : pgx .BinaryFormatCode },
304+ }
305+
306+ for _ , format := range formats {
307+ input := parentWithSlice {Name : "test" , Locations : []* point3d {nil }}
308+ var output parentWithSlice
309+ err := conn .QueryRow (ctx , "select $1::parent_with_slice" , pgx.QueryResultFormats {format .code }, input ).Scan (& output )
310+ require .NoErrorf (t , err , "%v" , format .name )
311+ require .Equalf (t , input , output , "%v" , format .name )
312+ }
313+ })
314+ }
315+
162316func TestCompositeCodecDecodeValue (t * testing.T ) {
163317 defaultConnTestRunner .RunTest (context .Background (), t , func (ctx context.Context , t testing.TB , conn * pgx.Conn ) {
164318 _ , err := conn .Exec (ctx , `drop type if exists point3d;
0 commit comments