如何在 Go 中参数化动态查询
How to parametrize a dynamic query in Go
用户可以根据许多不同的标准请求产品价格,这将导致它可能访问 table 中的不同列。我正在遍历请求的产品并构建一堆查询,但 运行 遇到了一些麻烦。
运行 将它们一个接一个地组合起来比合并它们花费的时间要长得多。所以我尝试像下面这样构建查询,它可以工作并且速度很快,但现在很容易被注入。
没有联盟,有更好的方法吗?或者有没有一种简单的方法可以像这样参数化动态查询?
var fullQuery string
var counter int
for i, d:= range dataMap{
if counter != 0 {
fullQuery = fullQuery + " UNION "
}
var records string
for _, p := range d{
records = records + `'` + string(p) + `',`
}
recordLength:= len(records)
if recordLength> 0 && records [recordLength-1] == ',' {
records = records[:recordLength-1]
}
counter++
fullQuery = fullQuery + fmt.Sprintf(`
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in (%s) and products.store= %s
`, records, p.store)
}
err := sqlx.Select(db, &dataStruct, fullQuery)
因此,在某些情况下,我可能会有以下查询:
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in (%s) and products.store= %s
而在其他情况下(取决于要求),我可能会有这样的事情:
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in ('testid1', 'testid2') and products.store= 2
UNION
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in ('testid3', 'testid4') and products.store= 1
如果我确定查询是什么,我会只使用 $1、$2 等,但我认为我不能在这里使用,因为我不知道会有多少参数,而且它们都需要不同。
弄明白了,粗略的未经测试的例子说明了我是如何最终做到这一点的,以防其他人遇到这个问题。
var counter int = 1
var parameters []interface{}
for _, d:= range data{
if counter != 1 {
fullQuery = fullQuery + " UNION "
}
fullQuery = fullQuery + fmt.Sprintf(`
SELECT
price_`+fmt.Sprint(d.type)+` as price,
FROM products
WHERE products.id = ANY($%v) and products.store= $%d
`, counter, counter+1)
counter+=2
parameters = append(parameters, pq.Array(d.ids), d.store)
}
err := sqlx.Select(db, &dataStruct, fullQuery, parameters...)
Will still need to validate column names prior to querying to prevent injection.
用户可以根据许多不同的标准请求产品价格,这将导致它可能访问 table 中的不同列。我正在遍历请求的产品并构建一堆查询,但 运行 遇到了一些麻烦。
运行 将它们一个接一个地组合起来比合并它们花费的时间要长得多。所以我尝试像下面这样构建查询,它可以工作并且速度很快,但现在很容易被注入。
没有联盟,有更好的方法吗?或者有没有一种简单的方法可以像这样参数化动态查询?
var fullQuery string
var counter int
for i, d:= range dataMap{
if counter != 0 {
fullQuery = fullQuery + " UNION "
}
var records string
for _, p := range d{
records = records + `'` + string(p) + `',`
}
recordLength:= len(records)
if recordLength> 0 && records [recordLength-1] == ',' {
records = records[:recordLength-1]
}
counter++
fullQuery = fullQuery + fmt.Sprintf(`
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in (%s) and products.store= %s
`, records, p.store)
}
err := sqlx.Select(db, &dataStruct, fullQuery)
因此,在某些情况下,我可能会有以下查询:
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in (%s) and products.store= %s
而在其他情况下(取决于要求),我可能会有这样的事情:
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in ('testid1', 'testid2') and products.store= 2
UNION
SELECT
price_`+fmt.Sprint(p.type)+` as price,
FROM products
WHERE products.id in ('testid3', 'testid4') and products.store= 1
如果我确定查询是什么,我会只使用 $1、$2 等,但我认为我不能在这里使用,因为我不知道会有多少参数,而且它们都需要不同。
弄明白了,粗略的未经测试的例子说明了我是如何最终做到这一点的,以防其他人遇到这个问题。
var counter int = 1
var parameters []interface{}
for _, d:= range data{
if counter != 1 {
fullQuery = fullQuery + " UNION "
}
fullQuery = fullQuery + fmt.Sprintf(`
SELECT
price_`+fmt.Sprint(d.type)+` as price,
FROM products
WHERE products.id = ANY($%v) and products.store= $%d
`, counter, counter+1)
counter+=2
parameters = append(parameters, pq.Array(d.ids), d.store)
}
err := sqlx.Select(db, &dataStruct, fullQuery, parameters...)
Will still need to validate column names prior to querying to prevent injection.