33.3. 命令执行功能

成功构建与数据库服务器的连接后,此处描述的功能将用于执行 SQL 查询和命令。

33 .3.1. 主要功能

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

返回PGresult指针或可能为空的指针。通常会返回非空指针,除非在内存不足的情况下或严重错误(例如无法将命令发送到服务器)。应该调用PQresultStatus函数来检查返回值是否有错误(包括空指针的值,在这种情况下它将返回PGRES_FATAL_ERROR)。使用PQerrorMessage获得有关此类错误的更多信息。

命令字符串可以包含多个 SQL 命令(以分号分隔)。除非在查询字符串中包含明确的BEGIN/COMMIT命令以将其分为多个事务,否则在单个PQexec调用中发送的多个查询将在单个事务中处理。但是请注意,返回的PGresult结构仅描述了从字符串执行的最后一个命令的结果。如果其中一个命令失败,则字符串的处理随之停止,返回的PGresult描述了错误情况。

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 和更高版本的 Connecting 受支持;使用协议 2.0 时它将失败。

函数参数为:

以二进制格式传递的值需要了解后端期望的内部表示形式。例如,必须以网络字节 Sequences 传递整数。传递numeric值需要了解服务器存储格式,如src/backend/utils/adt/numeric.c::numeric_send()src/backend/utils/adt/numeric.c::numeric_recv()所实现。

PQexecParams优于PQexec的主要优点是可以将参数值与命令字符串分开,从而避免了乏味且容易出错的引号和转义。

PQexec不同,PQexecParams在给定的字符串中最多允许一个 SQL 命令。 (其中可以包含分号,但不能超过一个非空命令.)这是对基础协议的限制,但可以作为对 SQL 注入攻击的额外防御来使用。

Tip

通过 OID 指定参数类型很繁琐,尤其是如果您不想将特定的 OID 值硬连接到程序中时。但是,即使服务器本身无法确定参数的类型或选择与所需类型不同的类型,也可以避免这样做。在 SQL 命令文本中,将显式强制转换附加到参数符号以显示将发送的数据类型。例如:

SELECT * FROM mytable WHERE x = $1::bigint;

这将强制将参数$1视为bigint,而默认情况下会将其分配为与x相同的类型。在以二进制格式发送参数值时,强烈建议通过这种方式或通过指定数字类型 OID 来强制执行参数类型决定,因为二进制格式的冗余度小于文本格式,因此服务器检测类型的可能性较小为您错配错误。

PGresult *PQprepare(PGconn *conn,
                    const char *stmtName,
                    const char *query,
                    int nParams,
                    const Oid *paramTypes);

PQprepare创建一个准备好的语句,供以后使用PQexecPrepared执行。此功能允许重复执行命令,而无需每次都进行解析和计划。有关详情,请参见PREPAREPQprepare仅在协议 3.0 及更高版本的 Connecting 受支持;使用协议 2.0 时它将失败。

该函数从* query 字符串创建一个名为 stmtName *的准备好的语句,该语句必须包含一个 SQL 命令。 * stmtName 可以是""以创建未命名的语句,在这种情况下,任何现有的未命名的语句都会被自动替换;否则,如果在当前会话中已经定义了语句名称,则会出现错误。如果使用了任何参数,则它们在查询中称为$1$2等. nParams 是在数组 paramTypes[] 中预先指定类型的参数的数量。 (当 nParams 为零时,数组指针可以为NULL.) paramTypes[] 通过 OID 指定要分配给参数符号的数据类型。如果 paramTypes NULL,或者数组中的任何特定元素为零,则服务器将其数据类型分配给参数符号,方式与对无类型 Literals 字符串的处理方式相同。此外,查询可以使用数字符号大于 nParams *的参数符号;这些符号也将推断出数据类型。 (请参见PQdescribePrepared,以了解推断出哪些数据类型的方法.)

PQexec一样,结果通常是PGresult对象,其内容表示服务器端成功或失败。结果为空表示内存不足或根本无法发送命令。使用PQerrorMessage获取有关此类错误的更多信息。

也可以通过执行 SQL PREPARE语句来创建供PQexecPrepared使用的预备语句。同样,尽管没有 libpq 函数可删除准备好的语句,但 SQL DEALLOCATE语句可用于该目的。

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 和更高版本的 Connecting 受支持;使用协议 2.0 时它将失败。

参数与PQexecParams相同,除了给出的是预准备语句的名称而不是查询字符串,并且不存在* paramTypes[] *参数(由于创建时准备好的语句的参数类型是确定的,因此不需要此参数) 。

PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName);

