27.3. 命令执行函数

一旦与数据库服务器的连接成功建立,便可用这里描述的函数执行 SQL 查询和命令。

27.3.1. 主函数

PQexec

给服务器提交一条命令并且等待结果。

PGresult *PQexec(PGconn *conn, const char *command);

返回一个PGresult指针或者也可能是一个 NULL 指针。 通常返回一个非空(non-NULL)的指针, 除非没有内存或发生了象不能把命令发送到服务器这样的严重错误。 如果返回的是 NULL,它应该被当作PGRES_FATAL_ERROR结果处理。 用PQerrorMessage获取有关错误的更多信息。

我们可以在命令行字串里包含多个 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 的形式声明了赋与参数符号的数据类型。 如果 paramTypesNULL,或者数组中任意元素是零, 那么服务器给对应的参数符号赋与和无类型文本串一样的数据类型。 paramValues[] 声明该参数的实际数值。这个数组中的空指针意味着对应的参数是空; 否则,这个指针指向一个空零结尾的文本字串(文本格式)或者服务器期待的格式的二进制数据(用于二进制格式)。 paramLengths[] 声明二进制格式参数的实际数据长度。 对于空参数和文本格式的参数会忽略这个参数。如果没有二进制参数,那么这个数组指针可以是空。 paramFormats[] 声明某个参数是文本(在数组中放一个零)还是二进制(在数组中放一个1)。 如果这个数组指针是空,那么所有参数都认为是文本的。 resultFormat 为零则获取以文本方式返回的结果,为一则获取以二进制形式返回的结果。 (目前不能规定从不同的字段获取不同格式的结果,尽管对下层的协议是可能的。)

PQexecParamsPQexec 最主要的优势是我们可以和命令串分开声明参数值, 这样就可以避免枯燥无聊并且很容易出错的引起和逃逸。 和 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);

PQexecPreparedPQexecParams 类似, 但是要执行的命令是通过命名一个前面准备好的语句声明的,而不是给出一个查询字串。 这个特性允许那些要重复使用的命令只进行一次分析和规划,而不是每次执行都来一遍。 PQexecPrepared 只在协议 3.0 和以后的版本里支持;在使用 2.0 版本的协议的时候,它们会失败。

参数和 PQexecParams 一样,只是给出的是一个准备好语句的名字,而不是一个查询字串, 并且没有 paramTypes[] 参数(没必要,因为准备好语句的参数类型是在创建的时候确定的)。

目前,和 PQexecPrepared 一起用的准备好语句必须通过执行 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_OUT

(从服务器)Copy Out (拷贝出)数据传输开始

PGRES_COPY_IN

Copy In (拷贝入)(到服务器)数据传输开始

PGRES_BAD_RESPONSE

服务器的响应无法理解

PGRES_NONFATAL_ERROR

发生了一个非致命错误(通知或者警告)

PGRES_FATAL_ERROR

发生了一个致命错误

如果结果状态是 PGRES_TUPLES_OK, 那么可以用下面的函数从查询的返回中抽取元组信息。 注意一个碰巧检索了零条元组的SELECT仍然显示 PGRES_TUPLES_OKPGRES_COMMAND_OK用于不返回元组的命令(INSERTUPDATE,等)。 返回 PGRES_EMPTY_QUERY 的响应通常意味着暴露了客户端软件里面的臭虫。

状态为 PGRES_NONFATAL_ERROR 的结果永远不会直接由 PQexec 或者其它查询执行函数返回;这类的结果会被传递给通知处理器(参阅 Section 27.9)。

PQresStatus

PQresultStatus返回的枚举类型转换成一个描述状态码的字符串常量。

char *PQresStatus(ExecStatusType status);

PQresultErrorMessage

返回与查询关联的错误信息,或在没有错误时返回一个空字符串。

char *PQresultErrorMessage(const PGresult *res);

如果有错误,那么返回的字串将包括一个结尾的新行。

紧跟在一个 PQexecPQgetResult 调用后面,PQerrorMessage (对连接)将返回与 PQresultErrorMessage (对结果)一样的字符串。 不过,一个PGresult将保有其错误信息直到被删除, 而连结的错误信息将在后续的操作完成时被改变。当你想知道与某个 PGresult相关联的状态时用 PQresultErrorMessage;当你想知道与连接的最近一个操作相关联的状态时用 PQerrorMessage

PQclear

PQclear 释放于PGresult相关联的存储空间。 任何不再需要的查询结果在不需要的时候都应该用PQclear释放掉。

void PQclear(PQresult *res);

只要你需要,你可以保留PGresult对象任意长的时间; 当你提交新的查询时它并不消失,甚至你断开连接后也是这样。 要删除它,你必须调用 PQclear。不这么做将导致你的应用中的内存泄漏。

PQmakeEmptyPGresult

构造一个带有给出的状态的,空的PGresult对象。

PGresult* PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);

