在 psycopg2 中将表名作为参数传递

2025-03-04 08:25:00
admin
原创
78
摘要:问题描述:我有以下代码,使用 pscyopg2:sql = 'select %s from %s where utctime > %s and utctime < %s order by utctime asc;' data = (dataItems, voyage, dateRangeLower...

问题描述:

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

欢迎提出任何见解。


解决方案 1:

根据官方文档:

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

sql模块是 psycopg2 版本 2.7 中的新模块。它具有以下语法:

from psycopg2 import sql

cur.execute(
    sql.SQL("insert into {table} values (%s, %s)")
        .format(table=sql.Identifier('my_table')),
    [10, 20])

更多信息请见:https://www.psycopg.org/docs/sql.html#module-usage

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

此外,根据 psycopg2 文档:

警告:永远、永远、永远不要使用 Python 字符串连接 ( +) 或字符串参数插值 ( %) 将变量传递给 SQL 查询字符串。即使在枪口下也不行。

解决方案 2:

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

import psycopg2
from psycopg2.extensions import AsIs

#Create your connection and cursor...

cursor.execute("SELECT * FROM %(table)s", {"table": AsIs("my_awesome_table")})

解决方案 3:

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

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

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

SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' and table_name=%s LIMIT 1

解决方案 4:

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

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

希望有帮助:)

解决方案 5:

这是对@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

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

解决方案 6:

我创建了一个小实用程序,用于对带有变量表(...)名称的 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每个未知数key,并将它们保存起来以供值处理。该函数可以检查是否存在任何引号字符,因为现在应该处理所有引号...

解决方案 7:

如果您想将表名作为参数传递,您可以使用这个包装器:

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), ))

解决方案 8:

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

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

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

解决方案 9:

如果需要传递合格的标识符,例如架构名称+表名:

from psycopg2 import sql

cur.execute(
    sql.SQL(
        "INSERT INTO {table} VALUES (%s, %s)"
    ).format(
        table=sql.Identifier("my_schema", "my_table")
    ),
    [10, 20]
)

# INSERT INTO "my_schema"."my_table" VALUES (10, 20)

请参阅: https: //www.psycopg.org/docs/sql.html#psycopg2.sql.Identifier

解决方案 10:

令人惊讶的是没有人提到这样做:

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

格式输入的字符串不带引号。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2796  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1710  
  PLM系统在企业项目管理中扮演着至关重要的角色,尤其是在项目采购管理方面,能够通过一系列策略提升采购效率、降低成本并保障质量。通过深入解析相关策略,企业可以更好地利用PLM系统优化采购流程,实现项目的顺利推进与整体目标的达成。需求精准定义策略在项目采购中,明确需求是首要任务。PLM系统可助力企业精准定义采购需求。首先,...
plm是什么意思   8  
  在企业的运营过程中,跨部门数据共享一直是一个关键且颇具挑战的问题。不同部门之间由于业务差异、系统不兼容等多种因素,常常形成信息孤岛,导致数据无法顺畅流通,影响企业整体的决策效率和协同效果。而 PLM 系统作为一种先进的管理工具,为解决这一难题提供了有效的途径。通过其一系列强大的核心功能,能够打破部门之间的数据壁垒,实现...
plm系统   7  
  PLM(产品生命周期管理)项目涉及产品从概念设计到退役的全流程管理,其复杂性和长期性要求高效的项目进度管理工具。甘特图作为一种直观且实用的项目进度可视化工具,在PLM项目中发挥着关键作用。通过甘特图,项目团队成员能够清晰地了解项目任务的时间安排、进度状态以及各项任务之间的关系,从而更好地协调工作、分配资源,确保项目按计...
plm流程是什么   6  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用