在SQLAlchemy中手动构建SQL查询时如何正确转义字符串?

时间:2014-08-03 11:36:48

标签: python sql sqlalchemy escaping

我使用SQLAlchemy连接到Python中的不同数据库,但没有使用ORM支持,因为有几个原因导致无法实现。

主要是我使用

之类的东西构建复杂的SQL查询
sql += "AND fieldname = '%s'" % myvar

在我的情况下,不是SQL注入的问题,因为数据总是来自受信任的来源,但即使源是可信的,它也可能包含可能破坏查询的字符,如'%_

主要是,我需要逃避它们,我想知道是否有一个已经存在的可以重复使用的转义函数。

5 个答案:

答案 0 :(得分:9)

你不应该尝试实现自己的转义,而应该使用SQLAlchemy的内置方法:

sql = 'select * from foo where fieldname = :name'
result = connection.execute(sql, name = myvar)

答案 1 :(得分:2)

扩展@edd 的答案,其作用有限。

@edd 提供:

import sqlalchemy

engine = sqlalchemy.create_engine(...)
sqlalchemy.String('').literal_processor(dialect=engine.dialect)(value="untrusted value")

如果您的“不受信任的值”是您要执行的查询,这将最终得到一个包含单引号字符串的双引号字符串,如果不去掉引号,您将无法直接执行该字符串,即 {{1} }.

您可以使用 "'SELECT ...'" 来做同样的事情,但结果不会有额外的内引号,因为它旨在创建一个像 sqlalchemy.Integer().literal_processor 这样的整数而不是像 {{1} 这样的字符串}}。所以你的结果只会被引用一次:5

我发现这个整数方法有点粗略 - 阅读我代码的人会知道我为什么要这样做吗?至少对于 psycopg2 来说,有一个更直接、更清晰的方法。

如果您的底层驱动程序是 psycopg2,您可以使用 sqlalchemy 深入驱动程序,获取光标,然后使用 psycopg2 的 '5' 绑定和转义您的查询

"SELECT ..."

我知道如何从这个答案中抓取光标:SQLAlchemy, Psycopg2 and Postgresql COPY

并从这个答案中得到mogrify:psycopg2 equivalent of mysqldb.escape_string?

我的用例是构建一个查询,然后将其包装在括号中并使用 cursor.mogrify 之类的别名,以便将其传递给 PySpark JDBC from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) session = Session() cursor = session.connection().connection.cursor() processed_query = cursor.mogrify([mogrify args, see docs]).decode("UTF-8") 。当 SQLAlchemy 构建查询时,它会最小化括号,因此我只能得到 (SELECT ...) AS temp_some_table。我使用上面的方法来获得我需要的东西:

read

答案 2 :(得分:1)

编译并完成其他贡献者的答案。

写直接SQL字符串通常是一个糟糕的解决方案,因为每个数据库系统都支持自己的SQL方言,因此SQL字符串通常不能跨数据库移植。

为了使用户免除此问题,SQLAlchemy邀请您以更加面向对象的方式在更高级别上编写SQL查询。它被称为SQL Expression语言,并在此处进行记录: https://docs.sqlalchemy.org/en/13/core/tutorial.html

基本上,您可以使用Python构建描述您要执行的操作的表达式,SQLAlchemy会使用适合您所用数据库的方言为您生成相应的SQL字符串。

由于您熟悉SQL,因此您可以在数小时内学会这种“迷你语言”(我很犹豫在此处输入“ s”)。

如果我相信@BrtH,则使用此系统还将为您透明地转义字符串。 转义很难实现,因此,将其留给一个成熟的系统总是比自己尝试做更好。

下面是一个选择子句的随机示例:

from sqlalchemy import select
...
ham_table = meta.tables['ham']
sel = select([ham_table.c.size, ham_table.c.weight]).where(ham_table.c.taste == 'yummy')
result = meta.bind.execute(sel)

不要被“ .c。”甩掉,这只是一个约定,可以帮助他们很好地为您自动化。它们基本上为您生成列描述符,并将它们存储在表对象的.c字段下。

答案 3 :(得分:1)

在必须显式转义字符串且标准工具与要求不符的情况下,您可以要求SQLAlchemy使用引擎的方言转义。

import sqlalchemy


engine = sqlalchemy.create_engine(...)
sqlalchemy.String('').literal_processor(dialect=engine.dialect)(value="untrusted value")

对于我来说,我需要根据用户输入动态创建一个数据库(sqlalchemy-utils具有此功能,但是在我的情况下失败了)。

答案 4 :(得分:0)

您可以使用pymysql中的escape_string方法,然后转义:,以便SQLAlchemy不会尝试为该变量绑定参数,这是示例

import MySQLdb
query = """ insert into.... values("{}"...) """.format(MySQLdb.escape_string(item).replace(':','\:'))

请注意,如果使用这种方式,您的代码容易受到SQL注入的攻击 安装pymysql

pip3 install pymysql