生成SQL "IN" 子句:如何安全处理输入和空值列表?

2 投票
4 回答
4449 浏览
提问于 2025-04-15 17:58

在我的Python代码中,我经常会做以下事情(使用DB-API):

yValues = pickInterestingValuesOfY()

sql = "..." # includes a clause stating that "y must be in yValues"

c.execute(sql, yValues)

最后,执行的SQL可能简单到像这样:

SELECT x FROM table1 WHERE y IN (1,2,3);

问题是,y的可能值(1,2,3)是在运行时决定的。

我有两个担忧:

  1. 当yValues为空时,生成有效的SQL('WHERE y IN ()'不是有效的SQL)
  2. 如果这些值来自不可信的来源,要注意SQL注入的问题

为了处理第二个问题,我必须让DB-API实际将yValues插入到SQL语句中。所以我最后得到了以下复杂的解决方案:

def inClause(columnName, values):

    if len(values):
         placeHolders = ','.join( ['%s'] * len(values) )
         sql = "%s IN (%s)" % (columnName, placeHolders)
    else: 
         sql = "FALSE"
    return "(%s)" % sql

# get a db-api cursor called c
c.execute("SELECT x FROM table1 WHERE %s;" % inClause('y', yValues), yValues)

这个方案似乎可以正确解决上面提到的两个问题。然而,我不敢相信这笨拙的解决方案就是我所需要的。

你们是怎么处理这样的查询的?我是不是漏掉了更优雅的做法?

我不想使用ORM。

(我使用的是MySQL,所以如果有某种神奇的MySQL非标准选项可以默默接受'WHERE y IN ()'作为有效的语句,请告诉我,这样第一个问题就解决了。)

4 个回答

0

对于MySQL,你可以这样做:

SELECT x FROM table1 WHERE y IN (SELECT NULL FROM DUAL WHERE 0);

DUAL 是一个虚拟表,专门用来处理这种情况。它适合用来选择那些不需要实际表格的数据。而 WHERE 0 这个条件确保子查询不会返回任何记录。

简单来说,SELECT NULL FROM DUAL WHERE 0 是一个完美的替代空列表的方法。

5

对于 in 这个条件,你只需要总是加一个不会出现在列表里的值(比如一个负数)。

3

不,没什么好办法。数据库API并没有规定如何以这种方式处理序列。

撰写回答