将表名作为参数传递给psycopg2

Cal*_*ari 48 python sql postgresql sql-injection psycopg2

我有以下代码,使用pscyopg2:

sql = 'select %s from %s where utctime > %s and utctime < %s order by utctime asc;'
data = (dataItems, voyage, dateRangeLower, dateRangeUpper)
rows = cur.mogrify(sql, data)
Run Code Online (Sandbox Code Playgroud)

这输出:

select 'waterTemp, airTemp, utctime' from 'ss2012_t02' where utctime > '2012-05-03T17:01:35+00:00'::timestamptz and utctime < '2012-05-01T17:01:35+00:00'::timestamptz order by utctime asc;
Run Code Online (Sandbox Code Playgroud)

当我执行它时,它会失败 - 这是可以理解的,因为表名周围的引号是非法的.

有没有办法合法地将表名作为参数传递,或者我是否需要执行(显式警告)字符串连接,即:

voyage = 'ss2012_t02'
sql = 'select %s from ' + voyage + ' where utctime > %s and utctime < %s order by utctime asc;'
Run Code Online (Sandbox Code Playgroud)

为任何见解干杯.

Ant*_*aux 63

根据官方文件:

如果需要动态生成SQL查询(例如动态选择表名),可以使用psycopg2.sql模块提供的功能.

sql模块是psycopg2版本2.7中的新增功能.它具有以下语法:

from psycopg2 import sql

cur.execute(
    sql.SQL("insert into {} values (%s, %s)")
        .format(sql.Identifier('my_table')),
    [10, 20])
Run Code Online (Sandbox Code Playgroud)

更多信息:http://initd.org/psycopg/docs/sql.html#module-psycopg2.sql

[更新2017-03-24:AsIs不应该用于表示表或字段名称,而sql应使用新模块:https://stackoverflow.com/a/42980069/5285608 ]

另外,根据psycopg2文档:

警告:从不,从不,从不使用Python字符串连接(+)或字符串参数插值(%)将变量传递给一个SQL查询字符串.甚至在枪口下也没有.

  • 这是最新的答案 (10认同)
  • 确保在“sql.SQL”对象上调用“.format(...)”,而不是在内部字符串上调用。我只是因为括号错误而损失了一个小时。也就是说,“SQL('select * from {}'.format(...))”不起作用,而“SQL('select * from {}').format(...)”则有效。 (5认同)

jcz*_*lew 30

根据这个答案你可以这样做:

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")})
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,"AsIs"违背了参数化的目的.**不要将**与用户数据一起使用. (5认同)
  • `AsIs`不应该用于此目的:http://stackoverflow.com/a/42980069/5285608 (3认同)
  • @AntoineDusséaux回答如下:stackoverflow.com/a/42947632/1888503是正确的.jczaplew的回答不正确,不应该使用. (2认同)

Pea*_*oto 19

表名不能作为参数传递,但其他一切都可以.因此,表名应该在您的应用程序中进行硬编码(不要输入或使用程序之外的任何名称作为名称).您拥有的代码应该适用于此.

如果您有合理的理由获取外部表名,请确保您不允许用户直接输入它.也许可以传递一个索引来选择一个表,或者可以用其他方式查找表名.但是,你应该警惕这样做.这是有效的,因为周围的表名相对较少.找到一种验证表名的方法,你应该没问题.

可以做这样的事情,看看表名是否存在.这是参数化版本.只需确保执行此操作并在运行SQL代码之前验证输出.对此的部分想法来自这个答案.

SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' and table_name=%s LIMIT 1
Run Code Online (Sandbox Code Playgroud)

  • http://stackoverflow.com/a/42947632/1888503以下答案是正确的. (3认同)
  • 是的,表名确实需要在外部传递,实际上并没有任何解决方法.我确实有一个'有效'(即安全)表的列表,所以我可以对此进行检查,以确保传递的参数是可接受的,以防止注入.. (2认同)