这是libpq的内部函数, 用于分配和初始化一个空PGresult对象。 它被输出是因为一些应用需要自行生成结果对象(尤其是特定的带有错误状态的对象)。 如果conn非空(NULL)并且状态指示一个错误, 连接当前的错误信息被拷贝到PGresult。 注意最终对该对象还是要调用PQclear, 正如libpq本身返回的PGresult一样。

27.3.2. 检索查询结果信息

这些函数用于从一个代表着成功查询结果(也就是说,状态为 PGRES_TUPLES_OK 的查询) 的 PGresult 对象。对于其它状态值的对象,他们的行为会好像他们有零行和零列一样。

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

格式码为 0 表示文本数据,而格式码是一表示二进制数据。(其它编码保留给将来定义。)

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返回在数据库行里面给该数据字段分配的空间, 换句话说就是该数据类型在服务器里的二进制形式的大小(尺寸)。 (因此,这个对客户端没有什么用。) 如果该数据域是可变尺寸,返回 -1。

PQbinaryTuples

如果PGresult包含二进制元组数据时返回 1, 如果包含 ASCII 数据返回 0。

int PQbinaryTuples(const PGresult *res);

这个函数已经废弃了(除了还用于与 COPY 连接之外),因为我们可能在一个 PGresult 的某些字段里包含文本数据,而另外一些字段包含二进制数据。 更好的是使用 PQfformatPQbinaryTuples 只有在结果中的所有字段都是二进制(格式 1)的时候才返回 1。

PQgetvalue

返回一个PGresult 里面一行的单独的一个字段的值。 行和字段编号从 0 开始。

char* PQgetvalue(const PGresult *res,
		 int row_number,
		 int column_number);

对于文本格式的数据, PQgetvalue 返回的值是一个表示字段值的空(NULL)结尾的字符串。 对于二进制格式, 返回的值就是由该数据类型的 typsendtypreceive 决定的二进制表现形式。 (在这种情况下,数值实际上也跟着一个字节零,但是通常这个字节没什么用处,因为数值本身很可能包含内嵌的空。)

如果字段值是空,则返回一个空字串。参阅 PQgetisnull 来区别空值和空字串值。

PQgetvalue 返回的指针指向一个本身是 PGresult结构的一部分的存储区域。我们不能更改它, 并且如果我们要在PGresult结构的生存期后还要使用它的话, 我们必须明确地把该数值拷贝到其他存储器中。

PQgetisnull

测试一个字段是否为空(NULL)。行和字段编号从 0 开始。

int PQgetisnull(const PGresult *res,
		int row_number,
		int column_number);

如果该域包含 NULL,函数返回 1,如果包含非空(non-null )值,返回 0。 (注意,对一个 NULL 数据域,PQgetvalue 将返回一个空字符串, 不是一个空指针。)

PQgetlength

返回以字节计的字段的长度。行和字段编号从 0 开始。

int PQgetlength(const PGresult *res,
		int row_number,
		int column_number);

这是某一特定数据值的实际数据长度。 行和字段编号从 0 开始。

int PQgetlength(const PGresult *res,
                int row_number,
                int column_number);

这是特定数值的实际数据长度,也就是说,PQgetvalue 指向的对象的大小。 对于文本数据格式,它和 strlen() 相同。对于二进制格式,这是潜在的信息。 请注意我们应该依靠 PQfsize 获取实际数据长度。

PQprint

向指定的输出流打印所有的行和(可选的)字段名称。

void PQprint(FILE* fout,      /* 输出流 */
	     const PGresult *res,
	     const PQprintOpt *po);

struct {
    pqbool  header;      /* 打印输出域头和行计数 */
    pqbool  align;       /* 填充对齐各字段 */
    pqbool  standard;    /* 旧的傻格式 */
    pqbool  html3;       /* 输出 HTML 表 */
    pqbool  expanded;    /* 扩展表 */
    pqbool  pager;       /* 必要时在输出中使用分页器 */
    char    *fieldSep;   /* 字段分隔符 */
    char    *tableOpt;   /* 在 HTML 中插入 table ... */
    char    *caption;    /* HTML caption */
    char    **fieldName; /* 替换字段名组成的空零结尾的数组 */
} PQprintOpt;

这个函数以前被 psql 用于打印查询结果,但是现在已经不用这个函数了。请注意它假设所有的数据都是文本格式。

27.3.3. 检索其它命令的结果信息

这些函数用于从 PGresult 对象里检索那些非 SELECT 结果的信息。

PQcmdStatus

返回产生PGresult的 SQL 命令的命令状态字符串。

char * PQcmdStatus(PGresult *res);

通常这只是命令的名字,但是它可能包括额外的数据,比如处理过的行数。

PQcmdTuples

返回被 SQL 命令影响的行的数量。

char * PQcmdTuples(PGresult *res);

如果产生PGresultSQL 命令是 INSERTUPDATEDELETEMOVE,或者 FETCH, 那么这里返回涉及行的行数。如果是其他命令返回一个空字符串。

PQoidValue

返回一个插入的元组的对象标识(OID)—— 如果SQL 命令是INSERT,而且刚好向带 OID 的表插入了一行。否则,返回 InvalidOid

