SQLite“IN”的参数替换条款
我正在尝试使用 Python 中的 SQLite 作为 IN 子句的参数替换。这是一个完整的运行示例,演示了:
import sqlite3
c = sqlite3.connect(":memory:")
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)')
for name in 'Ubuntu Fedora Puppy DSL SuSE'.split():
c.execute('INSERT INTO distro (name) VALUES (?)', [ name ] )
desired_ids = ["1", "2", "5", "47"]
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)), ())
for result in result_set:
print result
它打印出:
(1, u'Ubuntu') (2,u'Fedora') (5,u'SuSE')
由于文档指出“[y]ou 不应该使用 Python 的字符串操作来组装查询,因为这样做是不安全的;它使您的程序容易受到 SQL 注入攻击”,我希望使用参数替换。
当我尝试:
result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ])
我得到一个空结果集,当我尝试:
result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ] )
我得到:
InterfaceError:绑定参数 0 时出错 - 可能不受支持的类型。
虽然我希望这个简化问题的任何答案都能起作用,但我想指出我想要执行的实际查询是在双重嵌套子查询中。也就是说:
UPDATE dir_x_user SET user_revision = user_attempted_revision
WHERE user_id IN
(SELECT user_id FROM
(SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN
("Argl883", "Manf496", "Mook657") GROUP BY user_id
)
)
I am trying to use parameter substitution with SQLite within Python for an IN clause. Here is a complete running example that demonstrates:
import sqlite3
c = sqlite3.connect(":memory:")
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)')
for name in 'Ubuntu Fedora Puppy DSL SuSE'.split():
c.execute('INSERT INTO distro (name) VALUES (?)', [ name ] )
desired_ids = ["1", "2", "5", "47"]
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)), ())
for result in result_set:
print result
It prints out:
(1, u'Ubuntu')
(2, u'Fedora')
(5, u'SuSE')
As the docs state that "[y]ou shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack," I am hoping to use parameter substitution.
When I try:
result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ])
I get an empty result set, and when I try:
result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ] )
I get:
InterfaceError: Error binding parameter 0 - probably unsupported type.
While I hope that any answer to this simplified problem will work, I would like to point out that the actual query I want to perform is in a doubly-nested subquery. To wit:
UPDATE dir_x_user SET user_revision = user_attempted_revision
WHERE user_id IN
(SELECT user_id FROM
(SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN
("Argl883", "Manf496", "Mook657") GROUP BY user_id
)
)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您确实需要正确数量的
?
,但这不会造成 sql 注入风险:You do need the right number of
?
s, but that doesn't pose a sql injection risk:根据http://www.sqlite.org/limits.html(第9项),SQLite无法(默认情况下)处理超过 999 个查询参数,因此如果您要
IN
查找数千个项目,此处的解决方案(生成所需的占位符列表)将会失败。如果是这种情况,您将需要分解列表,然后循环遍历其中的各个部分并自行连接结果。如果您的
IN
子句中不需要数千个项目,那么 Alex 的解决方案就是实现这一目标的方法(并且似乎是 Django 的实现方式)。According to http://www.sqlite.org/limits.html (item 9), SQLite can't (by default) handle more than 999 parameters to a query, so the solutions here (generating the required list of placeholders) will fail if you have thousands of items that you're looking
IN
. If that's the case, you're going to need to break up the list then loop over the parts of it and join up the results yourself.If you don't need thousands of items in your
IN
clause, then Alex's solution is the way to do it (and appears to be how Django does it).更新:这有效:
问题是你需要一个?对于输入列表中的每个元素。
语句
("?," * len(desired_ids))[:-1]
生成一个重复的“?,”字符串,然后截掉最后一个逗号。这样,desired_ids 中的每个元素都有一个问号。Update: this works:
The issue was that you need to have one ? for each element in the input list.
The statement
("?," * len(desired_ids))[:-1]
makes a repeating string of "?,", then cuts off the last comma. so that there is one question mark for each element in desired_ids.我总是最终做这样的事情:
不存在注入风险,因为您没有将desired_ids中的字符串直接放入查询中。
I always end up doing something like this:
There's no injection risk because you're not putting strings from desired_ids into the query directly.
我需要使用一些其他命名参数,因此我开发了两个可能值得分享的辅助函数。
I needed to use some other named parameters so I've developed two helper functions that may be worth sharing.
我编写了一个名为
notanorm
的非常薄的层来解决这个问题。https://pypi.org/project/notanorm/
...然后你的代码看起来像this:
这既易于阅读,又允许您有一天切换数据库。
还可以考虑查看较重的框架: https://www.sqlalchemy.org/
I wrote a very thin layer called
notanorm
to address this issue.https://pypi.org/project/notanorm/
...and then your code looks like this:
Which is both easy to read, and also allows you to switch databases some day.
Also consider looking at the heavier framework: https://www.sqlalchemy.org/