PQdescribePrepared允许应用程序获取有关先前准备好的语句的信息。 PQdescribePrepared仅在协议 3.0 和更高版本的 Connecting 受支持;使用协议 2.0 时它将失败。

PGresult *PQdescribePortal(PGconn *conn, const char *portalName);

PQdescribePortal允许应用程序获取有关先前创建的门户的信息。 (libpq 不提供对门户的任何直接访问,但是您可以使用此功能来检查使用DECLARE CURSOR SQL 命令创建的游标的属性.)PQdescribePortal仅在协议 3.0 及更高版本的 Connecting 受支持;使用协议 2.0 时它将失败。

PGresult 结构封装了服务器返回的结果。 libpq 应用程序程序员应小心维护PGresult抽象。使用下面的访问器函数获取PGresult的内容。避免直接引用PGresult结构的字段,因为它们将来可能会发生变化。

ExecStatusType PQresultStatus(const PGresult *res);

PQresultStatus可以返回以下值之一:

如果结果状态为PGRES_TUPLES_OKPGRES_SINGLE_TUPLE,则可以使用下面描述的函数来检索查询返回的行。请注意,恰巧检索零行的SELECT命令仍显示PGRES_TUPLES_OKPGRES_COMMAND_OK用于永远不会返回行的命令(没有RETURNING子句的INSERTUPDATE等)。响应PGRES_EMPTY_QUERY可能表示 Client 端软件中存在错误。

状态PGRES_NONFATAL_ERROR的结果将永远不会被PQexec或其他查询执行功能直接返回;此类结果将传递到通知处理器(请参阅Section 33.12)。

char *PQresStatus(ExecStatusType status);
char *PQresultErrorMessage(const PGresult *res);

如果有错误,则返回的字符串将包含尾随换行符。调用方不应直接释放结果。当关联的PGresult句柄传递到PQclear时,它将被释放。

紧接PQexecPQgetResult调用之后,PQerrorMessage(在连接上)将返回与PQresultErrorMessage(在结果上)相同的字符串。但是,PGresult将保留其错误消息,直到被销毁,而连接的错误消息将在后续操作完成后更改。当您想了解与特定PGresult关联的状态时,请使用PQresultErrorMessage;当您想从连接的最新操作中了解状态时,请使用PQerrorMessage

char *PQresultVerboseErrorMessage(const PGresult *res,
                                  PGVerbosity verbosity,
                                  PGContextVisibility show_context);

在某些情况下,Client 可能希望获得以前报告的错误的更详细的版本。 PQresultVerboseErrorMessage通过计算在生成给定PGresult时对连接有效的指定详细设置对PQresultErrorMessage会产生的消息来满足此需求。如果PGresult不是错误结果,则会报告“ PGresult 不是错误结果”。返回的字符串包括尾随换行符。

与大多数其他用于从PGresult提取数据的函数不同,此函数的结果是一个新分配的字符串。当不再需要该字符串时,调用者必须使用PQfreemem()释放它。

如果没有足够的内存,则可能返回 NULL。

char *PQresultErrorField(const PGresult *res, int fieldcode);

可以使用以下域代码:

Note

仅为有限数量的错误类型提供模式名称,表名称,列名称,数据类型名称和约束名称的字段。参见Appendix A。不要以为任何这些字段的存在都可以保证另一个字段的存在。核心错误源遵循上面提到的相互关系,但是用户定义的函数可能以其他方式使用这些字段。同样,不要假定这些字段表示当前数据库中的当代对象。

Client 负责格式化显示的信息以满足其需求;特别是它应根据需要断开长行。错误消息字段中出现的换行符应视为段落分隔符,而不是换行符。

libpq 内部生成的错误将具有严重性和主要消息,但通常没有其他字段。 3.0 协议之前的服务器返回的错误将包括严重性和主要消息,有时还包括详细消息,但没有其他字段。

请注意,错误字段仅适用于PGresult个对象,而不适用于PGconn个对象;没有PQerrorField功能。

void PQclear(PGresult *res);

您可以根据需要保持PGresult对象的存在时间。发出新命令或关闭连接也不会消失。要摆脱它,您必须调用PQclear。否则,将导致应用程序中的内存泄漏。

33 .3.2. 检索查询结果信息

这些函数用于从表示成功查询结果的PGresult对象(即状态为PGRES_TUPLES_OKPGRES_SINGLE_TUPLE)中提取信息。它们也可以用于从成功的 Describe 操作中提取信息:Describe 的结果具有与实际执行查询所提供的所有列信息相同的信息,但其行数为零。对于具有其他状态值的对象,这些函数的作用就好像结果具有零行零列。

int PQntuples(const PGresult *res);
int PQnfields(const PGresult *res);
char *PQfname(const PGresult *res,
              int column_number);

