我在扫描Golang的pgx个查询时遇到问题.id字段始终是最后一条记录的id字段.如果我取消对函数顶部的var person声明的注释,则每个id都是3.我的数据库中有3条id为1到3的记录.当我对该声明进行注释并在rows.Next()循环中声明变量时,我得到了正确的id.我不明白为什么personId没有被正确覆盖

来自封送JSON的输出,在函数顶部声明var.

[{"person_id":3,"first_name":"Mark","last_name":"Brown"},{"person_id":3,"first_name":"Sam","last_name":"Smith"},{"person_id":3,"first_name":"Bob","last_name":"Jones"}]

在扫描循环的每个迭代中声明person后输出

第一名,最后一名:"{u"姓:"{u"姓:"{u"姓:"{u"姓:"{u":

我有这个 struct

// Person model
type Person struct {
    PersonId       *int64   `json:"person_id"`
    FirstName      *string  `json:"first_name"`
    LastName       *string  `json:"last_name"`
}

这是我的查询函数

func getPersons(rs *appResource, companyId int64) ([]Person, error) {

    //  var person Person

    var persons []Person

    queryString := `SELECT 
      user_id, 
      first_name, 
      last_name,
      FROM users 
      WHERE company_id = $1`

    rows, err := rs.db.Query(context.Background(), queryString, companyId)

    if err != nil {
        return persons, err
    }

    for rows.Next() {

        var person Person

        err = rows.Scan(
            &person.PersonId,
            &person.FirstName,
            &person.LastName)

        if err != nil {
            return persons, err
        }

        log.Println(*person.PersonId) // 1, 2, 3 for both var patterns

        persons = append(persons, person)
    }

    if rows.Err() != nil {
        return persons, rows.Err()
    }

    return persons, err
}

推荐答案

我相信你已经在github.com/jackc/pgx/v4年中发现了一个bug(或者至少是意外行为).当运行Scan时,如果指针(so person.PersonId)不是nil,那么它指向的任何东西都会被重用.为了证明这一点,我复制了这个问题,并确认您也可以通过以下方式解决它:

persons = append(persons, person)
person.PersonId = nil

我可以用这个简化的代码复制这个问题:

conn, err := pgx.Connect(context.Background(), "postgresql://user:password@127.0.0.1:5432/schema?sslmode=disable")
if err != nil {
    panic(err)
}
defer conn.Close(context.Background())

queryString := `SELECT num::int FROM generate_series(1, 3) num`

var scanDst *int64
var slc []*int64

rows, err := conn.Query(context.Background(), queryString)

if err != nil {
    panic(err)
}

for rows.Next() {
    err = rows.Scan(&scanDst)

    if err != nil {
        panic(err)
    }

    slc = append(slc, scanDst)
    // scanDst = nil
}

if rows.Err() != nil {
    panic(err)
}

for _, i := range slc {
    fmt.Printf("%v %d\n", i, *i)
}

这项研究的结果是:

0xc00009f168 3
0xc00009f168 3
0xc00009f168 3

您将注意到,指针在每种情况下都是相同的.我做了一些进一步的测试:

  • 在上面取消注释scanDst = nil可以解决这个问题.
  • 当使用database/sql("github.com/jackc/pgx/stdlib"驱动程序)时,代码按预期工作.
  • 如果PersonId*string(而query使用num::text),它将按预期工作.

convert.go年来,这个问题似乎归结为以下几点:

if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
    el := v.Elem()
    switch el.Kind() {
    // if dst is a pointer to pointer, strip the pointer and try again
    case reflect.Ptr:
        if el.IsNil() {
            // allocate destination
            el.Set(reflect.New(el.Type().Elem()))
        }
        return int64AssignTo(srcVal, srcStatus, el.Interface())

因此,这将处理目标是指针(对于某些数据类型)的情况.代码判断它是否为nil,如果是,则创建一个相关类型的新实例作为目标.如果不是nil,它只会重用指针.注意:我已经有一段时间没有使用reflect了,所以我的解释可能会有问题.

由于该行为与database/sql不同,可能会引起混淆,我认为这可能是一个bug(我猜这可能是试图减少分配).我已经快速查看了这些问题,但没有发现任何报告(稍后将进行更详细的查看).

Go相关问答推荐

如何获得与cksum相同的CRC 32?

gorm插入不支持的数据

无法在Macos上使用Azure Speech golang SDK

为什么(编码器).EncodeElement忽略";,innerxml";标记?

带有一个新变量的Go冒号等于运算符

这种合并排序的实现有什么问题?

Go 中的 protobuf FieldMask 解组

如何从 Asterisk Manager Interface Event 获取活动呼叫数

如何在 Go 中编写示例测试?

访问传递给可变参数函数的通用 struct 的特定字段

如何过滤来自 fsnotify 的重复系统消息

Golang - 将 [8] 布尔转换为字节

为什么时间很短.睡眠时间比基准测试中要求的(约 300 ns)长?

如何优雅地映射到 Go 中返回可变长度数组的方法?

如何使用golang操作很长的字符串以避免内存不足

Go lang - 惯用的默认后备

如何在测试中传递用户名和密码等参数

如何在 Go 中使用 Pact 返回错误请求(400、500)?

Go 赋值涉及到自定义类型的指针

如何从应用程序调用谷歌云身份 API