PEP 249 - Python 数据库 API 规范 v2.0
作者:Marc-André Lemburg <mal at lemburg.com>
讨论:Db-SIG list
状态:Final
类型:Informational
创建时间:12-Apr-1999
文章历史:
替代:PEP 248
介绍
这个API定义用于促进访问数据库的Python模块间的相似性。通过这样,我们希望实现一种一致性,其使得模块更易理解,代码在不同数据库间更具可移植性,以及Python对更广泛的数据库的连接支持。
关于该规范的评论和问题由SIG for Database Interfacing with Python直接领导。
更多关于数据库接口和可用包的信息见Database Topic Guide。
该文档描述了Python数据库API 2.0以及一系列通用可选扩展。先前的PEP 248中的1.0版本规范依然可作为参考。鼓励包开发者采用本规范版本作为新接口的基础规范。
模块接口
constructors,构造函数
数据库的访问是通过连接对象来实现的。模块必须为这些连接对象提供以下构造函数:
connect(parameters…)
- 创建数据库连接的构造函数
- 返回一个Connection对象,创建该对象需要一些数据库参数。 [1]
globals,模块全局属性
模块全局变量必须定义:
apilevel
- 字符串常量,指明支持的数据库API版本。
- 当前仅支持
1.0和2.0,若不指定,默认适用1.0版本接口。
threadsafety
整数常量,指明当前接口支持的线程安全级别。可能的值有:
| 线程安全性 | 描述 |
|---|---|
| 0 | 线程不共享模块 |
| 1 | 线程可以共享模块,但连接不共享 |
| 2 | 线程和连接都可以共享模块 |
| 3 | 线程、连接和游标都可以共享模块 |
在上面所属上下文(线程、连接和游标)中共享资源意味着两个线程可以使用一个资源,而无需使用互斥信号量来实现资源锁定。注意,无法总是通过使用互斥手段管理资源访问从而使得外部资源线程安全:资源可能依赖于全局变量或其他控制范围的外部资源。
paramstyle
字符串常量,指明接口期望的参数标记格式化类型。可能的值有:
| 参数标记格式化类型 | 描述 |
|---|---|
| qmark | 问题标记风格,例如... WHERE name=? |
| numberic | 数字位置风格,例如... WHERE name=:1 |
| named | 名称风格,例如... WHERE name=:name |
| format | ANSI C printf格式化代码风格,例如... Where name=%s |
| pyformat | Python扩展格式化代码风格,例如... where name=%(name)s |
exceptions,异常
模块应当通过下方异常或其子类提供错误信息。
Warning
重要的警告,例如,插入数据时的数据截断。其必须是PythonException的子类。[10][11]
Error
所有其他错误异常的基类,可以使用该类及单个except语句捕获所有错误,Warning不被认为是错误,因此不会使用该类作为基类,Error必须是PythonException的子类。[10]
InterfaceError
与数据库接口有关的错误,不是数据库本身的,必须是Error的子类。
DatabaseError
与数据库本身有关的错误,必须是Error的子类。
DataError
由于数据处理问题造成的错误,例如,除零,值超出范围等等,必须是DatabaseError的子类。
OperationError
与数据库操作有关的问题造成的错误,不一定需要在开发者控制范围内,例如,意外断开连接、数据源名称未找到、事务无法处理,以及处理过程中发生内存分配错误等等,必须是DatabaseError的子类。
IntegrityError
数据库完整性约束导致的错误,例如,外键检查失败,必须是DatabaseError的子类。
InternalError
数据库内部错误导致的错误,例如,游标不再有效,事务同步问题等等,必须是DatabaseError的子类。
ProgrammingError
编程问题导致的错误,例如,表找不到或已经存在,SQL语句格式错误,参数数量不对等等,必须是DatabaseError的子类。
NotSupportedError
当使用的方法或数据库API不被数据库支持时的错误,例如,在一个不支持事务或者事务已经关闭的连接上调用rollback(),必须是DatabaseError的子类。
Exception
|__Warning
|__Error
|__InterfaceError
|__DatabaseError
|__DataError
|__OperationalError
|__IntegrityError
|__InternalError
|__ProgrammingError
|__NotSupportedError
[!NOTE]
没有定义这些异常的具体值,但应当能够给用户一个相当好的关于出了什么问题的概念。
Connection对象
Connection对象应当支持如下方法:
Connection方法
close()
- 立即关闭一个连接(不是
__del__()方法被调用的时候) - 从该方法调用之后,连接将不可用。任何使用该连接的操作将会抛出Error或其子类代表的异常,对这个连接的任一cursor的任意操作同理。注意,关闭一个在之前没有提交事务的连接会导致隐式地执行rollback。
commit()
- 提交事务
- 注意,若数据库支持自动提交事务功能,那么应该在最开始时关闭自动提交事务(若希望使用commit()手动提交事务的话)。可能会提供一个接口用于打开自动提交事务。
- 不支持数据库事务的模块应当以空函数实现本方法。
rollback()
- 本方法可选,因为不是所有数据库都提供事务支持。[3]
- 当数据库支持事务时,本方法会使得事务回滚,关闭一个在之前没有提交事务的连接会导致隐式地执行rollback。
cusor()
- 返回一个该连接的新的Cursor对象
- 若数据库本身并不直接提供游标支持,那么模块必须通过其他方法模拟游标以满足规范要求。
Cursor对象
这些对象表示一个数据库游标,游标用于管理获取操作的上下文。从同一连接创建的游标不是隔离的,也就是说,游标对数据库所做的任何更改都会立即被其他游标“看到”。根据事务支持的实现方式,从不同连接创建的游标可以隔离也可以不隔离(另请参阅连接的rollback()和commit()方法)。
Cursor应当支持如下属性和方法:
Cursor属性
description
- 只读属性,一个序列,序列中每个元素都是包含七个元素的序列
- 这些序列中每个序列包含描述一个结果列的信息
- name
- type_code
- display_size
- internal_size
- precision
- scale
- null_ok
- 七元素中前两个元素(
name和type_code)是必须的,另外五个若没有值提供可以设置为None - 对于没有返回结果行的操作,或者游标没有通过
execute*()方法调用执行一个操作时,本属性为None。 type_code可以通过与下一小节Type Object对比来进行解读。
rowcount
- 只读属性,指明先前
execute*()查询(DQL语句,例如SELECT)或者影响(DML语句,例如UPDATE或INSERT)多少行。[9] - 属性值是-1时,表明没有
execute*()在游标上执行,或者最后一个操作该接口无法判断查询或影响行数。[7]
[!NOTE]
数据库API规范的未来版本中会重新定义后一种情况(指最后一个操作该接口无法判断查询或影响行数)返回None而不是-1。
Cursor方法
callproc(procname [, parameters])
- 本方法可选,因为并非所有数据库会提供存储过程支持。[3]
- 使用指定名称调用对应存储过程。参数序列中必须包含存储过程需要的每一个参数。调用结果返回一个输入序列的一个被修改过的副本,其中,input参数原样保留,output参数和input/output参数可能会以新的值替换。
- 存储过程可能会提供一个结果集作为输出,必须随后可以通过
fetch*()方法获取。
close()
- 立即关闭游标(而不是当
__del__被调用时) - 从该方法调用之后,游标将不可用。任何使用该游标的操作将会抛出Error或其子类代表的异常。
execute(operation [, parameters])
准备并且执行一个数据库操作(查询或者命令)。
参数可能会以序列或者映射形式提供,并且将会被绑定到操作中的变量上。变量以数据库特有表示方法指定(详见模块全局属性
paramstyle)。[5]操作的引用会被游标持有,若相同操作被再次传入,游标将会优化其行为。这对一些算法是极大的优化,算法中使用(多次)相同操作但传入参数不同。
译者解释:当一个算法需要重复执行相同的操作,但是每次使用不同的参数时,优化措施(如保留操作的引用)可以提高效率。简而言之,就是通过重用相同的操作对象并改变其参数,可以优化算法的性能,尤其是在需要多次执行相同操作但参数不同的情况下。
为达成最大性能,当重用操作时,最好使用
setinputsize()提前指定参数类型和大小。参数与预定义信息不匹配是合法的,实现会进行补偿,但可能会损失性能。参数也可能会指定为一个元素为元组的列表,例如,在一个操作中插入多行数据,但是该类操作被废弃:使用
executemany()替代。返回值并没有在规范中定义。
executemany(operation, seq_of_parameters)
准备一个数据库操作(查询或命令)并且用
seq_of_parameters参数提供的参数序列或映射进行执行。模块可以自由实现该方法,使用多次调用
execute()方法的形式,或者使用操作数组,使得数据库在一次调用中将其作为一个整体处理。Use of this method for an operation which produces one or more result sets constitutes undefined behavior, and the implementation is permitted (but not required) to raise an exception when it detects that a result set has been created by an invocation of the operation.
execute()方法的相关说明同样适用于本方法。返回值并没有在规范中定义。
fetchone()
- 获取查询结果集中的下一行,返回一个单序列,或者当没有数据获取时返回
None。[6] - 若先前调用
execute*()并未返回任何结果,或者先前没有调用execute*(),Error(或其子类)类型异常会被抛出。
fetchmany([size=cursor.arraysize])
- 获取查询结果的下一个行集合,返回一个序列的序列(例如,一个以元组为元素的列表)。当没有行可以获取时返回一个空序列。
- 每次调用获取的行数由参数指定。若没有指定参数,cusor.arraysize将指定要获取的行数量。方法应当获取与指定参数一样多的行数,若无法获取指定函数,将会返回少于指定数量的行数。
- 若先前调用
execute*()并未返回任何结果,或者先前没有调用execute*(),Error(或其子类)类型异常会被抛出。 - 注意,size参数会涉及性能问题。为了最优性能,最好使用arraysize属性设置的参数。若size参数被指定,则最好在下次
fetchmany()使用相同参数值。
fetchall()
- 获取查询结果中所有(剩余)的行,返回一个序列的序列(例如,一个以元组为元素的列表)。注意游标arraysize属性会影响该操作的性能。
- 若先前调用
execute*()并未返回任何结果,或者先前没有调用execute*(),Error(或其子类)类型异常会被抛出。
nextset()
- 该方法可选,因为并非所有数据库都支持多个结果集。[3]
- 该方法将会使得游标跳到下一个可获得的结果集,忽略当前结果集的剩余行
- 若无更多结果集,方法返回None,否则,将会返回一个结果集,并且调用
fetch*()方法时回从再下一个结果集返回数据。 - 若先前调用
execute*()并未返回任何结果,或者先前没有调用execute*(),Error(或其子类)类型异常会被抛出。
arraysize
- 可读可写属性,指明
fetchmany()方法一次获取的行数,默认是1,表示一次获取一行。 - 实现必须注意
fetchmany()方法参数中的本属性值,但是自由选择与数据库是一次单行(还是一次多行)交互。该属性也用在executemany()方法中。
setinputsizes(sises)
- 可以在调用
execute*()方法之前为操作参数预定义内存区域。 - sizes是一个序列,每个元素对应一个输入参数,元素应当是一个与输入参数相对应的
Type Object,或者应当是一个用于指定字符串最大长度的整数。若元素为None,则不会为该列保留预定义的内存区域(有助于避免为大型输入保留预定义内存区域)。 - 本方法应当在调用
execute*()方法之前调用。 - 具体实现可以自由选择让本方法不执行任何操作,用户也可以自由选择不使用本方法。
setoutputsize(size [, column])
- 为要获取的大列(例如,
LONG、BLOB等等)设置列缓存大小。列通过结果集序列中的索引进行指定。游标中不指定列将会为所有大列设置默认值。 - 本方法应当在调用
execute*()方法之前调用。 - 具体实现可以自由选择让本方法不执行任何操作,用户也可以自由选择不使用本方法。
Type Objects and Constructors
许多数据库需要有特定格式的输入,以便绑定到操作的输入参数。例如,若一个输入的目标是DATE列,那么该输入必须以特定字符串格式绑定到数据库。“Row ID”列合伙大型二进制列(例如,blob或RAW列)也存在类似问题。这给Python带来了问题,因为execute*()方法的参数是不含(数据库)类型的。当数据库模块遇到一个Python字符串对象,不知道其应当绑定为CHAR列、原始BINARY列,还是DATE列。
为克服该问题,模块必须提供定义在下面的构造函数,用以创建能够存储特殊值的对象。当该对象传给游标方法时,模块可以检测到输入参数的正确类型然后进行绑定。
游标对象的description属性返回查询结果集每一列的信息。type_code必须与下面定义的对象进行相等比较。Type Object可能不仅仅等于一个类型代码(例如,DATETIME可能等于date、time和timestamp列的类型代码,详见下面对模块开发者的实现提示小节)。
该模块导出如下构造函数和单例:
Date(year, month, day)
- 函数构造一个对象,该对象持有date值
Time(hour, minute, second)
- 函数构造一个对象,该对象持有一个time值
Timestamp(year, month, day, hour, minute, second)
- 函数构造一个对象,该对象持有一个time stamp值
DateFromTicks(ticks)
- 函数构造一个对象,该对象持有一个date值,该date值由给定ticks值构建(number of seconds since the epoch; see the documentation of the standard Python time module for details)
TimeFromTicks(ticks)
- 函数构造一个对象,该对象持有一个time值,该date值由给定ticks值构建(number of seconds since the epoch; see the documentation of the standard Python time module for details)
TimestampFromTicks(ticks)
- 函数构造一个对象,该对象持有一个timestamp值,该timestamp值由给定ticks值构建(number of seconds since the epoch; see the documentation of the standard Python time module for details)
Binary(string)
- 函数构造一个对象,该对象持有一个二进制串
STRING type
- 该类型对象用于描述数据库中基于字符串的列(例如,CHAR)
BINARY type
- 该类型对象用于描述数据库中的二进制列(例如,LONG、RAW、BLOB)
NUMBER type
- 该类型对象用于描述数据库中的数字列
DATETIME type
- 该类型对象用于描述数据库中的
date/time列
ROWID type
- 该类型对象用于描述数据库中的
Row ID列。
SQL NULL值由输入和输出上的Python None单例表示。
[!NOTE]
使用Unix时钟用于数据库接口可能会造成问题,因为其覆盖的日期范围有限。
对模块开发者的实现提示
Date/Time可以通过Python datetime模块(从Python2.3开始可用,并且从Python2.4版本开始提供C API),或者使用mxDateTime包(从Python1.5.2开始可用)实现。两者都在Python和C级别上提供必要的构造函数和方法。
这是一个基于Unix tick的Date/TIme构造器的示例实现,这些构造器将工作委托给通用构造器。
import time def DateFromTicks(ticks): return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): return Timestamp(*time.localtime(ticks)[:6])对于Binary对象,推荐使用buffer类型,buffer类型自Python1.5.2开始引入,详见Python文档。C语言接口可以在Python源码中查看
Include/bufferobject.h和Objects/bufferobject.c。这个Python类允许实现上述类型对象,即使描述类型代码字段为一个类型对象产生多个值。
class DBAPITypeObject: def __init__(self, *values): self.values = values def __cmp__(self, other): if other in self.values: return 0 if other < self.values: return 1 else: return -1生成的类型对象与传递给构造函数的所有值相等。
这里有一个实现上文中定义的异常继承关系的Python代码片段[10]:
class Error(Exception): pass class Warning(Exception): pass class InterfaceError(Error): pass class DatabaseError(Error): pass class InternalError(DatabaseError): pass class OperationalError(DatabaseError): pass class ProgrammingError(DatabaseError): pass class IntegrityError(DatabaseError): pass class DataError(DatabaseError): pass class NotSupportedError(DatabaseError): passC中可以使用
PyErr_NewException(fullname, base, NULL)API来创建异常对象。