2024年9月26日:PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:开发版
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3

44.6. 数据库访问 #

PL/Python 语言模块自动导入一个名为plpy的 Python 模块。此模块中的函数和常量在 Python 代码中可作为plpy.foo使用。

44.6.1. 数据库访问函数 #

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 列表和列的类型特定类型修饰符列表。

当在未产生结果集的命令的结果对象上调用这些方法时,它们会引发异常,例如,没有RETURNINGUPDATEDROP 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 章)以了解这意味着什么。为了在函数调用之间有效地利用它,需要使用持久存储字典SDGD之一(请参阅第 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 游标混淆。除了名称之外,它们没有任何共同之处。

44.6.2. 捕获错误 #

访问数据库的函数可能会遇到错误,这会导致它们中止并引发异常。plpy.executeplpy.prepare都可能引发plpy.SPIError的子类的实例,默认情况下,这将终止函数。可以通过使用try/except构造来处理此错误,就像处理任何其他 Python 异常一样。例如

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变为DivisionByZerounique_violation变为UniqueViolationfdw_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”错误代码的字符串值。此方法提供了大致相同的功能

提交更正

如果您在文档中看到任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用此表单报告文档问题。