【问题标题】:ORDER BY command using a prepared statement parameter pg-promise使用准备好的语句参数 pg-promise 的 ORDER BY 命令
【发布时间】:2021-07-24 10:33:57
【问题描述】:

我是 Node、API 和 SQL 的新手,我碰壁了。我正在使用pg-promise 准备好的语句,我正在尝试使用sorted 参数和ORDERED BY 命令对结果进行通常的排序,但它不起作用,因为总是按相同的顺序排列。 如果我直接使用参数的名称,它会起作用,你能看到我的查询做错了什么吗? 像往常一样非常感谢您的时间和帮助。 这是我的方法:

if (city, region, country, category, minPrice, maxPrice, orderedBy, sorted) {
    if (sorted == 'asc') {
      await db.any({
        name: 'get-city-category-price-range-ordered-by-asc-products',
        // text: 'SELECT * FROM products WHERE city = $1 AND region = $2 AND country = $3 AND category = $4 AND price >= $5 AND price <= $6 ORDER BY price ASC', // working
        text: 'SELECT * FROM products WHERE city = $1 AND region = $2 AND country = $3 AND category = $4 AND price >= $5 AND price <= $6 ORDER BY $7 ASC', // not working
        values: [city, region, country, category, minPrice, maxPrice, orderedBy]
      })
        .then(result => {
          console.log('get-city-category-price-range-ordered-by-asc-products:', result);
          if (result.length > 0) {
            res.status(200).send({
              data: result
            });
          }
          else {
            res.status(404).send({
              error: 'No product found.'
            });
          }
        })
        .catch(function (error) {
          console.log('get-city-category-price-range-ordered-by-asc-products error:', error);
        });

    } else if (sorted == 'desc') {
      await db.any({
        name: 'get-city-category-price-range-ordered-by-desc-products',
        // text: 'SELECT * FROM products WHERE city = $1 AND region = $2 AND country = $3 AND category = $4 AND price >= $5 AND price <= $6 ORDER BY price DESC', // working
        text: 'SELECT * FROM products WHERE city = $1 AND region = $2 AND country = $3 AND category = $4 AND price >= $5 AND price <= $6 ORDER BY $7 DESC', // not working
        values: [city, region, country, category, minPrice, maxPrice, orderedBy]
      })
        .then(result => {
          console.log('get-city-category-price-range-ordered-by-desc-products:', result);
          if (result.length > 0) {
            res.status(200).send({
              data: result
            });
          }
          else {
            res.status(404).send({
              error: 'No product found.'
            });
          }
        })
        .catch(function (error) {
          console.log('get-city-category-price-range-ordered-by-desc-products error:', error);
        });
    }
  }

【问题讨论】:

    标签: node.js postgresql pg-promise


    【解决方案1】:

    不幸的是,您不能将查询参数用于列名和表名等内容,而只能用于值。所以这永远行不通,您需要通过添加正确的“order by”语句来手动构建 SQL 文本。然后所有关于 SQL 注入的警告都适用,因此请检查您的 orderBy 字段是否有效(例如,仅包含字母,没有空格或其他字符)。

    【讨论】:

    • 感谢您的回答。因为我只有 3 个按案例排序,所以我可以实现一个开关来检查“orderedBy”查询参数并形成正确的 SQL 文本,这对 SQLI 是否足够安全?
    • 是的,只使用常量字符串而不是来自用户的字符串来构建 SQL 会更安全。
    • 当然,orderedBy 参数(与其他参数一样)是从下拉菜单中选择的。只有产品创建确实有用户输入,但pg-promiseandprepared statements 确实负责为 SQLi 转义,对吗?
    【解决方案2】:

    当您选择PreparedStatement 时,您的查询格式是在服务器端完成的。因此,如果您决定使用 PreparedStatement 来实现该目的,以获取服务器格式化的值,那么您将不得不放弃 pg-promise 在内部支持常规查询的丰富查询格式,并将自己限制在 PostgreSQL 服务器上在这方面可以做到。与pg-promise 不同,PostgreSQL 服务器不允许在任何地方使用动态 SQL 名称或标识符。

    但是,如果您选择 PreparedStatement 是因为您认为在您的情况下它会执行得更快,那么您仍然可以从 pg-promise 支持的丰富查询格式中受益,方法是单独格式化查询,然后执行 PreparedStatement预格式化查询,即不带任何参数:

    const text = pgp.as.format(`SELECT * FROM products WHERE city = $/city/
                                AND region = $/region/ AND country = $/country/
                                AND category = $/category/
                                AND price BETWEEN $/minPrice/ AND $/maxPrice/
                                ORDER BY $/orderedBy:name/ $/sorted:value/`, {
                                  city, region, country, category,
                                  minPrice, maxPrice, orderedBy, sorted
                                });
    
    const name = 'get-price-range-ordered-by-products-' + sorted; // unique query name
    
    await db.any({name, text});
    

    如您所见,排序顺序也可以动态设置,无需复制代码。我们将sorted 附加到查询的name 之后,因为它会生成不同的执行路径,这需要唯一的查询名称。

    附:我是pg-promise的作者。

    【讨论】:

    • 再次感谢您的帮助。我实际上是为了 SQLI 保护而选择它,而不是为了执行速度,(你问是因为 PreparedStatements 很慢?我看到了 Performance Boost 文章)。事实上,当我刚刚开始并围绕 sql 概念和 pg-promise 进行思考时,我正在经历一些重复的代码,所以有时我可能会选择更详细的方法并随着我的进展重构代码,就像在这种情况下一样format 方法。再次感谢图书馆。
    • 在 sorted 属性中将 ASC 交换为 DESC 并再次运行请求时,我确实收到了错误 Prepared statements must be unique
    • 那是因为不同的排序顺序在内部会在服务器上创建不同的查询路径,但是您给它相同的名称,因此服务器会抛出错误。请参阅我更新了我的答案,将sorted 附加到查询的name 末尾。
    • 确实,它发生在我的旧代码中,我尝试将它插入到字符串中,如 get-city-category-price-range-ordered-by-${orderedBy}-${sorted}-products 或者但没有工作,因为知道类型的替换失败.. 附加确实有效正如最终所做的那样。
    • 你不能使用{}查询ES6模板字符串as explained in docs
    猜你喜欢
    • 2017-04-17
    • 2016-01-04
    • 2011-02-02
    • 2018-06-03
    • 2011-02-10
    • 2020-04-18
    相关资源
    最近更新 更多