如果列号超出范围,则返回NULL

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
Oid PQftable(const PGresult *res,
             int column_number);

如果列号超出范围,或者指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回InvalidOid。您可以查询系统表pg_class以确定确切引用了哪个表。

当您包含 libpq 头文件时,将定义类型Oid和常量InvalidOid。它们都将是某种整数类型。

int PQftablecol(const PGresult *res,
                int column_number);

如果列号超出范围,或者指定的列不是对表列的简单引用,或者使用 3.0 之前的协议,则返回零。

int PQfformat(const PGresult *res,
              int column_number);

格式代码零表示文本数据表示,而格式代码 1 表示二进制表示。 (其他代码保留以供将来定义.)

Oid PQftype(const PGresult *res,
            int column_number);

您可以查询系统表pg_type,以获得各种数据类型的名称和属性。内置数据类型的 OID 在源树的文件src/include/catalog/pg_type.h中定义。

int PQfmod(const PGresult *res,
           int column_number);

修饰符值的解释是特定于类型的。它们通常表示精度或尺寸限制。值-1 表示“无可用信息”。大多数数据类型不使用修饰符,在这种情况下,值始终为-1.

int PQfsize(const PGresult *res,
            int column_number);

PQfsize返回在数据库行中为此列分配的空间,换句话说,服务器内部数据类型表示的大小。 (因此,它对 Client 端并不是很有用.)负值表示数据类型为可变长度。

int PQbinaryTuples(const PGresult *res);

不建议使用此功能(与COPY结合使用时除外),因为单个PGresult可能在某些列中包含文本数据,而在另一些列中包含二进制数据。 PQfformat是首选。 PQbinaryTuples仅在结果的所有列均为二进制(格式 1)时返回 1.

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

对于文本格式的数据,由PQgetvalue返回的值是该字段值的以空值终止的字符串表示形式。对于二进制格式的数据,该值采用由数据类型的typsendtypreceive函数确定的二进制表示形式。 (在这种情况下,该值实际上也跟有一个零字节,但这通常不起作用,因为该值可能包含嵌入的 null.)

如果字段值为空,则返回一个空字符串。请参见PQgetisnull,以区分空值和空字符串值。

PQgetvalue返回的指针指向PGresult结构的一部分的存储。一个人不应该修改它指向的数据,如果要在PGresult结构本身的生存期之后使用它,则必须将其显式复制到其他存储中。

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

如果该字段为 null,则此函数返回 1;如果该字段包含非 null 值,则返回 0. (请注意,PQgetvalue将为空字段返回空字符串,而不是空指针.)

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

这是特定数据值的实际数据长度,即PQgetvalue指向的对象的大小。对于文本数据格式,它与strlen()相同。对于二进制格式,这是必不可少的信息。请注意,一个人不应该* PQfsize来获得实际的数据长度。

int PQnparams(const PGresult *res);

此功能仅在检查PQdescribePrepared的结果时有用。对于其他类型的查询,它将返回零。

Oid PQparamtype(const PGresult *res, int param_number);

此功能仅在检查PQdescribePrepared的结果时有用。对于其他类型的查询,它将返回零。

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 以前曾使用此函数来打印查询结果,但现在不再如此。请注意,它假定所有数据均为文本格式。

33 .3.3. 检索其他结果信息

这些功能用于从PGresult对象中提取其他信息。

char *PQcmdStatus(PGresult *res);

通常,这只是命令的名称,但是它可能包含其他数据,例如处理的行数。调用方不应直接释放结果。当关联的PGresult句柄传递到PQclear时,它将被释放。

char *PQcmdTuples(PGresult *res);

此函数返回一个字符串,其中包含受生成PGresult的 SQL 语句影响的行数。仅在执行SELECTCREATE TABLE ASINSERTUPDATEDELETEMOVEFETCHCOPY语句或包含INSERTUPDATEDELETE语句的已准备查询的EXECUTE之后,才能使用此函数。如果生成PGresult的命令是其他命令,则PQcmdTuples返回空字符串。调用者不应直接释放返回值。当关联的PGresult句柄传递给PQclear时,它将被释放。

Oid PQoidValue(const PGresult *res);
char *PQoidStatus(const PGresult *res);

33 .3.4. 转义包含在 SQL 命令中的字符串

char *PQescapeLiteral(PGconn *conn, const char *str, size_t length);

PQescapeLiteral转义用于 SQL 命令的字符串。在 SQL 命令中将数据值作为 Literals 常量插入时,这很有用。某些字符(例如引号和反斜杠)必须转义以防止它们由 SQL 解析器特别解释。 PQescapeLiteral执行此操作。

