扩展@edd 的答案,它在有限的能力范围内起作用。
@edd 提供:
import sqlalchemy
engine = sqlalchemy.create_engine(...)
sqlalchemy.String('').literal_processor(dialect=engine.dialect)(value="untrusted value")
如果您的“不受信任的值”是您要执行的查询,这将导致一个双引号字符串包裹一个单引号字符串,如果不去掉引号就无法直接执行,即"'SELECT ...'"。
您可以使用sqlalchemy.Integer().literal_processor 来做同样的事情,但结果不会有额外的内引号,因为它旨在创建像5 这样的整数,而不是像'5' 这样的字符串。所以你的结果只会被引用一次:"SELECT ..."。
我发现这种 Integer 方法有点粗略 - 阅读我的代码的人会知道我为什么要这样做吗?至少对于 psycopg2 来说,有一个更直接、更清晰的方法。
如果您的底层驱动程序是 psycopg2,您可以使用 sqlalchemy 进入驱动程序,获取光标,然后使用 psycopg2 的 cursor.mogrify 绑定和转义您的查询
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, Psycopg2 and Postgresql COPY
并从这个答案中删除:psycopg2 equivalent of mysqldb.escape_string?
我的用例是构建一个查询,然后将其包装在括号中并使用 (SELECT ...) AS temp_some_table 之类的别名,以便将其传递给 PySpark JDBC read。当 SQLAlchemy 构建查询时,它会最小化括号,所以我只能得到SELECT ... AS temp_some_table。我使用上述方法来获得我需要的东西:
cursor = session.connection().connection.cursor()
aliased_query = cursor.mogrify(
f"({query}) AS temp_{model.__tablename__}"
).decode("UTF-8")