生成SQL "IN" 子句:如何安全处理输入和空值列表?
在我的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)是在运行时决定的。
我有两个担忧:
- 当yValues为空时,生成有效的SQL('WHERE y IN ()'不是有效的SQL)
- 如果这些值来自不可信的来源,要注意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并没有规定如何以这种方式处理序列。