我正在try 使用pq driver对围棋中的PostgreSQL数据库执行以下查询:

SELECT COUNT(id)
FROM tags
WHERE id IN (1, 2, 3)

其中1, 2, 3在切片tags := []string{"1", "2", "3"}处通过.

我try 过很多不同的方法,比如:

s := "(" + strings.Join(tags, ",") + ")"
if err := Db.QueryRow(`
    SELECT COUNT(id)
    FROM tags
    WHERE id IN $1`, s,
).Scan(&num); err != nil {
    log.Println(err)
}

结果是pq: syntax error at or near "$1".我也试过了

if err := Db.QueryRow(`
    SELECT COUNT(id)
    FROM tags
    WHERE id IN ($1)`, strings.Join(stringTagIds, ","),
).Scan(&num); err != nil {
    log.Println(err)
}

对于pq: invalid input syntax for integer: "1,2,3",它也失败了

我还try 直接传递一个整数/字符串片段,得到了sql: converting Exec argument #0's type: unsupported type []string, a slice.

那么我如何在Go中执行这个查询呢?

推荐答案

预先构建SQL查询(防止SQL注入)

如果要 for each 值生成一个带有参数占位符的SQL字符串,那么立即生成最终的SQL就更容易了.

请注意,由于值是string,因此SQL注入攻击是有空间的,所以我们首先测试string个值是否都是数字,并且只有在是的情况下才继续:

tags := []string{"1", "2", "3"}
buf := bytes.NewBufferString("SELECT COUNT(id) FROM tags WHERE id IN(")
for i, v := range tags {
    if i > 0 {
        buf.WriteString(",")
    }
    if _, err := strconv.Atoi(v); err != nil {
        panic("Not number!")
    }
    buf.WriteString(v)
}
buf.WriteString(")")

执行它:

num := 0
if err := Db.QueryRow(buf.String()).Scan(&num); err != nil {
    log.Println(err)
}

Using ANY

也可以使用Postgresql's ANY,其语法如下:

expression operator ANY (array expression)

使用它,我们的查询可能如下所示:

SELECT COUNT(id) FROM tags WHERE id = ANY('{1,2,3}'::int[])

在这种情况下,可以将数组的文本形式声明为参数:

SELECT COUNT(id) FROM tags WHERE id = ANY($1::int[])

可以这样简单地建造:

tags := []string{"1", "2", "3"}
param := "{" + strings.Join(tags, ",") + "}"

请注意,在这种情况下不需要判断,因为数组表达式不允许SQL注入(而是会导致查询执行错误).

所以完整的代码是:

tags := []string{"1", "2", "3"}

q := "SELECT COUNT(id) FROM tags WHERE id = ANY($1::int[])"
param := "{" + strings.Join(tags, ",") + "}"

num := 0
if err := Db.QueryRow(q, param).Scan(&num); err != nil {
    log.Println(err)
}

Go相关问答推荐

有没有更简单的方法在Go中编写这个逻辑?

Go:嵌入类型不能是类型参数""

在保留额外参数的同时解封YAML

Pulumi-S3-当策略依赖于访问点时,如何将AccesspintPolicy附加到访问点

CGO Linux到Windows交叉编译中的未知类型名称

为什么要立即调用内联函数,而不仅仅是调用其包含的函数?

go中跨域自定义验证的问题

3 字节切片和有符号整数类型之间的转换

使用 Go 解组 SOAP 消息

errors.Wrap 和 errors.WithMessage 有什么区别

如何将元素从一个切片移动到另一个切片

Golang 通过接口反映/迭代{}

Golang 数据库/sql 与 SetMaxOpenConns 挂起

如果服务器在客户端的 gRPC 中不可用,则等待的方法

如何在 GORM 中迭代一个 int 数组

Golang ACMEv2 HTTP-01 挑战不挑战服务器

K8s 算子读取原始数据

在 Go 中将十六进制转换为带符号的 Int

如何迭代在泛型函数中传递的片的并集?

为什么 Go 的构造函数要返回地址?