71

我有以下代码,使用 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)

这输出:

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;

当我执行此操作时,它会倒下 - 这是可以理解的,因为表名周围的引号是非法的。

有没有办法合法地将表名作为参数传递,或者我需要做一个(明确警告)字符串连接,即:

voyage = 'ss2012_t02'
sql = 'select %s from ' + voyage + ' where utctime > %s and utctime < %s order by utctime asc;'

为任何见解干杯。

4

9 回答 9

100

根据官方文档:

如果您需要动态生成 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])

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

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

此外,根据 psycopg2 文档:

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

于 2017-03-22T09:37:00.463 回答
32

根据这个答案,您可以这样做:

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")})
于 2015-02-18T20:26:45.763 回答
25

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

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

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

SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' and table_name=%s LIMIT 1
于 2012-12-10T00:29:49.503 回答
5

这是我过去使用的解决方法

query = "INSERT INTO %s (col_1, col_2) VALUES (%%s, %%s)" % table_name
cur.execute(query, (col_1_var, col_2_var))

希望它有帮助:)

于 2017-05-27T21:52:08.050 回答
2

我创建了一个小实用程序,用于预处理带有变量表 (...) 名称的 SQL 语句:

from string import letters
NAMECHARS = frozenset(set(letters).union('.'))

def replace_names(sql, **kwargs):
    """
    Preprocess an SQL statement: securely replace table ... names
    before handing the result over to the database adapter,
    which will take care of the values.

    There will be no quoting of names, because this would make them
    case sensitive; instead it is ensured that no dangerous chars
    are contained.

    >>> replace_names('SELECT * FROM %(table)s WHERE val=%(val)s;',
    ...               table='fozzie')
    'SELECT * FROM fozzie WHERE val=%(val)s;'
    """
    for v in kwargs.values():
        check_name(v)
    dic = SmartDict(kwargs)
    return sql % dic

def check_name(tablename):
    """
    Check the given name for being syntactically valid,
    and usable without quoting
    """
    if not isinstance(tablename, basestring):
        raise TypeError('%r is not a string' % (tablename,))
    invalid = set(tablename).difference(NAMECHARS)
    if invalid:
        raise ValueError('Invalid chars: %s' % (tuple(invalid),))
    for s in tablename.split('.'):
        if not s:
            raise ValueError('Empty segment in %r' % tablename)

class SmartDict(dict):
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            check_name(key)
            return key.join(('%(', ')s'))

SmartDict 对象%(key)s为每个 unknown返回key,保留它们以进行值处理。该函数可以检查是否缺少任何引号字符,因为现在应该注意所有引用...

于 2013-12-13T19:06:17.613 回答
1

如果要将表名作为参数传递,可以使用此包装器:

class Literal(str):
    def __conform__(self, quote):
        return self

    @classmethod
    def mro(cls):
        return (object, )

    def getquoted(self):
        return str(self)

用法:cursor.execute("CREATE TABLE %s ...", (Literal(name), ))

于 2013-08-30T19:07:04.767 回答
1

这是@Antoine Dusséaux 回答的一个小补充。如果要在 SQL 查询中传递两个(不带引号的)参数,可以按如下方式进行:-

query = sql.SQL("select {field} from {table} where {pkey} = %s").format(
    field=sql.Identifier('my_name'),
    table=sql.Identifier('some_table'),
    pkey=sql.Identifier('id'))

根据文档,

通常,您应该将查询的模板表示为带有 {} 样式占位符的 SQL 实例,并使用 format() 将可变部分合并到其中,所有这些都必须是 Composable 子类。您仍然可以在查询中使用 %s 样式的占位符并将值传递给 execute():这样的值占位符将不受 format() 的影响

来源:https ://www.psycopg.org/docs/sql.html#module-usage

另外,在编写查询时请记住一点。

于 2021-04-14T03:11:39.107 回答
0

您可以只使用模块格式作为表名,然后使用常规参数化执行:

xlist = (column, table)
sql = 'select {0} from {1} where utctime > %s and utctime < %s order by utctime asc;'.format(xlist)

请记住,如果这暴露给最终用户,除非您为它编写代码,否则您将不会受到 SQL 注入的保护。

于 2015-12-04T21:56:28.407 回答
-6

很惊讶没有人提到这样做:

sql = 'select {} from {} where utctime > {} and utctime < {} order by utctime asc;'.format(dataItems, voyage, dateRangeLower, dateRangeUpper)
rows = cur.mogrify(sql)

format 放入不带引号的字符串。

于 2015-12-16T14:39:06.517 回答