PL/Python 语言模块会自动导入一个名为 plpy
的 Python 模块。此模块中的函数和常量可以在 Python 代码中以 plpy.
的形式使用。foo
plpy
模块提供了一些函数来执行数据库命令。
plpy.execute
(query
[, limit
])
调用带有查询字符串和可选行限制参数的 plpy.execute
会执行该查询,并将结果返回给结果对象。
如果指定了 limit
并且大于零,则 plpy.execute
最多检索 limit
行,这很像查询中包含 LIMIT
子句。省略 limit
或将其指定为零将不会限制行数。
结果对象模拟列表或字典对象。结果对象可以通过行号和列名访问。例如
rv = plpy.execute("SELECT * FROM my_table", 5)
从 my_table
中返回最多 5 行。如果 my_table
有一个名为 my_column
的列,则可以通过以下方式访问它:
foo = rv[i]["my_column"]
可以使用内置的 len
函数获取返回的行数。
结果对象还有以下附加方法:
nrows
()
返回命令处理的行数。请注意,这不一定与返回的行数相同。例如,UPDATE
命令将设置此值,但不会返回任何行(除非使用了 RETURNING
)。
status
()
SPI_execute()
的返回值。
colnames
()
coltypes
()
coltypmods
()
分别返回列名列表、列类型 OID 列表和列的类型特定的类型修饰符列表。
当在不产生结果集的命令(例如,不带 RETURNING
的 UPDATE
或 DROP TABLE
)的结果对象上调用这些方法时,它们会引发异常。但是,在包含零行的结果集上使用这些方法是可以的。
__str__
()
定义了标准的 __str__
方法,以便可以例如使用 plpy.debug(rv)
来调试查询执行结果。
结果对象可以被修改。
请注意,调用 plpy.execute
将导致整个结果集被读入内存。仅当您确定结果集相对较小时才使用此函数。如果您不想在获取大型结果时冒着过度使用内存的风险,请使用 plpy.cursor
而不是 plpy.execute
。
plpy.prepare
(query
[, argtypes
])
plpy.execute
(plan
[, arguments
[, limit
]])
plpy.prepare
准备查询的执行计划。如果查询中有参数引用,则使用查询字符串和参数类型列表进行调用。例如:
plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])
text
是您将为 $1
传递的变量的类型。如果您不想将任何参数传递给查询,则第二个参数是可选的。
准备好语句后,您可以使用 plpy.execute
函数的变体来运行它:
rv = plpy.execute(plan, ["name"], 5)
将计划作为第一个参数(而不是查询字符串),并将要替换到查询中的值列表作为第二个参数传递。如果查询不接受任何参数,则第二个参数是可选的。第三个参数是可选的行限制,与之前相同。
或者,您可以调用计划对象上的 execute
方法:
rv = plan.execute(["name"], 5)
查询参数和结果行字段在 PostgreSQL 和 Python 数据类型之间进行转换,如 第 44.2 节中所述。
当您使用 PL/Python 模块准备计划时,它会自动保存。阅读 SPI 文档(第 45 节)以了解其含义。为了在函数调用之间有效利用这一点,需要使用持久化存储字典 SD
或 GD
(请参阅 第 44.3 节)。例如:
CREATE FUNCTION usesavedplan() RETURNS trigger AS $$ if "plan" in SD: plan = SD["plan"] else: plan = plpy.prepare("SELECT 1") SD["plan"] = plan # rest of function $$ LANGUAGE plpython3u;
plpy.cursor
(query
)
plpy.cursor
(plan
[, arguments
])
plpy.cursor
函数接受与 plpy.execute
相同的参数(行限制除外),并返回一个游标对象,该对象允许您分块处理大型结果集。与 plpy.execute
一样,可以使用查询字符串或计划对象以及参数列表,也可以将 cursor
函数作为计划对象的方法进行调用。
游标对象提供一个 fetch
方法,该方法接受一个整数参数并返回一个结果对象。每次调用 fetch
时,返回的对象将包含下一批行,其大小不超过参数值。一旦所有行都已提取完毕,fetch
将开始返回一个空的结果对象。游标对象还提供一个 迭代器接口,每次生成一行,直到所有行都已提取完毕。以这种方式获取的数据不会作为结果对象返回,而是作为字典返回,每个字典对应一行结果。
处理大型表数据的两种方法的示例是:
CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$ odd = 0 for row in plpy.cursor("select num from largetable"): if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpython3u; CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$ odd = 0 cursor = plpy.cursor("select num from largetable") while True: rows = cursor.fetch(batch_size) if not rows: break for row in rows: if row['num'] % 2: odd += 1 return odd $$ LANGUAGE plpython3u; CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$ odd = 0 plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"]) rows = list(plpy.cursor(plan, [2])) # or: = list(plan.cursor([2])) return len(rows) $$ LANGUAGE plpython3u;
游标会自动处理。但如果您想显式释放游标持有的所有资源,请使用 close
方法。一旦关闭,就不能再从游标中获取数据了。
请不要将 plpy.cursor
创建的对象与 Python 数据库 API 规范定义的 DB-API 游标混淆。它们除了名称之外没有任何共同之处。
访问数据库的函数可能会遇到错误,这些错误会导致它们中止并引发异常。 plpy.execute
和 plpy.prepare
都可以引发 plpy.SPIError
的子类实例,该实例默认会终止函数。此错误可以像任何其他 Python 异常一样处理,方法是使用 try/except
构造。例如:
CREATE FUNCTION try_adding_joe() RETURNS text AS $$ try: plpy.execute("INSERT INTO users(username) VALUES ('joe')") except plpy.SPIError: return "something went wrong" else: return "Joe added" $$ LANGUAGE plpython3u;
引发异常的实际类对应于导致错误的特定条件。有关可能条件的列表,请参阅 表 A.1。plpy.spiexceptions
模块为每个 PostgreSQL 条件定义一个异常类,其名称从条件名称派生。例如,division_by_zero
变为 DivisionByZero
,unique_violation
变为 UniqueViolation
,fdw_error
变为 FdwError
,依此类推。这些异常类都继承自 SPIError
。这种分离使得处理特定错误更加容易,例如:
CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$ from plpy import spiexceptions try: plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"]) plpy.execute(plan, [numerator, denominator]) except spiexceptions.DivisionByZero: return "denominator cannot equal zero" except spiexceptions.UniqueViolation: return "already have that fraction" except plpy.SPIError as e: return "other error, SQLSTATE %s" % e.sqlstate else: return "fraction inserted" $$ LANGUAGE plpython3u;
请注意,由于 plpy.spiexceptions
模块中的所有异常都继承自 SPIError
,因此处理它的 except
子句将捕获任何数据库访问错误。
作为处理不同错误条件的替代方法,您可以捕获 SPIError
异常,并在 except
块中通过查看异常对象的 sqlstate
属性来确定特定的错误条件。此属性是一个字符串值,包含“SQLSTATE”错误代码。此方法提供了大致相同的功能:
如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步说明的内容,请使用 此表单报告文档问题。