with - using postgresql golang




Cláusula Go e IN no Postgres (2)

Pré-construindo a Consulta SQL (Evitando a Injeção SQL)

Se você estiver gerando uma string SQL com um espaço reservado param para cada um dos valores, é mais fácil gerar o SQL final imediatamente.

Observe que, como os valores são string s, há lugar para o ataque de injeção de SQL, então testamos primeiro se todos os valores da string são realmente números e só prosseguimos se:

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(")")

Executando:

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

Usando ANY

Você também pode usar ANY do Postgresql , cuja sintaxe é a seguinte:

expression operator ANY (array expression)

Usando isso, nossa consulta pode ficar assim:

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

Nesse caso, você pode declarar o formato de texto da matriz como um parâmetro:

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

Que pode simplesmente ser construído assim:

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

Observe que, neste caso, nenhuma verificação é necessária, pois a expressão da matriz não permitirá a injeção de SQL (mas resultará em um erro de execução da consulta).

Então, o código completo:

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)
}

Estou tentando executar a seguinte consulta no banco de dados PostgreSQL no Go usando o driver pq :

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

onde 1, 2, 3 é passado em uma tags := []string{"1", "2", "3"} fatia tags := []string{"1", "2", "3"} .

Eu tentei muitas coisas diferentes, como:

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)
}

que resulta em pq: syntax error at or near "$1" . Eu também tentei

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

que também falha com pq: invalid input syntax for integer: "1,2,3"

Eu também tentei passar uma fatia de números inteiros / strings diretamente e obtive sql: converting Exec argument #0's type: unsupported type []string, a slice .

Então, como posso executar esta consulta no Go?


Este não é realmente um problema de Golang, você está usando uma string para comparar com o número inteiro (id) na sua solicitação SQL. Isso significa que o SQL recebe:

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

em vez do que você deseja dar. Você só precisa converter suas tags em número inteiro e passá-las para a consulta.

EDIT: Como você está tentando passar vários valores para a consulta, você deve informar:

params := make([]string, 0, len(tags))
for i := range tags {
    params = append(params, fmt.Sprintf("$%d", i+1))
}
query := fmt.Sprintf("SELECT COUNT(id) FROM tags WHERE id IN (%s)", strings.Join(params, ", "))

Isso encerrará a consulta com um "($ 1, $ 2, $ 3 ..." e depois converterá suas tags como int:

values := make([]int, 0, len(tags))
for _, s := range tags {
    val, err := strconv.Atoi(s)
    if err != nil {
        // Do whatever is required with the error
        fmt.Println("Err : ", err)
    } else {
        values = append(values, val)
    }
}

E, finalmente, você pode usá-lo na consulta:

Db.QueryRow(query, values...)

Isso deve servir.





go