Oid PQoidValue(const PGresult *res);

PQoidStatus

如果 SQL 命令是INSERT, 返回一个被插入的元组的对象ID的字串。 (如果 INSERT 并非恰好插入一行,或者目标表没有OID,那么字串将是 0。) 如果命令不是INSERT,则返回一个空字串。

char * PQoidStatus(const PGresult *res);

这个函数已经废弃乐,因为有了 PQoidValue,而且它也不是线程安全的。

27.3.4. 逃逸包含在 SQL 命令中的字串

PQescapeString为在 SQL 命令中使用字串而对之进行逃逸处理。 在我们向 SQL 命令里把数据值当作文本常量插入的时候很有用。有些字符(比如单引号和反斜杠)必须被逃逸, 以避免他们被 SQL 分析器作为特殊字符解析。

提示: 如果我们从一个不可信的来源收到一个字串的话,那么做恰当的逃逸就更重要了。 否则就有安全性危险:你会收到"SQL 注射"攻击,这个时候会有你不想看到的 SQL 喂给你的数据库。

请注意,如果一个数据值是作为 PQexecParams 或者同族函数的一个独立参数传递的, 那么逃逸就既不必要,也不正确。

size_t PQescapeString (char *to, const char *from, size_t length);

参数 from 指向将要逃逸的字串的第一个字符, length 参数给出在这个字串里的字符数量。字串结尾的字节零不是必须的,也不计入 length。 (如果在处理 length 个字节之前出现了一个字节零, 那么 PQescapeString 在这个字节零处停止; 这个行为类似 strncpy。) to 应该指向一个缓冲区,这个缓冲区至少能保存 length 数值的两倍还多一个的字符,否则该函数行为将不可预测。 调用 PQescapeString 就会把逃逸的 from 字串转换到 to 缓冲区,把特殊字符以免它们导致任何问题, 并且追加终止的字节零。那些必须包围在PostgreSQL 字串文本周围的单引号不算结果字串的一部分;你应该在把单引号放在插入这个处理结果的SQL命令周围。

PQescapeString 返回写到 to 里面的字符数目, 不包括结尾的字节零。

如果 tofrom 字串相互重叠,那么其行为不可预测。

27.3.5. 逃逸包含在 SQL 命令中的二进制字串

PQescapeBytea

逃逸那些在 SQL 命令中使用的用 bytea 表示的二进制数据。 和 PQescapeString 一样,这个函数只有在直接向 SQL 字串插入数据的时候使用。

unsigned char *PQescapeBytea(const unsigned char *from,
				 size_t from_length,
				 size_t *to_length);

SQL 语句中用做 bytea 字串文本的一部分的时候, 有些字节值必需逃逸(但是对于所有字节而言是可以逃逸)。 通常,要逃逸一个字节,它是被转换成一个三位八进制数字, 该数字数值等于该字节的数值,然后前缀两个反斜杠。 单引号(')和反斜杠字符(\)有自己特殊的逃逸序列。参阅 Section 8.4 获取更多信息。 PQescapeBytea 执行这个操作,它只逃逸需要逃逸的最少的字符。

from 参数指向需要逃逸的字串的第一个字节, from_length 参数反映在这个二进制字串(结尾的字节零既不必要也不计算在内的字串)里字节的个数。 to_length 参数应该是一个指向某个缓冲区的指针, 它的空间应该能够保存逃逸后的结果字串长度。 结果字串长度不包括结果结尾的字节零。

PQescapeBytea 在内存重返回一个 from 参数的二进制字串的逃逸后的版本,这片内存是用 malloc() 分配的, 在结果不再需要的时候,必须用 PQfreemem() 释放。 返回的字串已经把所有特殊的字符替换掉了,这样他们就可以由 PostgreSQL的字串文本分析器以及 bytea 的输入函数正确地处理。 同时还追加了一个结尾的字节零。那些必需包围在 PostgreSQL字串文本周围的单引号不算结果字串的一部分。

PQunescapeBytea

把一个二进制数据的逃逸后的字串表现形式转换成二进制数据 --- PQescapeBytea 的反作用。 在以文本格式抽取 bytea 数据的时候是必须的, 但是在以二进制格式抽取的时候是不必要的。

unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);

from 参数指向一个逃逸后的字串, 比如 PQgetvalue 处理过一个 bytea 字段后返回的。PQunescapeBytea 把它的字串表现形式转换成二进制形式, 它返回一个用 malloc() 分配的指向该缓冲区的指针, 或者是出错时返回空, 缓冲区的尺寸放在 to_length 里。 在不再需要这个结果之后,这片内存必须用 PQfreemem() 释放。

PQfreemem

释放 libpq 分配的内存。

void PQfreemem(void *ptr);

释放由 libpq 分配的内存, 特别是 PQescapeByteaPQunescapeBytea, 和 PQnotifies。这是 Microsoft Windows 必须的, 因为它不能跨越 DLL 释放内存,除非使用了多线程的 DLL (VC6 中的 /MD)。 在其它平台上,这个函数和标准的库函数free()一样。