github.com/jackc/pgx/v5@v5.5.5/pgtype/pgtype_test.go (about) 1 package pgtype_test 2 3 import ( 4 "context" 5 "database/sql" 6 "database/sql/driver" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "net" 11 "os" 12 "regexp" 13 "strconv" 14 "testing" 15 16 "github.com/jackc/pgx/v5" 17 "github.com/jackc/pgx/v5/pgtype" 18 "github.com/jackc/pgx/v5/pgxtest" 19 _ "github.com/jackc/pgx/v5/stdlib" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 var defaultConnTestRunner pgxtest.ConnTestRunner 25 26 func init() { 27 defaultConnTestRunner = pgxtest.DefaultConnTestRunner() 28 defaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig { 29 config, err := pgx.ParseConfig(os.Getenv("PGX_TEST_DATABASE")) 30 require.NoError(t, err) 31 return config 32 } 33 } 34 35 // Test for renamed types 36 type _string string 37 type _bool bool 38 type _uint8 uint8 39 type _int8 int8 40 type _int16 int16 41 type _int16Slice []int16 42 type _int32Slice []int32 43 type _int64Slice []int64 44 type _float32Slice []float32 45 type _float64Slice []float64 46 type _byteSlice []byte 47 48 // unregisteredOID represents an actual type that is not registered. Cannot use 0 because that represents that the type 49 // is not known (e.g. when using the simple protocol). 50 const unregisteredOID = uint32(1) 51 52 func mustParseInet(t testing.TB, s string) *net.IPNet { 53 ip, ipnet, err := net.ParseCIDR(s) 54 if err == nil { 55 if ipv4 := ip.To4(); ipv4 != nil { 56 ipnet.IP = ipv4 57 } else { 58 ipnet.IP = ip 59 } 60 return ipnet 61 } 62 63 // May be bare IP address. 64 // 65 ip = net.ParseIP(s) 66 if ip == nil { 67 t.Fatal(errors.New("unable to parse inet address")) 68 } 69 ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)} 70 if ipv4 := ip.To4(); ipv4 != nil { 71 ipnet.IP = ipv4 72 ipnet.Mask = net.CIDRMask(32, 32) 73 } 74 return ipnet 75 } 76 77 func mustParseMacaddr(t testing.TB, s string) net.HardwareAddr { 78 addr, err := net.ParseMAC(s) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 return addr 84 } 85 86 func skipCockroachDB(t testing.TB, msg string) { 87 conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE")) 88 if err != nil { 89 t.Fatal(err) 90 } 91 defer conn.Close(context.Background()) 92 93 if conn.PgConn().ParameterStatus("crdb_version") != "" { 94 t.Skip(msg) 95 } 96 } 97 98 func skipPostgreSQLVersionLessThan(t testing.TB, minVersion int64) { 99 conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE")) 100 if err != nil { 101 t.Fatal(err) 102 } 103 defer conn.Close(context.Background()) 104 105 serverVersionStr := conn.PgConn().ParameterStatus("server_version") 106 serverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr) 107 // if not PostgreSQL do nothing 108 if serverVersionStr == "" { 109 return 110 } 111 112 serverVersion, err := strconv.ParseInt(serverVersionStr, 10, 64) 113 require.NoError(t, err) 114 115 if serverVersion < minVersion { 116 t.Skipf("Test requires PostgreSQL v%d+", minVersion) 117 } 118 } 119 120 // sqlScannerFunc lets an arbitrary function be used as a sql.Scanner. 121 type sqlScannerFunc func(src any) error 122 123 func (f sqlScannerFunc) Scan(src any) error { 124 return f(src) 125 } 126 127 // driverValuerFunc lets an arbitrary function be used as a driver.Valuer. 128 type driverValuerFunc func() (driver.Value, error) 129 130 func (f driverValuerFunc) Value() (driver.Value, error) { 131 return f() 132 } 133 134 func TestMapScanNilIsNoOp(t *testing.T) { 135 m := pgtype.NewMap() 136 137 err := m.Scan(pgtype.TextOID, pgx.TextFormatCode, []byte("foo"), nil) 138 assert.NoError(t, err) 139 } 140 141 func TestMapScanTextFormatInterfacePtr(t *testing.T) { 142 m := pgtype.NewMap() 143 var got any 144 err := m.Scan(pgtype.TextOID, pgx.TextFormatCode, []byte("foo"), &got) 145 require.NoError(t, err) 146 assert.Equal(t, "foo", got) 147 } 148 149 func TestMapScanTextFormatNonByteaIntoByteSlice(t *testing.T) { 150 m := pgtype.NewMap() 151 var got []byte 152 err := m.Scan(pgtype.JSONBOID, pgx.TextFormatCode, []byte("{}"), &got) 153 require.NoError(t, err) 154 assert.Equal(t, []byte("{}"), got) 155 } 156 157 func TestMapScanBinaryFormatInterfacePtr(t *testing.T) { 158 m := pgtype.NewMap() 159 var got any 160 err := m.Scan(pgtype.TextOID, pgx.BinaryFormatCode, []byte("foo"), &got) 161 require.NoError(t, err) 162 assert.Equal(t, "foo", got) 163 } 164 165 func TestMapScanUnknownOIDToStringsAndBytes(t *testing.T) { 166 unknownOID := uint32(999999) 167 srcBuf := []byte("foo") 168 m := pgtype.NewMap() 169 170 var s string 171 err := m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &s) 172 assert.NoError(t, err) 173 assert.Equal(t, "foo", s) 174 175 var rs _string 176 err = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &rs) 177 assert.NoError(t, err) 178 assert.Equal(t, "foo", string(rs)) 179 180 var b []byte 181 err = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &b) 182 assert.NoError(t, err) 183 assert.Equal(t, []byte("foo"), b) 184 185 var rb _byteSlice 186 err = m.Scan(unknownOID, pgx.TextFormatCode, srcBuf, &rb) 187 assert.NoError(t, err) 188 assert.Equal(t, []byte("foo"), []byte(rb)) 189 } 190 191 func TestMapScanPointerToNilStructDoesNotCrash(t *testing.T) { 192 m := pgtype.NewMap() 193 194 type myStruct struct{} 195 var p *myStruct 196 err := m.Scan(0, pgx.TextFormatCode, []byte("(foo,bar)"), &p) 197 require.NotNil(t, err) 198 } 199 200 func TestMapScanUnknownOIDTextFormat(t *testing.T) { 201 m := pgtype.NewMap() 202 203 var n int32 204 err := m.Scan(0, pgx.TextFormatCode, []byte("123"), &n) 205 assert.NoError(t, err) 206 assert.EqualValues(t, 123, n) 207 } 208 209 func TestMapScanUnknownOIDIntoSQLScanner(t *testing.T) { 210 m := pgtype.NewMap() 211 212 var s sql.NullString 213 err := m.Scan(0, pgx.TextFormatCode, []byte(nil), &s) 214 assert.NoError(t, err) 215 assert.Equal(t, "", s.String) 216 assert.False(t, s.Valid) 217 } 218 219 type scannerString string 220 221 func (ss *scannerString) Scan(v any) error { 222 *ss = scannerString("scanned") 223 return nil 224 } 225 226 // https://github.com/jackc/pgtype/issues/197 227 func TestMapScanUnregisteredOIDIntoRenamedStringSQLScanner(t *testing.T) { 228 m := pgtype.NewMap() 229 230 var s scannerString 231 err := m.Scan(unregisteredOID, pgx.TextFormatCode, []byte(nil), &s) 232 assert.NoError(t, err) 233 assert.Equal(t, "scanned", string(s)) 234 } 235 236 type pgCustomInt int64 237 238 func (ci *pgCustomInt) Scan(src interface{}) error { 239 *ci = pgCustomInt(src.(int64)) 240 return nil 241 } 242 243 func TestScanPlanBinaryInt32ScanScanner(t *testing.T) { 244 m := pgtype.NewMap() 245 src := []byte{0, 42} 246 var v pgCustomInt 247 248 plan := m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &v) 249 err := plan.Scan(src, &v) 250 require.NoError(t, err) 251 require.EqualValues(t, 42, v) 252 253 ptr := new(pgCustomInt) 254 plan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr) 255 err = plan.Scan(src, &ptr) 256 require.NoError(t, err) 257 require.EqualValues(t, 42, *ptr) 258 259 ptr = new(pgCustomInt) 260 err = plan.Scan(nil, &ptr) 261 require.NoError(t, err) 262 assert.Nil(t, ptr) 263 264 ptr = nil 265 plan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr) 266 err = plan.Scan(src, &ptr) 267 require.NoError(t, err) 268 require.EqualValues(t, 42, *ptr) 269 270 ptr = nil 271 plan = m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, &ptr) 272 err = plan.Scan(nil, &ptr) 273 require.NoError(t, err) 274 assert.Nil(t, ptr) 275 } 276 277 // Test for https://github.com/jackc/pgtype/issues/164 278 func TestScanPlanInterface(t *testing.T) { 279 m := pgtype.NewMap() 280 src := []byte{0, 42} 281 var v interface{} 282 plan := m.PlanScan(pgtype.Int2OID, pgtype.BinaryFormatCode, v) 283 err := plan.Scan(src, v) 284 assert.Error(t, err) 285 } 286 287 func TestPointerPointerStructScan(t *testing.T) { 288 m := pgtype.NewMap() 289 type composite struct { 290 ID int 291 } 292 293 int4Type, _ := m.TypeForOID(pgtype.Int4OID) 294 pgt := &pgtype.Type{ 295 Codec: &pgtype.CompositeCodec{ 296 Fields: []pgtype.CompositeCodecField{ 297 { 298 Name: "id", 299 Type: int4Type, 300 }, 301 }, 302 }, 303 Name: "composite", 304 OID: 215333, 305 } 306 m.RegisterType(pgt) 307 308 var c *composite 309 plan := m.PlanScan(pgt.OID, pgtype.TextFormatCode, &c) 310 err := plan.Scan([]byte("(1)"), &c) 311 require.NoError(t, err) 312 require.Equal(t, c.ID, 1) 313 } 314 315 // https://github.com/jackc/pgx/issues/1263 316 func TestMapScanPtrToPtrToSlice(t *testing.T) { 317 m := pgtype.NewMap() 318 src := []byte("{foo,bar}") 319 var v *[]string 320 plan := m.PlanScan(pgtype.TextArrayOID, pgtype.TextFormatCode, &v) 321 err := plan.Scan(src, &v) 322 require.NoError(t, err) 323 require.Equal(t, []string{"foo", "bar"}, *v) 324 } 325 326 func TestMapScanPtrToPtrToSliceOfStruct(t *testing.T) { 327 type Team struct { 328 TeamID int 329 Name string 330 } 331 332 // Have to use binary format because text format doesn't include type information. 333 m := pgtype.NewMap() 334 src := []byte{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc9, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x31, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x32} 335 var v *[]Team 336 plan := m.PlanScan(pgtype.RecordArrayOID, pgtype.BinaryFormatCode, &v) 337 err := plan.Scan(src, &v) 338 require.NoError(t, err) 339 require.Equal(t, []Team{{1, "team 1"}, {2, "team 2"}}, *v) 340 } 341 342 type databaseValuerString string 343 344 func (s databaseValuerString) Value() (driver.Value, error) { 345 return fmt.Sprintf("%d", len(s)), nil 346 } 347 348 // https://github.com/jackc/pgx/issues/1319 349 func TestMapEncodeTextFormatDatabaseValuerThatIsRenamedSimpleType(t *testing.T) { 350 m := pgtype.NewMap() 351 src := databaseValuerString("foo") 352 buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil) 353 require.NoError(t, err) 354 require.Equal(t, "3", string(buf)) 355 } 356 357 type databaseValuerFmtStringer string 358 359 func (s databaseValuerFmtStringer) Value() (driver.Value, error) { 360 return nil, nil 361 } 362 363 func (s databaseValuerFmtStringer) String() string { 364 return "foobar" 365 } 366 367 // https://github.com/jackc/pgx/issues/1311 368 func TestMapEncodeTextFormatDatabaseValuerThatIsFmtStringer(t *testing.T) { 369 m := pgtype.NewMap() 370 src := databaseValuerFmtStringer("") 371 buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil) 372 require.NoError(t, err) 373 require.Nil(t, buf) 374 } 375 376 type databaseValuerStringFormat struct { 377 n int32 378 } 379 380 func (v databaseValuerStringFormat) Value() (driver.Value, error) { 381 return fmt.Sprint(v.n), nil 382 } 383 384 func TestMapEncodeBinaryFormatDatabaseValuerThatReturnsString(t *testing.T) { 385 m := pgtype.NewMap() 386 src := databaseValuerStringFormat{n: 42} 387 buf, err := m.Encode(pgtype.Int4OID, pgtype.BinaryFormatCode, src, nil) 388 require.NoError(t, err) 389 require.Equal(t, []byte{0, 0, 0, 42}, buf) 390 } 391 392 // https://github.com/jackc/pgx/issues/1445 393 func TestMapEncodeDatabaseValuerThatReturnsStringIntoUnregisteredTypeTextFormat(t *testing.T) { 394 m := pgtype.NewMap() 395 buf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, driverValuerFunc(func() (driver.Value, error) { return "foo", nil }), nil) 396 require.NoError(t, err) 397 require.Equal(t, []byte("foo"), buf) 398 } 399 400 // https://github.com/jackc/pgx/issues/1445 401 func TestMapEncodeDatabaseValuerThatReturnsByteSliceIntoUnregisteredTypeTextFormat(t *testing.T) { 402 m := pgtype.NewMap() 403 buf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, driverValuerFunc(func() (driver.Value, error) { return []byte{0, 1, 2, 3}, nil }), nil) 404 require.NoError(t, err) 405 require.Equal(t, []byte(`\x00010203`), buf) 406 } 407 408 func TestMapEncodeStringIntoUnregisteredTypeTextFormat(t *testing.T) { 409 m := pgtype.NewMap() 410 buf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, "foo", nil) 411 require.NoError(t, err) 412 require.Equal(t, []byte("foo"), buf) 413 } 414 415 func TestMapEncodeByteSliceIntoUnregisteredTypeTextFormat(t *testing.T) { 416 m := pgtype.NewMap() 417 buf, err := m.Encode(unregisteredOID, pgtype.TextFormatCode, []byte{0, 1, 2, 3}, nil) 418 require.NoError(t, err) 419 require.Equal(t, []byte(`\x00010203`), buf) 420 } 421 422 // https://github.com/jackc/pgx/issues/1763 423 func TestMapEncodeNamedTypeOfByteSliceIntoTextTextFormat(t *testing.T) { 424 m := pgtype.NewMap() 425 buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, json.RawMessage(`{"foo": "bar"}`), nil) 426 require.NoError(t, err) 427 require.Equal(t, []byte(`{"foo": "bar"}`), buf) 428 } 429 430 // https://github.com/jackc/pgx/issues/1326 431 func TestMapScanPointerToRenamedType(t *testing.T) { 432 srcBuf := []byte("foo") 433 m := pgtype.NewMap() 434 435 var rs *_string 436 err := m.Scan(pgtype.TextOID, pgx.TextFormatCode, srcBuf, &rs) 437 assert.NoError(t, err) 438 require.NotNil(t, rs) 439 assert.Equal(t, "foo", string(*rs)) 440 } 441 442 // https://github.com/jackc/pgx/issues/1326 443 func TestMapScanNullToWrongType(t *testing.T) { 444 m := pgtype.NewMap() 445 446 var n *int32 447 err := m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &n) 448 assert.NoError(t, err) 449 assert.Nil(t, n) 450 451 var pn pgtype.Int4 452 err = m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &pn) 453 assert.NoError(t, err) 454 assert.False(t, pn.Valid) 455 } 456 457 func TestScanToSliceOfRenamedUint8(t *testing.T) { 458 m := pgtype.NewMap() 459 var ruint8 []_uint8 460 err := m.Scan(pgtype.Int2ArrayOID, pgx.TextFormatCode, []byte("{2,4}"), &ruint8) 461 assert.NoError(t, err) 462 assert.Equal(t, []_uint8{2, 4}, ruint8) 463 } 464 465 func TestMapScanTextToBool(t *testing.T) { 466 tests := []struct { 467 name string 468 src []byte 469 want bool 470 }{ 471 {"t", []byte("t"), true}, 472 {"f", []byte("f"), false}, 473 {"y", []byte("y"), true}, 474 {"n", []byte("n"), false}, 475 {"1", []byte("1"), true}, 476 {"0", []byte("0"), false}, 477 {"true", []byte("true"), true}, 478 {"false", []byte("false"), false}, 479 {"yes", []byte("yes"), true}, 480 {"no", []byte("no"), false}, 481 {"on", []byte("on"), true}, 482 {"off", []byte("off"), false}, 483 } 484 485 for _, tt := range tests { 486 t.Run(tt.name, func(t *testing.T) { 487 m := pgtype.NewMap() 488 489 var v bool 490 err := m.Scan(pgtype.BoolOID, pgx.TextFormatCode, tt.src, &v) 491 require.NoError(t, err) 492 assert.Equal(t, tt.want, v) 493 }) 494 } 495 } 496 497 func TestMapScanTextToBoolError(t *testing.T) { 498 tests := []struct { 499 name string 500 src []byte 501 want string 502 }{ 503 {"nil", nil, "cannot scan NULL into *bool"}, 504 {"empty", []byte{}, "cannot scan empty string into *bool"}, 505 {"foo", []byte("foo"), "unknown boolean string representation \"foo\""}, 506 } 507 508 for _, tt := range tests { 509 t.Run(tt.name, func(t *testing.T) { 510 m := pgtype.NewMap() 511 512 var v bool 513 err := m.Scan(pgtype.BoolOID, pgx.TextFormatCode, tt.src, &v) 514 require.ErrorContains(t, err, tt.want) 515 }) 516 } 517 } 518 519 type databaseValuerUUID [16]byte 520 521 func (v databaseValuerUUID) Value() (driver.Value, error) { 522 return fmt.Sprintf("%x", v), nil 523 } 524 525 // https://github.com/jackc/pgx/issues/1502 526 func TestMapEncodePlanCacheUUIDTypeConfusion(t *testing.T) { 527 expected := []byte{ 528 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0xb, 0x86, 0, 0, 0, 2, 0, 0, 0, 1, 529 0, 0, 0, 16, 530 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 531 0, 0, 0, 16, 532 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} 533 534 m := pgtype.NewMap() 535 buf, err := m.Encode(pgtype.UUIDArrayOID, pgtype.BinaryFormatCode, 536 []databaseValuerUUID{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}}, 537 nil) 538 require.NoError(t, err) 539 require.Equal(t, expected, buf) 540 541 // This actually *should* fail. In the actual query path this error is detected and the encoding falls back to the 542 // text format. In the bug this test is guarding against regression this would panic. 543 _, err = m.Encode(pgtype.UUIDArrayOID, pgtype.BinaryFormatCode, 544 []string{"00010203-0405-0607-0809-0a0b0c0d0e0f", "0f0e0d0c-0b0a-0908-0706-0504-03020100"}, 545 nil) 546 require.Error(t, err) 547 } 548 549 // https://github.com/jackc/pgx/issues/1763 550 func TestMapEncodeRawJSONIntoUnknownOID(t *testing.T) { 551 m := pgtype.NewMap() 552 buf, err := m.Encode(0, pgtype.TextFormatCode, json.RawMessage(`{"foo": "bar"}`), nil) 553 require.NoError(t, err) 554 require.Equal(t, []byte(`{"foo": "bar"}`), buf) 555 } 556 557 func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) { 558 m := pgtype.NewMap() 559 src := []byte{0, 0, 0, 42} 560 var v pgtype.Int4 561 562 for i := 0; i < b.N; i++ { 563 v = pgtype.Int4{} 564 err := m.Scan(pgtype.Int4OID, pgtype.BinaryFormatCode, src, &v) 565 if err != nil { 566 b.Fatal(err) 567 } 568 if v != (pgtype.Int4{Int32: 42, Valid: true}) { 569 b.Fatal("scan failed due to bad value") 570 } 571 } 572 } 573 574 func BenchmarkMapScanInt4IntoGoInt32(b *testing.B) { 575 m := pgtype.NewMap() 576 src := []byte{0, 0, 0, 42} 577 var v int32 578 579 for i := 0; i < b.N; i++ { 580 v = 0 581 err := m.Scan(pgtype.Int4OID, pgtype.BinaryFormatCode, src, &v) 582 if err != nil { 583 b.Fatal(err) 584 } 585 if v != 42 { 586 b.Fatal("scan failed due to bad value") 587 } 588 } 589 } 590 591 func BenchmarkScanPlanScanInt4IntoBinaryDecoder(b *testing.B) { 592 m := pgtype.NewMap() 593 src := []byte{0, 0, 0, 42} 594 var v pgtype.Int4 595 596 plan := m.PlanScan(pgtype.Int4OID, pgtype.BinaryFormatCode, &v) 597 598 for i := 0; i < b.N; i++ { 599 v = pgtype.Int4{} 600 err := plan.Scan(src, &v) 601 if err != nil { 602 b.Fatal(err) 603 } 604 if v != (pgtype.Int4{Int32: 42, Valid: true}) { 605 b.Fatal("scan failed due to bad value") 606 } 607 } 608 } 609 610 func BenchmarkScanPlanScanInt4IntoGoInt32(b *testing.B) { 611 m := pgtype.NewMap() 612 src := []byte{0, 0, 0, 42} 613 var v int32 614 615 plan := m.PlanScan(pgtype.Int4OID, pgtype.BinaryFormatCode, &v) 616 617 for i := 0; i < b.N; i++ { 618 v = 0 619 err := plan.Scan(src, &v) 620 if err != nil { 621 b.Fatal(err) 622 } 623 if v != 42 { 624 b.Fatal("scan failed due to bad value") 625 } 626 } 627 } 628 629 func isExpectedEq(a any) func(any) bool { 630 return func(v any) bool { 631 return a == v 632 } 633 }