sql – SQL 字符串组成
该模块包含可用于以方便和安全的方式动态生成 SQL 的对象和函数。SQL 标识符(例如表和字段的名称)不能像查询参数一样传递给execute()
方法:
# This will not work
table_name = 'my_table'
cur.execute("INSERT INTO %s VALUES (%s, %s)", [table_name, 10, 20])
SQL 查询应该在参数合并之前组成,例如:
# This works, but it is not optimal
table_name = 'my_table'
cur.execute(
"INSERT INTO %s VALUES (%%s, %%s)" % table_name,
[10, 20])
这种方法可行,但这是一个等待发生的意外:表名可能是无效的 SQL 文字,需要引用;如果表名来自不受信任的来源,则更严重的是安全问题。该名称应使用escape_identifier()
转义:
from psycopg.pq import Escaping
# This works, but it is not optimal
table_name = 'my_table'
cur.execute(
"INSERT INTO %s VALUES (%%s, %%s)" % Escaping.escape_identifier(table_name),
[10, 20])
这现在是安全的,但它有点特别。如果出于某种原因,有必要在查询字符串中包含一个值(与在值中相反),合并规则仍然不同。它也仍然相对危险:如果escape_identifier()
在某处被遗忘,程序通常会运行,但最终会在包含要转义的字符的表名或字段名时崩溃,或者会出现潜在的可利用弱点。
模块psycopg.sql
公开的对象允许动态生成 SQL 语句,将语句的可变部分与查询参数清楚地分开:
from psycopg import sql
cur.execute(
sql.SQL("INSERT INTO {} VALUES (%s, %s)")
.format(sql.Identifier('my_table')),
[10, 20])
通常你应该将查询的模板表达为带有{}
风格的占位符的SQL
实例,并使用format()
将可变部分合并到其中,所有这些都必须是Composable
子类。您仍然可以在查询中使用%s
风格的占位符并将值传递给execute()
:此类值占位符将不会受到format()
的影响:
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'))
结果对象旨在直接传递给游标方法,例如execute()
, executemany()
, copy()
,但也可以使用as_string()
方法将它组合为 Python 字符串查询:
cur.execute(query, (42,))
full_query = query.as_string(cur)
如果您的查询的一部分是可变参数序列,例如以逗号分隔的字段名称列表,您可以使用SQL.join()
方法将它们传递给查询:
query = sql.SQL("SELECT {fields} FROM {table}").format(
fields=sql.SQL(',').join([
sql.Identifier('field1'),
sql.Identifier('field2'),
sql.Identifier('field3'),
]),
table=sql.Identifier('some_table'))
这些sql
对象位于以下继承层次结构中:
Composable
: 公开公共接口的基类
|__
SQL
: SQL 查询的文字片段
|__
Identifier
: PostgreSQL 标识符或点号分隔的标识符序列
|__
Literal
: 硬编码到查询中的值
|__
Placeholder
: 一个%s
风格的占位符,其值将在以后添加,例如通过execute()
|__
Composed
: Composable
实例序列。
class psycopg.sql.Composable
可用于组成 SQL 字符串的对象的抽象基类。
Composable
对象可以直接传递给execute()
, executemany()
, copy()
代替查询字符串。
Composable
对象可以使用运算符+
进行连接:结果将是一个包含连接对象的Composed
实例。运算符*
还支持整数参数:结果是一个Composed
实例,其中包含按要求将左参数重复多次。
abstract as_bytes(context: Optional[AdaptContext]) → bytes
以字节形式返回对象的值。
参数:
context (connection
或 cursor
) – 处理对象的上下文。
如果传递的是Composable
而不是查询字符串,该方法会由 execute()
, executemany()
, copy()
自动调用。
as_string(context: Optional[AdaptContext]) → str
将对象的值作为字符串返回。
参数:
context (connection
或 cursor
) – 处理字符串的上下文。
class psycopg.sql.SQL(obj: LiteralString)
表示 SQL 语句片段的Composable
。
SQL
公开的join()
和format()
方法可用于创建模板,在模板中合并查询的可变部分(例如字段名或表名)。
字符串obj
不进行任何形式的转义,因此它不适合表示变量标识符或值:您应该只用它来传递表示模板或 SQL 语句片段的常量字符串;使用其他对象如Identifier
或Literal
来表示可变部分。
例子:
>>> query = sql.SQL("SELECT {0} FROM {1}").format(
... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
... sql.Identifier('table'))
>>> print(query.as_string(conn))
SELECT "foo", "bar" FROM "table"
在 3.1 版更改: 输入对象应该是一个LiteralString
。有关详细信息,请参阅 PEP 675。
format(*args: Any, **kwargs: Any) → Composed
将Composable
对象合并到模板中。
参数:
- args – 替换为编号 (
{0}
,{1}
) 或自动编号 ({}
) 占位符的参数 - kwargs – 替换为命名 (
{name}
) 占位符的参数
返回:
将SQL
字符串中的占位符使用参数值替换后的组合字符串
返回类型:
该方法类似于 Python 提供的str.format()
方法:字符串模板支持自动编号 ({}
)、编号 ({0}
, {1}
…) 和命名占位符 ({name}
),用位置参数替换编号占位符,用关键字替换命名占位符。但是,不支持占位符修饰符 ({0!r}
, {0:<10}
)。
如果将Composable
对象传递给模板,它将根据其as_string()
方法进行合并。如果传递任何其他 Python 对象,它将被包装在一个Literal
对象中,因此会根据 SQL 规则进行转义。
例子:
>>> print(sql.SQL("SELECT * FROM {} WHERE {} = %s")
... .format(sql.Identifier('people'), sql.Identifier('id'))
... .as_string(conn))
SELECT * FROM "people" WHERE "id" = %s
>>> print(sql.SQL("SELECT * FROM {tbl} WHERE name = {name}")
... .format(tbl=sql.Identifier('people'), name="O'Rourke"))
... .as_string(conn))
SELECT * FROM "people" WHERE name = 'O''Rourke'
join(seq: Iterable[Composable]) → Composed
连接Composable
对象序列。
参数:
seq (可迭代的Composable
) – 要连接的元素。
使用SQL
对象的字符串来分隔seq
。请注意,Composed
对象也是可迭代的,因此它们可以用作此方法的参数。
例子:
>>> snip = sql.SQL(', ').join(
... sql.Identifier(n) for n in ['foo', 'bar', 'baz'])
>>> print(snip.as_string(conn))
"foo", "bar", "baz"
class psycopg.sql.Identifier(*strings: str)
表示 SQL 标识符或点号分隔的标识符序列的Composable
。
标识符通常表示数据库对象的名称,例如表或字段。PostgreSQL 标识符遵循与 SQL 字符串文字不同的转义规则(例如,它们使用双引号而不是单引号)。
例子:
>>> t1 = sql.Identifier("foo")
>>> t2 = sql.Identifier("ba'r")
>>> t3 = sql.Identifier('ba"z')
>>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
"foo", "ba'r", "ba""z"
可以将多个字符串传递给对象以表示限定名称,即以点号分隔的标识符序列。
例子:
>>> query = sql.SQL("SELECT {} FROM {}").format(
... sql.Identifier("table", "field"),
... sql.Identifier("schema", "table"))
>>> print(query.as_string(conn))
SELECT "table"."field" FROM "schema"."table"
class psycopg.sql.Literal(obj: Any)
表示要包含在查询中的 SQL 值的Composable
。
通常您会希望在查询中包含占位符并将值作为参数传递给execute()
。但是,如果您确实确实需要在查询中包含文字值,则可以使用此对象。
使用as_string()
返回的字符串遵循 Python 对象的正常适配规则。
例子:
>>> s1 = sql.Literal("fo'o")
>>> s2 = sql.Literal(42)
>>> s3 = sql.Literal(date(2000, 1, 1))
>>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))
'fo''o', 42, '2000-01-01'::date
*在 3.1 版更改:*如果在不明确的上下文中有用,则向表示添加类型转换(例如'2000-01-01'::date
)。
class psycopg.sql.Placeholder(name: str = '', format: Union[str, PyFormat] = PyFormat.AUTO)
表示查询参数的占位符的Composable
。
如果指定了名称,则生成命名占位符(例如%(name)s
, %(name)b
),否则生成位置占位符(例如%s
, %b
)。
该对象可用于生成具有可变数量参数的 SQL 查询。
例子:
>>> names = ['foo', 'bar', 'baz']
>>> q1 = sql.SQL("INSERT INTO my_table ({}) VALUES ({})").format(
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(sql.Placeholder() * len(names)))
>>> print(q1.as_string(conn))
INSERT INTO my_table ("foo", "bar", "baz") VALUES (%s, %s, %s)
>>> q2 = sql.SQL("INSERT INTO my_table ({}) VALUES ({})").format(
... sql.SQL(', ').join(map(sql.Identifier, names)),
... sql.SQL(', ').join(map(sql.Placeholder, names)))
>>> print(q2.as_string(conn))
INSERT INTO my_table ("foo", "bar", "baz") VALUES (%(foo)s, %(bar)s, %(baz)s)
class psycopg.sql.Composed(seq: Sequence[Any])
由Composable
序列组成的Composable
对象。
通常使用Composable
的运算符和方法创建该对象。然而,也可以直接指定一系列对象作为参数创建一个Composed
的对象:如果它们不是Composable
,它们将被包装成一个Literal
。
例子:
>>> comp = sql.Composed(
... [sql.SQL("INSERT INTO "), sql.Identifier("table")])
>>> print(comp.as_string(conn))
INSERT INTO "table"
Composed
对象是可迭代的(因此它们可以用在SQL.join
这样的方法)。
join(joiner: Union[SQL, LiteralString]) → Composed
将joiner
插入Composed
项之间,返回一个新的Composed
。
例子:
>>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed
>>> print(fields.join(', ').as_string(conn))
"foo", "bar"
psycopg.sql.quote(obj: Any, context: Optional[AdaptContext] = None) → str
适配 Python 对象到带引号的 SQL 字符串。
仅当您绝对想将 Python 字符串转换为带引号的 SQL 文字以用于生成批处理 SQL 时才使用此函数,并且当您需要使用它时您还没有可用的连接。
这个函数效率比较低,因为它不缓存适配规则。如果你传递一个context
,你可以调整使用的适配规则,否则会使用全局的规则。
psycopg.sql.NULL
经常用在查询中的sql.SQL
对象。
psycopg.sql.DEFAULT
经常用在查询中的sql.SQL
对象。