# 46.7.数据库访问

46.7.1. 数据库访问功能

46.7.2. 陷阱错误

PL/Python语言模块自动导入一个名为plpy。本模块中的函数和常量在Python代码中可用,如下所示:plpy**.

# 46.7.1.数据库访问功能

这个plpy模块提供了几个执行数据库命令的功能:

plpy。处决(*查询* [, *最大行数*])

使命感plpy。处决使用查询字符串和可选的行限制参数可以运行该查询,并在结果对象中返回结果。

结果对象模拟列表或字典对象。结果对象可以通过行号和列名访问。例如:

rv = plpy.execute("SELECT * FROM my_table", 5)

从中返回最多5行我的桌子如果我的桌子他有一个专栏我的专栏,可通过以下方式访问:

foo = rv[i]["my_column"]

返回的行数可以使用内置的伦恩作用

结果对象具有以下附加方法:

``nrows()

返回命令处理的行数。请注意,这不一定与返回的行数相同。例如使现代化命令将设置此值,但不会返回任何行(除非返回(已使用)。

``地位()

这个SPI_execute()返回值。

colnames`()`coltypes()
``coltypmods()

分别返回列名列表、列类型OID列表和列的类型特定类型修饰符列表。

当从未生成结果集的命令调用结果对象时,这些方法会引发异常,例如。,使现代化没有返回升降台.但在包含零行的结果集上使用这些方法是可以的。

``str()

标准__str__方法的定义使得可以使用plpy。调试(rv).

可以修改结果对象。

请注意plpy。处决将导致整个结果集被读入内存。只有在确定结果集相对较小时才使用该功能。如果您不想在获取大型结果时冒内存过度使用的风险,请使用plpy。光标而不是plpy。处决.

plpy。准备(*查询* [, *argtypes*])
plpy。处决(*计划* [, *论据* [, *最大行数*]])

plpy。准备为查询准备执行计划。如果查询中有参数引用,则使用查询字符串和参数类型列表调用它。例如:

plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])

文本是要传递的变量的类型$1。如果不想向查询传递任何参数,则第二个参数是可选的。

在准备一条语句后,使用函数的变体plpy。处决要运行它:

rv = plpy.execute(plan, ["name"], 5)

将计划作为第一个参数(而不是查询字符串)传递,并将要替换到查询中的值列表作为第二个参数传递。如果查询不需要任何参数,则第二个参数是可选的。第三个参数是与前面一样的可选行限制。

或者,你可以打电话给处决计划对象上的方法:

rv = plan.execute(["name"], 5)

查询参数和结果行字段在PostgreSQL和Python数据类型之间转换,如中所述第46.3节.

当您使用PL/Python模块准备计划时,它会自动保存。阅读SPI文档(第47章)来描述这意味着什么。为了在函数调用中有效地利用这个功能,需要使用一个持久存储字典SDGD(见第46.4节).例如:

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 plpythonu;

plpy。光标(*查询*)
plpy。光标(*计划* [, *论据*])

这个plpy。光标函数接受的参数与plpy。处决(除了行限制)并返回游标对象,该对象允许您在较小的块中处理较大的结果集。就像plpy。处决,可以使用查询字符串或带有参数列表的计划对象,或者光标函数可以作为plan对象的方法调用。

游标对象提供了一个取来方法,该方法接受整数参数并返回结果对象。每次你打电话取来,返回的对象将包含下一批行,永远不会大于参数值。一旦所有的行都用完了,取来开始返回空的结果对象。游标对象还提供迭代接口 (opens new window),一次产生一行,直到所有行都用完。以这种方式获取的数据不会作为结果对象返回,而是作为字典返回,每个字典对应一个结果行。

处理大型表中数据的两种方法的示例如下:

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 plpythonu;

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 plpythonu;

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 plpythonu;

游标会自动处理。但是,如果要显式释放游标所持有的所有资源,请使用方法一旦关闭,就无法再从中提取光标。

# 提示

不要混淆由创建的对象plpy。光标使用Python数据库API规范 (opens new window).除了名字,他们没有任何共同之处。

# 46.7.2.陷阱错误

访问数据库的函数可能会遇到错误,这将导致它们中止并引发异常。二者都plpy。处决plpy。准备可以引发plpy。斯皮尔罗,默认情况下将终止该函数。通过使用尝试/例外建筑例如:

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 plpythonu;

引发的异常的实际类对应于导致错误的特定条件。提到表A.1查看可能的条件列表。模块plpy。特例为每个PostgreSQL条件定义一个异常类,从条件名称派生它们的名称。例如,除法变成除法归零, 唯一违反变成独一无二的侵犯, fdw_错误变成弗德沃罗等等每个异常类都继承自斯皮尔罗。这种分离使处理特定错误变得更容易,例如:

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 plpythonu;

请注意,因为plpy。特例模块继承自斯皮尔罗除了子句处理它将捕获任何数据库访问错误。

作为处理不同错误条件的另一种方法,您可以捕获斯皮尔罗异常,并确定除了通过查看sqlstate异常对象的属性。此属性是一个字符串值,包含“SQLSTATE”错误代码。这种方法提供了大致相同的功能