27.3. 命令执行函数 #
与数据库服务器的连接成功建立后,此处描述的函数用于执行 SQL 查询和命令。
27.3.1. 主要函数 #
命令字符串中允许包含多条 SQL 命令(用分号分隔)。在单次
PQexec
调用中发送的多个查询会在单个事务中处理,除非查询字符串中包含显式的
BEGIN/COMMIT 命令将其划分成多个事务。但要注意,返回的
PGresult 结构只描述字符串中最后一条被执行命令的结果。如果其中一条命令失败,对该字符串的处理即停止,返回的
PGresult 描述该错误情况。
PQexecParams提交一个命令给服务器并且等待结果,它可以在 SQL 命令文本之外独立地传递参数。
PGresult *PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);PQexecParams与PQexec相似,但是提供了额外的功能:参数值可以与命令字符串分开指定,并且可以以文本或二进制格式请求查询结果。PQexecParams仅支持使用协议 3.0 及更高版本的连接;使用协议 2.0 时会失败。如果使用了参数,它们在命令字符串中被引用为
$1、$2等。nParams是提供的参数数量;它是数组paramTypes[]、paramValues[]、paramLengths[]和paramFormats[]的长度(当nParams为零时,数组指针可以是NULL)。paramTypes[]通过 OID 指定要赋予给参数符号的数据类型。如果paramTypes为NULL或者该数组中任何特定元素为零,服务器会用对待未指定类型的字符串字面量的相同方式为参数符号指派一种数据类型。paramValues[]指定参数的实际值。数组中的空指针表示对应参数为 null;否则,指针指向以零结尾的文本字符串(文本格式),或采用服务器所要求格式的二进制数据(二进制格式)。paramLengths[]指定二进制格式参数的实际数据长度。对于 null 参数和文本格式参数,该值会被忽略。如果没有二进制参数,数组指针可以为空指针。paramFormats[]指定参数采用文本格式(在数组中填入零)还是二进制格式(填入一)。如果数组指针为空指针,则将所有参数视为文本。resultFormat为零时获取文本格式的结果,为一时获取二进制格式的结果。(目前无法让不同结果列使用不同格式,尽管底层协议支持这样做。)
PQexecParams 相对于 PQexec 的主要优点是可以将参数值与命令字符串分开,从而避免繁琐且容易出错的加引号和转义工作。和PQexec不同,PQexecParams至多允许在给定串中出现一个 SQL 命令(其中可以有分号,但是不能有超过一个非空命令)。这是底层协议的一个限制,但是有助于抵抗 SQL 注入攻击。
PQexecPrepared发送一个以给定参数执行预备语句的请求,并等待结果。
PGresult *PQexecPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);PQexecPrepared与PQexecParams相似,但要执行的命令通过命名一个先前已准备好的语句来指定,而不是给出查询字符串。此特性让将反复使用的命令只需解析和规划一次,而不是每次执行时都进行。PQexecPrepared只在协议 3.0 及更高版本的连接中受支持;使用协议 2.0 时会失败。参数与
PQexecParams相同,只是用预备语句的名称代替查询字符串,且没有paramTypes[]参数(不需要它,因为预备语句的参数类型在创建时就已确定)。
目前,供 PQexecPrepared
使用的预备语句必须通过执行 SQL
PREPARE 命令建立,该命令通常用
PQexec 发送(不过
libpq 的任何查询提交函数都可以使用)。未来的版本可能会提供更底层的语句准备接口。
PGresult
结构封装了服务器返回的结果。libpq
应用程序员应注意维护
PGresult 的抽象。请使用下面的访问函数获取
PGresult 的内容。避免直接引用
PGresult 结构的字段,因为它们将来可能改变。
PQresultStatus返回命令的结果状态。
ExecStatusType PQresultStatus(const PGresult *res);
PQresultStatus可以返回下列值之一:PGRES_EMPTY_QUERY发送给服务器的字符串为空。
PGRES_COMMAND_OK不返回数据的命令成功完成。
PGRES_TUPLES_OK返回数据的命令(例如
SELECT或SHOW)成功完成。PGRES_COPY_OUTCopy Out(从服务器传出)数据传输已开始。
PGRES_COPY_INCopy In(向服务器传入)数据传输已开始。
PGRES_BAD_RESPONSE无法理解服务器的响应。
PGRES_NONFATAL_ERROR发生了一个非致命错误(一个通知或警告)。
PGRES_FATAL_ERROR发生了一个致命错误。
如果结果状态为
PGRES_TUPLES_OK,就可以使用下面描述的函数检索查询返回的行。注意,碰巧检索到零行的SELECT命令仍然显示PGRES_TUPLES_OK。PGRES_COMMAND_OK用于永远不会返回行的命令(INSERT、UPDATE等)。PGRES_EMPTY_QUERY响应可能表明客户端软件有 bug。状态为
PGRES_NONFATAL_ERROR的结果永远不会由PQexec或其他查询执行函数直接返回;这类结果会被传递给通知处理器(见第 27.9 节)。PQresStatus把
PQresultStatus返回的枚举类型转换为描述该状态码的字符串常量。char *PQresStatus(ExecStatusType status);
PQresultErrorMessage返回与命令相关联的错误消息;如果没有错误则返回空字符串。
char *PQresultErrorMessage(const PGresult *res);
如果发生过错误,返回的字符串将包含一个末尾换行符。
在
PQexec或PQgetResult调用之后紧接着,(连接上的)PQerrorMessage会返回与(结果上的)PQresultErrorMessage相同的字符串。但PGresult会保留其错误消息直到被销毁,而连接的错误消息会随后续操作而变化。想知道与某个特定PGresult相关联的状态时使用PQresultErrorMessage;想知道连接上最新操作的状态时使用PQerrorMessage。PQresultErrorField返回错误报告的单个字段。
char *PQresultErrorField(const PGresult *res, int fieldcode);
fieldcode是错误字段标识符;见下面列出的符号。如果PGresult不是错误或警告结果,或不包含指定字段,则返回NULL。字段值通常不包含末尾换行符。可用的字段代码如下:
PG_DIAG_SEVERITY严重级别;字段内容为
ERROR、FATAL或PANIC(错误消息中),或WARNING、NOTICE、DEBUG、INFO或LOG(通知消息中),或它们的本地化翻译。总是存在。PG_DIAG_SQLSTATE错误的 SQLSTATE 代码(见附录 A)。不可本地化。总是存在。
PG_DIAG_MESSAGE_PRIMARY主要的人类可读错误消息(通常一行)。总是存在。
PG_DIAG_MESSAGE_DETAIL细节:可选的次要错误消息,携带有关该问题的更多细节。可能有多行。
PG_DIAG_MESSAGE_HINT提示:关于如何处理该问题的可选建议。它与细节的区别在于它提供的是建议(可能并不恰当)而非确凿事实。可能有多行。
PG_DIAG_STATEMENT_POSITION一个包含十进制整数的字符串,以原始语句字符串中的下标指示错误光标位置。第一个字符的下标为 1,位置按字符而非字节度量。
PG_DIAG_CONTEXT错误发生所在上下文的指示。目前这包括活跃的 PL 函数的调用栈回溯。回溯每行一个条目,最近的在最前面。
PG_DIAG_SOURCE_FILE报告错误的源代码位置的文件名。
PG_DIAG_SOURCE_LINE报告错误的源代码位置的行号。
PG_DIAG_SOURCE_FUNCTION报告错误的源代码函数的名称。
客户端负责按自身需要格式化要显示的信息;特别是应当在需要时折行。错误消息字段中出现的换行符应被视为段落分隔而不是换行。
libpq 内部产生的错误会有严重级别和主要消息,但通常没有其他字段。3.0 之前协议的服务器返回的错误会包含严重级别和主要消息,有时还有一条细节消息,但没有其他字段。
注意,错误字段只能从
PGresult对象获取,不能从PGconn对象获取;不存在PQerrorField函数。PQclear释放与
PGresult关联的存储。每个命令结果在不再需要时都应通过PQclear释放。void PQclear(PQresult *res);
PGresult对象需要保留多久就可以保留多久;发出新命令时它不会消失,即使关闭连接也不会。要清除它,必须调用PQclear。不这样做将导致应用内存泄漏。PQmakeEmptyPGresult以给定的状态构造一个空的
PGresult对象。PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
这是 libpq 分配并初始化空
PGresult对象的内部函数。导出它是因为某些应用发现自己生成结果对象(特别是带有错误状态的对象)很有用。如果conn非空且status指示一个错误,指定连接的当前错误消息会被复制到PGresult中。注意,最终也应对该对象调用PQclear,就像对待 libpq 本身返回的PGresult一样。
27.3.2. 检索查询结果信息 #
这些函数用于从表示成功查询结果的PGresult对象(即状态为PGRES_TUPLES_OK的对象)中提取信息。对于其他状态值的对象,这些函数会将结果视为零行、零列。
PQntuples返回查询结果中的行(元组)数量。
int PQntuples(const PGresult *res);
PQnfields返回查询结果中每一行的列(字段)数。
int PQnfields(const PGresult *res);
PQfname返回与给定列号相关联的列名。列号从 0 开始。
char *PQfname(const PGresult *res, int column_number);如果列号超出范围,将返回
NULL。PQfnumber返回与给定列名相关联的列号。
int PQfnumber(const PGresult *res, const char *column_name);如果给定的名字不匹配任何列,将返回 -1。
给定的名称会被当作 SQL 命令中的标识符来处理,也就是说,除非用双引号括起来,否则会转换为小写。例如,对于由 SQL 命令
select 1 as FOO, 2 as "BAR";
生成的查询结果,将有如下结果:
PQfname(res, 0) foo PQfname(res, 1) BAR PQfnumber(res, "FOO") 0 PQfnumber(res, "foo") 0 PQfnumber(res, "BAR") -1 PQfnumber(res, "\"BAR\"") 1
PQftable返回给定列所取自的表的 OID。列号从 0 开始。
Oid PQftable(const PGresult *res, int column_number);如果列号超出范围、指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回
InvalidOid。可以查询系统表pg_class,确定所引用的具体表。包含 libpq 头文件后,将定义类型
Oid和常量InvalidOid。它们都属于某种整数类型。PQftablecol返回构成指定查询结果列的列在其所属表中的列号。结果列号从 0 开始。
int PQftablecol(const PGresult *res, int column_number);如果列号超出范围、指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回零。
PQfformat返回表示给定列格式的格式代码。列号从 0 开始。
int PQfformat(const PGresult *res, int column_number);格式代码零表示文本数据,格式代码一表示二进制数据。(其他代码保留供将来定义。)
PQftype返回与给定列号相关联的数据类型。被返回的整数是该类型的内部 OID 号。列号从 0 开始。
Oid PQftype(const PGresult *res, int column_number);可以查询系统表
pg_type来获取各种数据类型的名称和属性。内置数据类型的 OID 定义在源码树的src/include/catalog/pg_type.h文件中。PQfmod返回与给定列号相关联的列的类型修饰符。列号从 0 开始。
int PQfmod(const PGresult *res, int column_number);修饰符值的含义由数据类型决定,通常表示精度或大小限制。值 -1 表示“没有可用信息”。大多数数据类型不使用修饰符,此时该值始终为 -1。
PQfsize返回给定列号对应列的大小,以字节计。列号从 0 开始。
int PQfsize(const PGresult *res, int column_number);PQfsize返回数据库行中为该列分配的空间,即服务器内部表示该数据类型所需的大小。(因此,它对客户端用处不大。)负值表示该数据类型是变长类型。PQbinaryTuples如果
PGresult包含二进制数据,返回 1。如果包含的是文本数据,返回 0。int PQbinaryTuples(const PGresult *res);
此函数已弃用(与
COPY配合使用的情况除外),因为单个PGresult可能在部分列中包含文本数据,而在其他列中包含二进制数据。推荐使用PQfformat。只有结果中的所有列都采用二进制格式(格式 1)时,PQbinaryTuples才返回 1。PQgetvalue返回
PGresult中某一行的一个字段值。行号和列号都从 0 开始。char* PQgetvalue(const PGresult *res, int row_number, int column_number);对于文本格式的数据,
PQgetvalue返回字段值的字符串表示,以零字节结尾。对于二进制格式的数据,返回值采用该数据类型的typsend和typreceive函数所决定的二进制表示。(这种情况下,值后面实际上也有一个零字节,但通常没有用处,因为值本身很可能包含零字节。)如果字段值为 null,则返回空字符串。关于如何区分 null 值与空字符串值,参见
PQgetisnull。PQgetvalue返回的指针指向属于PGresult结构体的存储空间。不应修改它所指向的数据;如果需要在PGresult结构体的生命周期结束后继续使用这些数据,就必须显式地将数据复制到其他存储空间。PQgetisnull测试一个字段是否为 null 值。行号和列号从 0 开始。
int PQgetisnull(const PGresult *res, int row_number, int column_number);如果字段为 null,此函数返回 1;如果包含非 null 值,则返回 0。(注意,对于 null 字段,
PQgetvalue返回空字符串,而非空指针。)PQgetlength返回字段值的实际长度,以字节计。行号和列号从 0 开始。
int PQgetlength(const PGresult *res, int row_number, int column_number);这是该数据值的实际数据长度,即
PQgetvalue所指对象的大小。对于文本格式的数据,它与strlen()的结果相同。对于二进制格式,这是必不可少的信息。注意,不应依赖PQfsize来获取实际数据长度。PQprint打印出所有的行以及(可选的)列名到指定的输出流。
void PQprint(FILE* fout, /* output stream */ const PGresult *res, const PQprintOpt *po); typedef struct { pqbool header; /* print output field headings and row count */ pqbool align; /* fill align the fields */ pqbool standard; /* old brain dead format */ pqbool html3; /* output HTML tables */ pqbool expanded; /* expand tables */ pqbool pager; /* use pager for output if needed */ char *fieldSep; /* field separator */ char *tableOpt; /* attributes for HTML table element */ char *caption; /* HTML table caption */ char **fieldName; /* null-terminated array of replacement field names */ } PQprintOpt;psql 以前用此函数打印查询结果,现在已不再使用。注意,此函数假定所有数据都是文本格式。
27.3.3. 检索其他结果信息 #
这些函数用于从并非 SELECT 结果的
PGresult 对象中提取信息。
PQcmdStatus返回生成该
PGresult的 SQL 命令的命令状态标签。char * PQcmdStatus(PGresult *res);
通常这只是命令的名称,但可能包含附加数据,例如处理的行数。
PQcmdTuples返回受 SQL 命令影响的行数。
char * PQcmdTuples(PGresult *res);
如果生成该
PGresult的 SQL 命令是INSERT、UPDATE、DELETE、MOVE或FETCH,此函数返回一个包含受影响行数的字符串。如果是其他命令,返回空字符串。PQoidValue如果 SQL 命令是一个向带 OID 的表中恰好插入一行的
INSERT,返回所插入行的 OID。否则返回InvalidOid。Oid PQoidValue(const PGresult *res);
PQoidStatus如果 SQL 命令是一个
INSERT,返回一个含有所插入行 OID 的字符串。(如果该INSERT并非恰好插入一行,或目标表不带 OID,字符串为0。)如果命令不是INSERT,返回空字符串。char * PQoidStatus(const PGresult *res);
此函数已被弃用,推荐使用
PQoidValue。它不是线程安全的。
27.3.4. 用于在 SQL 命令中嵌入字符串的转义 #
PQescapeStringConn为了让一个字符串可用于 SQL 命令而对它进行转义。当在 SQL 命令中把数据值作为字符串字面量插入时,这很有用。一些字符(例如引号和反斜杠)必须经过转义,才不会被 SQL 解析器解释成特殊含义。PQescapeStringConn执行这种操作。
提示
在处理来自不可信来源的字符串时,正确转义尤其重要。否则就会有安全风险:你很容易受到“SQL 注入”攻击,不期望的 SQL 命令可能会被送入数据库。
注意,当一个数据值被作为PQexecParams或相关例程中的一个独立参数传递时,没有必要做转义而且做转义也不正确。
size_t PQescapeStringConn(PGconn *conn,
char *to, const char *from, size_t length,
int *error);
PQescapeStringConn把from字符串的转义版本写入to缓冲区,对特殊字符进行转义使它们不会造成任何危害,并添加一个终止的零字节。包围PostgreSQL字符串字面量所需的单引号不包含在结果字符串中;它们应在插入转义结果的 SQL 命令中提供。from参数指向待转义字符串的首字符,length参数给出该字符串的字节数。输入不必以零字节结尾,末尾零字节也不应计入length。(如果在处理完length个字节之前遇到末尾零字节,PQescapeStringConn会在该字节处停止;这一行为类似于strncpy。)to必须指向一个缓冲区,其容量至少为length的两倍加一个字节,否则行为未定义。如果to与from字符串重叠,行为同样未定义。
如果error参数不是NULL,则成功时将*error设为零,出错时设为非零。目前唯一可能的错误是源字符串中存在无效的多字节编码。出错时仍会生成输出字符串,但预计服务器会因其格式错误而拒绝它。发生错误时,无论error是否为NULL,一条合适的错误消息都会存储在conn对象中。
PQescapeStringConn返回写到to的字节数,不包括终止的零字节。
size_t PQescapeString (char *to, const char *from, size_t length);
PQescapeString是PQescapeStringConn的一个较老的、已弃用的版本;区别在于它不接受conn或error参数。因此,它无法根据连接属性(例如字符编码)调整行为,可能给出错误的结果。此外,它也无法报告错误情况。
PQescapeString可以在同一时刻仅使用一个PostgreSQL连接的单线程客户端程序中安全使用(在这种情况下,它能够“在内部”取得所需信息)。在其他情形下,它存在安全隐患,应改用PQescapeStringConn。
27.3.5. 用于在 SQL 命令中嵌入二进制串的转义 #
PQescapeByteaConn对二进制数据进行转义,使其能够在 SQL 命令中用作
bytea类型的值。与PQescapeStringConn一样,这仅用于将数据直接插入 SQL 命令字符串的情况。unsigned char *PQescapeByteaConn(PGconn *conn, const unsigned char *from, size_t from_length, size_t *to_length);当某些字节值被用作一个 SQL 语句中的
bytea字面量的一部分时,它们必须被转义(但所有字节值都可以被转义)。一般而言,要转义一个字节,需要把它转换为与该字节值相等的三位八进制数,并在前面加上一个或两个反斜线。单引号(')和反斜线(\)字符有特殊的替代转义序列。更多信息见第 8.4 节。PQescapeByteaConn执行这种操作,只转义最少必需的字节。from参数指向待转义字符串的首字节,from_length参数给出该二进制字符串的字节数。(末尾零字节既不需要,也不计入长度。)to_length参数指向用于保存转义后字符串长度的变量。该结果字符串长度包含结果末尾的零字节。PQescapeByteaConn返回from参数所指二进制字符串的转义版本,存放在通过malloc()分配的内存中。结果不再需要时,应使用PQfreemem()释放该内存。返回字符串中的所有特殊字符都已替换,以便 PostgreSQL 字符串字面量解析器和bytea输入函数正确处理。还会添加一个末尾零字节。包围 PostgreSQL 字符串字面量所需的单引号不包含在结果字符串中。在发生错误时,将返回一个空指针,并且一个合适的错误消息被存储在
conn对象中。当前,唯一可能的错误是没有足够的内存用于结果串。PQescapeByteaPQescapeBytea是PQescapeByteaConn的旧版本,现已弃用。unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length);与
PQescapeByteaConn唯一的区别是,PQescapeBytea不接受PGconn参数。因此,它无法根据连接属性(尤其是是否启用了标准符合字符串)调整行为,可能给出错误的结果。此外,它也无法在失败时返回错误消息。PQescapeBytea可以在同一时刻仅使用一个 PostgreSQL 连接的单线程客户端程序中安全使用(在这种情况下,它能够“在内部”取得所需信息)。在其他情形下,它存在安全隐患,应改用PQescapeByteaConn。PQunescapeBytea把二进制数据的字符串表示转换为二进制数据——即
PQescapeBytea的逆操作。以文本格式检索bytea数据时需要此函数;以二进制格式检索时则不需要。unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);
from参数指向一个字符串,例如对bytea列调用PQgetvalue时返回的字符串。PQunescapeBytea将这个字符串表示转换为二进制表示。它返回指向通过malloc()分配的缓冲区的指针,出错时返回NULL,并将缓冲区大小存入to_length。不再需要结果时,必须使用PQfreemem释放它。此转换并不完全是
PQescapeBytea的逆操作,因为从PQgetvalue收到的字符串并非经过“转义”的形式。具体而言,这意味着无需考虑字符串引号,因此也不需要PGconn参数。PQfreemem释放libpq分配的内存。
void PQfreemem(void *ptr);
释放 libpq 分配的内存,特别是
PQescapeByteaConn、PQescapeBytea、PQunescapeBytea和PQnotifies分配的内存。Microsoft Windows 需要此函数,因为它无法跨 DLL 释放内存,除非使用多线程 DLL(VC6 中的/MD)。在其他平台上,此函数与标准库函数free()相同。