【问题标题】:How to parametrize a dynamic query in Go如何在 Go 中对动态查询进行参数化
【发布时间】:2021-12-30 14:34:26
【问题描述】:

用户可以根据许多不同的标准请求产品价格,这将导致它可能访问表中的不同列。我正在遍历请求的产品并构建一堆查询,但遇到了一些麻烦。

逐个运行它们并组合结果比合并它们需要更长的时间。所以我尝试构建如下查询,它可以工作且速度很快,但现在很容易被注入。

没有联盟有没有更好的方法来做到这一点?或者有没有一种简单的方法可以像这样对动态查询进行参数化?

    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 等,但我认为我不能在这里,因为我不知道会有多少参数并且它们都需要与众不同。

【问题讨论】:

  • 这真的是一个关于如何使用 github.com/jmoiron/sqlx 包(在你的问题中有一点点了解这个事实)和/或(一些未命名的)用于 Go 的 PostgreSQL 驱动程序(基于在问题标签上)?请更具体。
  • 您不能在 SQL 中参数化列名。但是无论如何,列名更容易在后端代码中清理,因为所有名称都是预先知道的。它们没有像真实值这样的动态值。因此,您可以制作具有已知有效列名的地图,并查看用户输入是否在该地图中。如果它在地图中,您知道它是有效的并且不会造成任何伤害,因为只允许选择一小部分预定义值(列名)。
  • 或者你尝试像这样stackoverflow.com/a/30120527/9208887。所以我说你不能参数化列名的说法并不完全正确。我不确定包含多个这样的列名有多容易。
  • @TheFool 澄清一下,我不是试图参数化列名,而是产品 ID 和商店值。 (%s 替换)。我会用更多信息更新最初的问题。
  • 您肯定是在参数化列名。或者你会怎么称呼这个SELECT price_+fmt.Sprint(p.type)+?如果你没有正确清理 p.type,那么你就有一个潜在的注入漏洞。

标签: postgresql go prepared-statement psql


【解决方案1】:

想出一个粗略的未经测试的例子,说明我如何最终做到这一点,以防其他人遇到这种情况。

 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-13
    • 1970-01-01
    • 2019-09-22
    • 1970-01-01
    • 1970-01-01
    • 2013-05-14
    • 2017-11-29
    相关资源
    最近更新 更多