PQescapeLiteral在分配给malloc()的内存中返回* str 参数的转义版本。当不再需要结果时,应使用PQfreemem()释放此内存。不需要终止的零字节,也不应该在 length 中计数。 (如果在处理 length *字节之前找到了终止的零字节,则PQescapeLiteral会在零处停止;因此,行为就像strncpy一样.)返回字符串将替换所有特殊字符,以便 PostgreSQL 字符串可以对其进行正确处理。Literals 解析器。还添加了一个终止的零字节。结果字符串中包含必须包含 PostgreSQL 字符串 Literals 的单引号。

错误时,PQescapeLiteral返回NULL,并且适当的消息存储在* conn *对象中。

Tip

在处理从不可靠来源收到的字符串时,进行正确的转义尤为重要。否则存在安全风险:您容易受到“ SQL 注入”攻击的攻击,在这种攻击中,不需要的 SQL 命令会被馈送到数据库中。

请注意,当在PQexecParams或其同级例程中将数据值作为单独的参数传递时,进行转义既没有必要,也不正确。

char *PQescapeIdentifier(PGconn *conn, const char *str, size_t length);

PQescapeIdentifier转义一个字符串以用作 SQL 标识符,例如表,列或函数名。当用户提供的标识符可能包含特殊字符(否则 SQL 解析器不会将其解释为标识符的一部分),或者当标识符可能包含应保留大小写的大写字符时,此功能很有用。

PQescapeIdentifier返回通过malloc()分配的内存中作为 SQL 标识符转义的* str 参数的版本。当不再需要结果时,必须使用PQfreemem()释放此内存。不需要终止的零字节,也不应该在 length 中计数。 (如果在处理 length *字节之前找到终止的零字节,则PQescapeIdentifier会在零处停止;因此,行为就像strncpy一样.)返回字符串已替换了所有特殊字符,以便可以将其正确处理为 SQL 标识符。还添加了一个终止的零字节。返回字符串也将用双引号引起来。

错误时,PQescapeIdentifier返回NULL,并且适当的消息存储在* conn *对象中。

Tip

与字符串 Literals 一样,为防止 SQL 注入攻击,当从不可靠的来源收到 SQL 标识符时,必须对其进行转义。

size_t PQescapeStringConn(PGconn *conn,
                          char *to, const char *from, size_t length,
                          int *error);

PQescapeStringConn转义字符串 Literals,非常类似于PQescapeLiteral。与PQescapeLiteral不同,调用方负责提供适当大小的缓冲区。此外,PQescapeStringConn不会生成必须括在 PostgreSQL 字符串 Literals 中的单引号。应该在插入结果的 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);

PQescapeStringConn的唯一区别是PQescapeString不使用PGconn或* error 参数。因此,它无法根据连接属性(例如字符编码)调整其行为,因此它可能会给出错误的结果*。同样,它也无法报告错误情况。

PQescapeString可以安全地用于一次只能使用一个 PostgreSQL 连接的 Client 端程序中(在这种情况下,它可以找出“幕后”需要知道的内容)。在其他情况下,这是安全隐患,应避免使用PQescapeStringConn

unsigned char *PQescapeByteaConn(PGconn *conn,
                                 const unsigned char *from,
                                 size_t from_length,
                                 size_t *to_length);

当某些字节值用作 SQL 语句中byteaLiterals 的一部分时,必须转义。 PQescapeByteaConn使用十六进制编码或反斜杠转义符转义字节。有关更多信息,请参见Section 8.4

PQescapeByteaConn在分配给malloc()的内存中返回* from *参数二进制字符串的转义版本。当不再需要结果时,应使用PQfreemem()释放此内存。返回字符串将替换所有特殊字符,以便 PostgreSQL 字符串 Literals 解析器和byteaImporting 函数可以正确处理它们。还添加了一个终止的零字节。必须用 PostgreSQL 字符串 Literals 括起来的单引号不是结果字符串的一部分。

发生错误时,将返回空指针,并将适当的错误消息存储在* conn *对象中。当前,唯一可能的错误是结果字符串的内存不足。

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

PQescapeByteaConn的唯一区别是PQescapeBytea不采用PGconn参数。因此,PQescapeBytea只能在一次使用单个 PostgreSQL 连接的 Client 端程序中安全使用(在这种情况下,它可以“了解其幕后情况”)。如果在使用多个数据库连接的程序中使用(在这种情况下,请使用PQescapeByteaConn),它“可能会给出错误的结果”。

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

此转换与PQescapeBytea并不完全相反,因为从PQgetvalue接收到该字符串时,预计该字符串不会“转义”。特别是,这意味着不需要考虑字符串引用,因此不需要PGconn参数。

上一章 首页 下一章