ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 31.3\. 命令执行函数 一旦与数据库服务器的连接成功建立,便可以使用这里描述的函数执行SQL查询和命令。 ## 31.3.1\. 主函数 `PQexec` 给服务器提交一条命令并且等待结果。 ``` PGresult *PQexec(PGconn *conn, const char *command); ``` 返回一个`PGresult`指针或者也可能是一个空指针。 通常返回一个非空指针,除非耗尽内存或发生了像不能把命令发送到服务器这样的严重错误。 应该调用`PQresultStatus`函数来检查任何错误的返回值 (包括空指针的值,在这种情况下它将返回`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的时候会失败。 函数的参数是: `conn` 连接对象通过它发送命令。 `command` 要执行的SQL命令字符串。如果使用参数,它们在命令字符串中被叫做`$1`、 `$2`等等。 `nParams` 提供的参数数目;它是`paramTypes[]`、`paramValues[]`、 `paramLengths[]`和`paramFormats[]`数组的长度。 (当`nParams`是0时,数组指针可以是`NULL`。) `paramTypes[]` 通过OID,将声明数据类型指定到参数标记。如果`paramTypes` 是`NULL`,或数组中任何的特定参数是0,服务器为参数标记推断数据类型, 采用的方式与一个未定义类型的文本字符串相同。 `paramValues[]` 声明参数的实际值。在这个数组中的一个空指针表示相应的参数是空; 否则指针指向一个以零结尾的文本字符串(文本格式)或者服务器希望的格式的二进制数据 (二进制格式)。 `paramLengths[]` 为二进制格式的参数声明实际数据长度。该设置忽略空参数或文本格式的参数。 如果没有二进制参数,那么数组指针可以为空。 `paramFormats[]` 声明参数为文本(为相应参数在数组条目中放置一个0)还是二进制格式 (为相应参数在数组条目中放置一个1)。如果数组指针是空,那么所有参数被看做是文本字符串。 以二进制格式传递的值需要能够被后台识别的内部表示。例如,整数必须以网络字节顺序来传递。 传递`numeric`值需要服务器存储格式的识别,如在 `src/backend/utils/adt/numeric.c::numeric_send()`和 `src/backend/utils/adt/numeric.c::numeric_recv()`中那样。 `resultFormat` 声明0用于以文本格式获得结果,或1用于以二进制格式获得结果。 (目前没有规定以不同的格式来获取不同的结果列,即使底层协议中可能实现。) `PQexecParams`相比`PQexec`的主要优势是参数值可以从命令字符串中分离出来, 因此避免了繁琐和容易出错的引用和逃逸的需要。 和`PQexec`不同的是,`PQexecParams`在一个给出的字符串里最多允许一个SQL命令。 (里面可以有分号,但是不得超过一个非空的命令。)这是下层协议的一个限制, 但是也有些好处,比如作为对SQL注入攻击的额外防御。 > **Tip:** 通过OID声明参数类型是非常繁琐的,尤其是你不希望在你的程序里写死特定的OID值的时候。 不过,你可以避免这么做,即使在服务器自己无法判断参数类型, 或者是选择了一种与你预期不同的参数类型的时候也一样。在SQL命令文本里, 给参数符号附加一个明确的类型转换,显示你准备发送的数据类型。比如: > > ``` > SELECT * FROM mytable WHERE x = $1::bigint; > ``` > > 这样强制参数`$1`当作`bigint`看待,即使缺省情况下它会被赋予和 `x`一样的类型。在以二进制格式发送参数值的时候, 我们强烈建议通过这种方法或者是声明数字类型OID的方法强制类型判断, 因为二进制格式比文本格式少一些冗余,因此服务器就会少一些机会捕捉类型的错误匹配。 `PQprepare` 用给定的参数提交请求,创建一个预备语句,然后等待结束。 ``` PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes); ``` `PQprepare`创建一个为后面`PQexecPrepared`执行用的预备语句。 这个特性允许那些重复使用的语句只分析和规划一次,而不是每次执行都分析规划。 只是在协议3.0和以后的连接里支持`PQprepare`;在使用2.0协议的时候,它会失败。 这个函数从`query`字串里创建一个叫`stmtName`的预备语句, `query`必须只包含一个 SQL 命令。`stmtName`可以是`""`, 这样就创建一个无名的语句,这种情况下,任何前面存在的无名语句都会自动被代替; 否则,如果语句名已经在当前会话里定义,那就是一个错误。如果使用了参数, 那么在查询里它们引用成`$1`,`$2`等等。`nParams` 是参数的个数,参数的类型在数组`paramTypes[]`里事先声明好了。 (如果`nParams`是零,那么这个数组指针可以是`NULL`。) `paramTypes[]`用 OID 的方式声明与参数符号关联的数据类型。 如果`paramTypes`为`NULL`,或者数组中某个特定元素是零, 那么服务器将用处理无类型文本同样的方法给这个参数符号赋予数据类型。还有, 查询可以使用比`nParams`数值更大的参数符号编号; 也为这些符号推断数据类型。(参阅`PQdescribePrepared` 作为一个找出推断的什么类型的手段。) 和`PQexec`相似,结果通常是一个`PGresult`对象, 其内容表明服务器端是成功还是失败。空的结果表示内存耗尽或者完全不能发送命令。 使用`PQerrorMessage`获取有关这类错误的更多信息。 用于`PQexecPrepared`的预备语句也可以通过执行SQL [PREPARE](#calibre_link-625)语句来创建。 还有,尽管没有libpq函数可以删除一个预备语句, SQL [DEALLOCATE](#calibre_link-479)语句却可以删除。 `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[]`参数(没必要,因为预备语句的参数类型是在创建的时候确定的)。 `PQdescribePrepared` 提交请求以获取有关指定的预备语句的信息,并等待完成。 ``` PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName); ``` `PQdescribePrepared`允许应用程序获取有关先前准备的语句的信息。 `PQdescribePrepared`只在协议 3.0 和以后的版本里支持; 在使用 2.0 版本的协议的时候,它们会失败。 `stmtName`可以是`""`或`NULL`以指向未命名声明, 要么必须与现有的预备语句同名。成功时,会返回一个带有`PGRES_COMMAND_OK` 的`PGresult`。可以在这个`PGresult`中使用 `PQnparams`和`PQparamtype` 函数以获得预备语句的参数信息,同时`PQnfields`, `PQfname`,`PQftype`等函数提供声明的结果列(如果有)的信息。 `PQdescribePortal` 提交请求以获取有关指定的端口的信息,并等待完成。 ``` PGresult *PQdescribePortal(PGconn *conn, const char *portalName); ``` `PQdescribePortal`允许应用程序获得关于之前创建的端口的信息。 (libpq不提供与端口的直接连接,但可以使用这个函数来检查 `DECLARE CURSOR`命令创建的游标的属性)。`PQdescribePortal` 只支持3.0及其之后的连接协议;当使用协议2.0时会失败。 `portalName`可以是`""`或`NULL`以指向未命名声明, 要么必须与现有的预备语句同名。成功时,会返回一个带有`PGRES_COMMAND_OK` 的`PGresult`。可以在`PGresult`中使用 `PQnfields`,`PQfname`, `PQftype`等函数获取端口的结果列(如果有)的信息。 `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_COPY_BOTH` 拷贝入/出(到和从服务器)数据传输开始。这个特性当前只用于流复制, 所以这个状态不会在普通应用中发生。 `PGRES_SINGLE_TUPLE` `PGresult`包含一个来自当前命令的结果元组。 这个状态只在查询选择了单行模式时发生(参阅[Section 31.5](#calibre_link-626))。 如果结果状态是`PGRES_TUPLES_OK`或`PGRES_SINGLE_TUPLE`, 那么可以用下面的函数从查询的返回中抽取元组信息。注意一个碰巧检索了零条元组的 `SELECT`仍然显示`PGRES_TUPLES_OK`。 `PGRES_COMMAND_OK`用于不返回元组的命令(没有`RETURNING` 子句的`INSERT`,`UPDATE`等)。 返回`PGRES_EMPTY_QUERY`的响应通常意味着暴露了客户端软件里面的臭虫。 状态为`PGRES_NONFATAL_ERROR`的结果永远不会直接由`PQexec` 或者其它查询执行函数返回;这类的结果会被传递给通知处理器 (参阅[Section 31.12](#calibre_link-627))。 `PQresStatus` 把`PQresultStatus`返回的枚举类型转换成一个描述状态码的字符串常量。 调用者不应该释放结果。 ``` char *PQresStatus(ExecStatusType status); ``` `PQresultErrorMessage` 返回与查询关联的错误信息,或在没有错误时返回一个空字符串。 ``` char *PQresultErrorMessage(const PGresult *res); ``` 如果有错误,那么返回的字串将包括一个结尾的新行。调用者不应该直接释放结果。 在相关的`PGresult`句柄传递给`PQclear`之后,它会自动释放。 紧跟在一个`PQexec`或`PQgetResult`调用后面, `PQerrorMessage`(对连接)将返回与`PQresultErrorMessage` (对结果)一样的字符串。不过,一个`PGresult`将保有其错误信息直到被删除, 而连接的错误信息将在后续的操作完成时被改变。当你想知道与某个`PGresult` 相关联的状态时用`PQresultErrorMessage`; 当你想知道与连接的最近一个操作相关联的状态时用`PQerrorMessage`。 `PQresultErrorField` 返回一个独立的错误报告字段。 ``` char *PQresultErrorField(const PGresult *res, int fieldcode); ``` `fieldcode`是一个错误字段标识符;参阅下面列出的符号。 如果`PGresult`不是错误或者警告结果或者不包括指定的字段, 那么返回`NULL`。字段值通常将不包括结尾的新行。调用者不应该直接释放结果。 在相关联的`PGresult`句柄传递给`PQclear`之后,它将被自动释放。 下列代码是可用的: `PG_DIAG_SEVERITY` 严重程度,这个字段的内容是`ERROR`,`FATAL`或者`PANIC` (在错误信息里),或者`WARNING`,`NOTICE`,`DEBUG`, `INFO`或`LOG`(在注意信息里),或者是这些东西的一个本地化翻译。总是出现。 `PG_DIAG_SQLSTATE` 这个错误的SQLSTATE代码。SQLSTATE代码表示所发生的错误的类型; 可以由前端应用用于对特定的数据库错误执行特定的操作(比如错误处理)。 可能的SQLSTATE代码的列表,请查看[Appendix A](#calibre_link-120)。 这个字段是不能区域化的,并且总是出现。 `PG_DIAG_MESSAGE_PRIMARY` 主要的人类可读错误的信息(通常一行)。总是出现。 `PG_DIAG_MESSAGE_DETAIL` 细节:一个可选的从属错误信息,里面有更多有关该问题的细节。可能有多行。 `PG_DIAG_MESSAGE_HINT` 提示:一个可选的有关如何处理该问题的建议。它和细节的区别是它提供了建议 (可能不太合适)而不光是事实。可能有好几行。 `PG_DIAG_STATEMENT_POSITION` 一个包含十进制整数的字串,表明错误游标的位置,作为一个索引指向最初的语句字符串。 第一个字符的索引是 1,并且这个位置是用字符计,而不是用字节计。 `PG_DIAG_INTERNAL_POSITION` 这个和`PG_DIAG_STATEMENT_POSITION`字段定义是一样的, 区别是它在游标位置指向内部生成的命令时使用,而不是客户端提交的命令。如果出现了这个字段, 那么`PG_DIAG_INTERNAL_QUERY`字段也总是出现。 `PG_DIAG_INTERNAL_QUERY` 一个失败的内部生成的命令的文本。比如,这个可能是一个 PL/pgSQL 函数发出的 SQL 查询。 `PG_DIAG_CONTEXT` 一个指示器,表明错误发生的环境。目前这个包括活跃的过程语言函数和内部生成的查询的调用堆栈跟踪。 跟踪是每行一条,最近的在上面。 `PG_DIAG_SCHEMA_NAME` 如果错误与特定的数据库对象相关,那么是包含该对象的模式名(如果有)。 `PG_DIAG_TABLE_NAME` 如果错误与特定的表相关,那么是该表的名字。(参考模式名字段获取表的模式的名字。) `PG_DIAG_COLUMN_NAME` 如果错误与特定的表字段相关,那么是该字段的名字。(参考模式和表名字段识别该表。) `PG_DIAG_DATATYPE_NAME` 如果错误与特定的数据类型相关,那么是该数据类型的名字。(参考模式名字段获取数据类型的模式的名字。) `PG_DIAG_CONSTRAINT_NAME` 如果错误与特定的约束相关,那么是该约束的名字。参考上面列出的字段获取相关的表或域。 (为了这个目的,索引被看做是约束,即使它们是用约束语法创建的。) `PG_DIAG_SOURCE_FILE` 报告错误的源代码所在的文件名。 `PG_DIAG_SOURCE_LINE` 报告错误的源代码所在的行号。 `PG_DIAG_SOURCE_FUNCTION` 报告错误的源代码函数的名字。 > **Note:** 只为有限的错误类型提供模式名、表名、字段名、数据类型名和约束名字段; 参阅[Appendix A](#calibre_link-120)。不要假设这些字段的出现会保证其他字段的出现。 核心错误来源观察以上提到的相互关系,但是用户定义的函数可以以其他方式使用这些字段。 同样的,不要假设这些字段表示当前数据库中的同时期对象。 按照自身的要求格式化显示信息是客户端的责任;特别是根据需要对长行进行折行。 在错误信息字段里出现的新行字符应该当作分段符号,而不是换行。 libpq生成的错误将会有严重性和主信息,但是通常没有其它字段。 3.0 协议之前返回的错误将包含严重性和主信息,有时候还有详细信息,但是没有其它字段。 请注意这些错误字段只能从`PGresult`对象里获得, 而不是`PGconn`对象;没有`PQerrorField`函数。 `PQclear` 释放与`PGresult`相关联的存储空间。 任何不再需要的查询结果都应该用`PQclear`释放掉。 ``` void PQclear(PGresult *res); ``` 只要你需要,你可以保留`PGresult`对象任意长的时间; 当你提交新的查询时它并不消失,甚至你断开连接后也是这样。要删除它, 你必须调用`PQclear`。不这么做将导致你应用中的内存泄漏。 ## 31.3.2\. 检索查询结果信息 这些函数用于从一个代表着成功查询结果(也就是说,状态为`PGRES_TUPLES_OK`或 `PGRES_SINGLE_TUPLE`的查询)的`PGresult`对象中抽取信息。 它们也可以用于从一个成功描述操作中抽取信息: 一个描述的结果和实际查询的执行将要提供的结果有所有相同的字段信息,但是它有零行。 对于其它状态值的对象,他们的行为会好像他们有零行和零列一样。 `PQntuples` 返回查询结果里的行(元组)个数。因为它返回一个整数的结果,在32位操作系统上大型结果集可能溢出返回值。 ``` int PQntuples(const PGresult *res); ``` `PQnfields` 返回查询结果里数据行的列(字段)的个数。 ``` int PQnfields(const PGresult *res); ``` `PQfname` 返回与给出的字段编号相关联的字段名。字段编号从 0 开始。调用者不应该直接释放结果。 在相关联的`PGresult`句柄传递给`PQclear`之后,结果会被自动释放。 ``` 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 开始, 但是表字段编号不会是 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`返回在数据库行里面给该数据字段分配的空间, 换句话说就是该数据类型在服务器的内部表现形式的大小(尺寸)。(因此, 这个对客户端没有什么用。) 负值表示该数据类型是可变长度。 `PQbinaryTuples` 如果`PGresult`包含二进制数据时返回 1,如果包含文本数据返回 0。 ``` int PQbinaryTuples(const PGresult *res); ``` 这个函数已经废弃了(除了还用于与`COPY`连接之外), 因为我们可能在一个`PGresult`的某些字段里包含文本数据, 而另外一些字段包含二进制数据。更好的是使用`PQfformat`。 `PQbinaryTuples`只有在结果中的所有字段都是二进制(格式 1)的时候才返回 1。 `PQgetvalue` 返回一个`PGresult`里面一行的单独的一个字段的值。 行和字段编号从 0 开始。调用者不应该直接释放结果。在把`PGresult` 句柄传递给`PQclear`之后,结果会被自动释放。 ``` char *PQgetvalue(const PGresult *res, int row_number, int column_number); ``` 对于文本格式的数据,`PQgetvalue`返回的值是一个表示字段值的空(NULL) 结尾的字符串。对于二进制格式,返回的值就是由该数据类型的`typsend` 和`typreceive`决定的二进制表现形式。(在这种情况下, 数值实际上也跟着一个字节零,但是通常这个字节没什么用处,因为数值本身很可能包含内嵌的空。) 如果字段值是空,则返回一个空字串。参阅`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); ``` 这是特定数值的实际数据长度,也就是说,`PQgetvalue`指向的对象的大小。 对于文本数据格式,它和`strlen()`相同。对于二进制格式,这是基本信息。 请注意我们_不_应该依靠`PQfsize` 获取实际数据长度。 `PQnparams` 返回一个预备语句中的参数的数目。 ``` int PQnparams(const PGresult *res); ``` 只有在检查`PQdescribePrepared`的结果时,这个函数是有用的。 对于其他类型的查询将返回零。 `PQparamtype` 返回指示语句中的参数的数据类型。参数编号从0开始。 ``` Oid PQparamtype(const PGresult *res, int param_number); ``` 只有在检查`PQdescribePrepared`的结果时,这个函数是有用的。 对于其他类型的查询将返回零。 `PQprint` 向指定的输出流打印所有的行和(可选的)字段名称。 ``` void PQprint(FILE *fout, /* 输出流 */ const PGresult *res, const PQprintOpt *po); typedef struct { pqbool header; /* 打印输出字段头和行计数 */ pqbool align; /* 填充对齐字段 */ pqbool standard; /* 旧的格式 */ pqbool html3; /* 输出HTML表 */ pqbool expanded; /* 扩展表 */ pqbool pager; /* 必要时在输出中使用分页器 */ char *fieldSep; /* 字段分隔符 */ char *tableOpt; /* HTML表格元素的属性 */ char *caption; /* HTML表标题 */ char **fieldName; /* 替换字段名组成的空结尾的数组 */ } PQprintOpt; ``` 这个函数以前被psql用于打印查询结果,但是现在已经不用这个函数了。 请注意它假设所有的数据都是文本格式。 ## 31.3.3\. 检索其它命令的结果信息 这些函数用于从`PGresult`对象里检索其他信息。 `PQcmdStatus` 返回产生`PGresult`的 SQL 命令的命令状态标签。 ``` char *PQcmdStatus(PGresult *res); ``` 通常这只是命令的名字,但是它可能包括额外的数据,比如处理过的行数。调用者不应该直接释放结果。 结果会在把`PGresult`句柄传递给`PQclear`的时候释放。 `PQcmdTuples` 返回被 SQL 命令影响的行的数量。 ``` char *PQcmdTuples(PGresult *res); ``` 这个函数返回一个字符串,包含`PGresult`产生的SQL语句影响的行数。 这个函数只能用于下列的执行:`SELECT`,`CREATE TABLE AS`,`INSERT`, `UPDATE`,`DELETE`,`MOVE`,`FETCH`,或者 `COPY`语句,或者是一个包含`INSERT`,`UPDATE`或`DELETE` 语句的预备查询的`EXECUTE`。如果生成这个`PGresult`的命令是其他的东西, 那么`PQcmdTuples`返回一个空字串。调用者不应该直接释放返回的数值。 在相关联的`PGresult`被传递给`PQclear`之后,它会被自动释放。 `PQoidValue` 返回插入的行的OID, 如果SQL命令是`INSERT`,插入了正好一行到有OID的表格, 或者是一个包含合适`INSERT`语句的预备查询`EXECUTE`的时候。 否则,函数返回`InvalidOid`。如果受`INSERT` 影响的表不包含 OID,也返回`InvalidOid`。 ``` Oid PQoidValue(const PGresult *res); ``` `PQoidStatus` 为了支持`PQoidValue`,这个函数已经废弃了,并且不是线程安全的。 它返回插入行的带有OID的字符串,而`PQoidValue`返回OID值。 ``` char *PQoidStatus(const PGresult *res); ``` ## 31.3.4\. 逃逸包含在SQL命令中的字符串 `PQescapeLiteral` ``` char *PQescapeLiteral(PGconn *conn, const char *str, size_t length); ``` `PQescapeLiteral`为在 SQL 命令中使用字串而对之进行逃逸处理。 在我们向 SQL 命令里把数据值当作文本常量插入的时候很有用。有些字符 (比如单引号和反斜杠)必须被逃逸,以避免他们被 SQL 分析器作为特殊字符解析。 `PQescapeLiteral`执行这个操作。 `PQescapeLiteral`返回一个内存中分配有`malloc()`的 `str`参数的逃逸版本。当结果不再需要时,需要通过`PQfreemem()` 来释放这块内存。不需要一个0字节结束,并且不应以`length`计数。 (如果在处理`length`字节之前出现0字节的结束,`PQescapeLiteral` 在此处结束;这个行为有点像`strncpy`)。返回的字符串中所有特殊字符都替换掉了, 因此可以很好的被PostgreSQL字符串文本解析器处理,同样, 允许增加一个0字节结尾。必须在PostgreSQL 字符串文本两边的单引号包含在结果字符串中。 一旦错误,`PQescapeLiteral`返回`NULL`并在`conn`对象中存储合适的信息。 > **Tip:** 处理从不可信来源收到的字符串时必须进行合适的逃逸,否则存在一定的安全风险: 容易受到"SQL 注入"攻击,数据库中会被写入未知的SQL命令。 需要注意的是,当一个数据以`PQexecParams`或它的兄弟格式, 作为一个单独的参数传递时,做逃逸是不必要,也是不正确的。 `PQescapeIdentifier` ``` char *PQescapeIdentifier(PGconn *conn, const char *str, size_t length); ``` `PQescapeIdentifier`逃逸一个字符串作为一个SQL标识符使用, 如一个表,列,或函数名。当一个用户自定义标识符需要包含特殊的字符, 否则将不能被SQL解析器解析为标识符的一部分时,或者当标识符需要包含大写字母, 且这种情况必须保留时,这样做是很有用的。 `PQescapeIdentifier`返回`str`参数逃逸为一个内存中分配有 `malloc()`的SQL标识符的版本。当结果不再需要时,这块内存必须使用 `PQfreemem()`来释放。不需要一个0字节结束,并且不应以`length`计数。 (如果在处理`length`字节之前出现0字节的结束,`PQescapeIdentifier` 在此处结束;这个行为比较像`strncpy`)。返回的字符串中所有特殊字符都替换掉了, 因此可以很好的作为SQL标识符被处理。也可以添加一个结尾的0字节。返回字符串也是被双引号环绕。 出错时,`PQescapeIdentifier`返回`NULL`,并且在`conn`对象中存贮合适的信息。 > **Tip:** 由于带有字符串常量,为阻止SQL注入攻击,当从一个不可信任资源获得时,SQL标识符必须逃逸。 `PQescapeStringConn` ``` size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error); ``` `PQescapeStringConn`逃逸字符串常量,比较像`PQescapeLiteral`。 不同于`PQescapeLiteral`,请求应该提供一个适当大小的缓冲区。更重要的是, `PQescapeStringConn`不会生成一个必须在PostgreSQL 字符串常量两端的单引号;SQL命令中应该提供,这样结果中会被插入。`from` 参数指向字符串的第一个字符(用以逃逸),`length`参数指出了在这个字符串中的字节数。 不需要一个0字节结束,并且不应以`length`计数。 (如果在处理`length`字节之前出现0字节的结束,`PQescapeStringConn` 在此处结束;这个行为比较像`strncpy`)。`to`应该指向一个包含至少多于两倍 `length`大小的缓冲区,要么就不会定义该行为。如果`to`和`from` 字符串交叠,那么也不会定义该行为。 `error`参数非`NULL`,那么在成功的时候`*error`会被设置为零, 失败的时候设置为非0。目前唯一可能的错误条件涉及在源字符串中无效的多字节编码。 输出字符串同样产生错误,但服务器可以视其为异常以拒绝。一旦发生错误, 一条合适的信息会存储在`conn`对象中,无论`error`是否为`NULL`。 `PQescapeStringConn`返回写到`to`的字节数,不包含0字节终止。 `PQescapeString` `PQescapeString`是一个老的,已经被`PQescapeStringConn`弃用了的版本。 ``` size_t PQescapeString (char *to, const char *from, size_t length); ``` 与`PQescapeStringConn`唯一的不同是,`PQescapeString` 不使用`PGconn`或`error`参数。因此,不能够根据连接属性 (如字符编码)来调整其行为,因此_可能会给出错误的结果_,同样,不会报告错误条件。 `PQescapeString`可以在客户端编程(一次只有一个PostgreSQL连接) 中安全的使用。在这种情况下,它可以找到"在屏幕背后"想要知道的。在其他情况下, 这是一个安全隐患,使用`PQescapeStringConn`时应该避免。 `PQescapeByteaConn` 逃逸那些在 SQL 命令中使用的用`bytea`表示的二进制数据。 和`PQescapeStringConn`一样,这个函数只有在直接向 SQL 字串插入数据的时候使用。 ``` unsigned char *PQescapeByteaConn(PGconn *conn, const unsigned char *from, size_t from_length, size_t *to_length); ``` 在SQL语句中用做`bytea`字串文本的一部分的时候, 有些字节值必需逃逸。`PQescapeByteaConn`逃逸字节使用十六进制编码或反斜杠逃逸。 参阅[Section 8.4](#calibre_link-628)获取更多信息。 `from`参数指向需要逃逸的字串的第一个字节,`from_length` 参数反映在这个二进制字串(结尾的字节零既不必要也不计算在内)里字节的个数。 `to_length`参数指向一个变量,它保存逃逸后字符串长度的结果。 结果字串长度包括结果结尾的零字节。 `PQescapeByteaConn`在内存中返回一个`from` 参数的二进制字串的逃逸后的版本,这片内存是用`malloc()`分配的 在不再需要结果的时候,必须用`PQfreemem()`释放内存。 返回的字串已经把所有特殊的字符替换掉了,这样他们就可以由PostgreSQL 的字串文本分析器以及`bytea`的输入函数正确地处理。同时还追加了一个结尾的字节零。 那些必需包围在PostgreSQL字串文本周围的单引号并非结果字串的一部分。 当出错时,返回一个空指针,一个合适的错误信息会被储存在`conn`对象中, 当前唯一可能的错误是结果字符串的内存不足。 `PQescapeBytea` `PQescapeBytea`是`PQescapeByteaConn`的一个旧的,过时的版本。 ``` unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length); ``` 与`PQescapeByteaConn`唯一的不同之处在于,`PQescapeByteaConn` 不使用`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`参数。