SPI_execute
SPI_execute — 执行一个命令
大纲
int SPI_execute(const char *command, boolread_only, longcount)
描述
SPI_execute 执行指定的 SQL 命令,并最多检索
count 行。如果 read_only
为 true,该命令必须是只读的,且执行开销会略有降低。
此函数只能从已连接的 C 函数中调用。
如果 count 为零,则该命令会针对其适用的所有行执行。如果 count 大于零,则最多检索
count 行;达到该计数时就会停止执行,这很像给查询增加了一个 LIMIT 子句。例如:
SPI_execute("SELECT * FROM foo", true, 5);
最多会从该表中检索 5 行。注意,这种限制只有在命令实际返回行时才有效。例如:
SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);
会忽略 count 参数,把
bar 中的所有行都插入进去。不过:
SPI_execute("INSERT INTO foo SELECT * FROM bar RETURNING *", false, 5);
最多只会插入 5 行,因为取到第 5 行 RETURNING 结果后就会停止执行。
你可以在一个字符串中传递多条命令;SPI_execute
返回最后执行的那条命令的结果。count 限制会分别作用于每条命令(尽管实际返回的只有最后一条命令的结果)。该限制不适用于规则生成的任何隐藏命令。
当 read_only 为 false 时,SPI_execute 会递增命令计数器,并在执行字符串中的每条命令前计算新的快照。如果当前事务隔离级别是
SERIALIZABLE 或 REPEATABLE READ,这个快照实际上不会变化;但在 READ COMMITTED 模式下,更新快照会让每条命令都能看到其他会话中新近提交事务的结果。这对于修改数据库的命令获得一致行为至关重要。
当 read_only 为 true 时,SPI_execute 不会更新快照和命令计数器,并且只允许命令字符串中出现普通的 SELECT 命令。这些命令会使用外围查询先前建立的快照来执行。由于消除了每条命令的额外开销,这种执行模式比读写模式略快。它还允许构造真正稳定的函数:由于连续执行都会使用同一个快照,结果也就不会发生变化。
在同一个使用 SPI 的函数中混合只读命令和读写命令通常并不明智,因为只读查询看不到读写查询所做的数据库更新,这可能导致非常令人困惑的行为。
(最后一条)命令实际执行所处理的行数,会通过全局变量
SPI_processed 返回。如果函数返回值是
SPI_OK_SELECT、SPI_OK_INSERT_RETURNING、SPI_OK_DELETE_RETURNING、SPI_OK_UPDATE_RETURNING,则可以通过全局指针
SPITupleTable *SPI_tuptable 访问结果行。有些工具命令(如 EXPLAIN)也会返回结果行集,此时
SPI_tuptable 同样会保存结果。另一些工具命令(COPY、CREATE TABLE AS)不返回行集,因此 SPI_tuptable 为 NULL,但它们依然会在
SPI_processed 中返回处理的行数。
结构体 SPITupleTable 定义如下:
typedef struct SPITupleTable
{
/* 公共成员 */
TupleDesc tupdesc; /* 元组描述符 */
HeapTuple *vals; /* 元组数组 */
uint64 numvals; /* 有效元组数 */
/* 私有成员,不供外部调用者使用 */
uint64 alloced; /* vals 数组的已分配长度 */
MemoryContext tuptabcxt; /* 结果表的内存上下文 */
slist_node next; /* 用于内部管理的链接 */
SubTransactionId subid; /* 创建 tuptable 的子事务 */
} SPITupleTable;
SPI 调用者可以使用 tupdesc、vals 和 numvals
字段;其余字段属于内部实现。vals 是一个指向各行的指针数组。行数由 numvals 给出(出于一些历史原因,这个计数也会通过 SPI_processed 返回)。tupdesc 是行描述符,可以传给那些处理行的 SPI
函数。
SPI_finish 会释放当前 C 函数调用期间分配的全部
SPITupleTable。如果某个结果表已经不再需要,也可以提前调用 SPI_freetuptable 释放它。
参数
const char *command包含待执行命令的字符串
boolread_onlytrue表示只读执行longcount要返回的最大行数,或者用
0表示不限制
返回值
如果命令执行成功,则返回下列(非负)值之一:
SPI_OK_SELECT执行了
SELECT(但不是SELECT INTO)SPI_OK_SELINTO执行了
SELECT INTOSPI_OK_INSERT执行了
INSERTSPI_OK_DELETE执行了
DELETESPI_OK_UPDATE执行了
UPDATESPI_OK_MERGE执行了
MERGESPI_OK_INSERT_RETURNING执行了
INSERT RETURNINGSPI_OK_DELETE_RETURNING执行了
DELETE RETURNINGSPI_OK_UPDATE_RETURNING执行了
UPDATE RETURNINGSPI_OK_UTILITY执行了工具命令(例如
CREATE TABLE)SPI_OK_REWRITTEN命令被规则重写成了另一类命令(例如
UPDATE变成了INSERT)
出错时,返回以下负值之一:
SPI_ERROR_ARGUMENTcommand为NULL,或者count小于 0SPI_ERROR_COPY尝试执行了
COPY TO stdout或COPY FROM stdinSPI_ERROR_TRANSACTION尝试执行了事务控制命令(
BEGIN、COMMIT、ROLLBACK、SAVEPOINT、PREPARE TRANSACTION、COMMIT PREPARED、ROLLBACK PREPARED及其各种变体)SPI_ERROR_OPUNKNOWN命令类型未知(理论上不应发生)
SPI_ERROR_UNCONNECTED如果从一个未连接的 C 函数中调用
注解
所有 SPI 查询执行函数都会设置 SPI_processed 和
SPI_tuptable(只设置指针,而不更改结构体内容)。如果需要在后续调用之后继续访问 SPI_execute 或其他查询执行函数的结果表,请把这两个全局变量保存到 C 函数的局部变量中。
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.