34.1. 数据库连接控制功能

以下功能处理与 PostgreSQL 后端服务器的连接。一个应用程序可以一次打开多个后端连接。 (这样做的一个原因是要访问多个数据库.)每个连接都由一个PGconn 对象表示,该对象是从函数PQconnectdbPQconnectdbParamsPQsetdbLogin获得的。请注意,这些函数将始终返回非空对象指针,除非可能甚至没有太多的内存来分配PGconn对象。在通过连接对象发送查询之前,应调用PQstatus函数以检查返回值是否成功连接。

Warning

如果不受信任的用户可以访问未采用安全模式使用模式的数据库,请通过从search_path删除可公开写模式来开始每个会话。可以将参数关键字options设置为值-csearch_path=。或者,可以在连接后发出PQexec(conn, "SELECT pg_catalog.set_config('search_path', '', false)")。这种考虑并非特定于 libpq;它适用于执行任意 SQL 命令的每个接口。

Warning

在 Unix 上,因为父进程和子进程共享相同的套接字和 os 资源,所以使用开放的 libpq 连接分叉进程可能导致无法预测的结果。因此,尽管从子进程执行exec来加载新的可执行文件是安全的,但不建议使用这种用法。

Note

在 Windows 上,如果重复启动和关闭单个数据库连接,则有一种提高性能的方法。在内部,libpq 分别调用WSAStartup()WSACleanup()来启动和关闭连接。 WSAStartup()递增内部 Windows 库引用计数,递减WSACleanup()。当引用计数仅为 1 时,调用WSACleanup()将释放所有资源,并卸载所有 DLL。这是一个昂贵的操作。为避免这种情况,应用程序可以手动调用WSAStartup(),因此在最后一个数据库连接关闭时不会释放资源。

PGconn *PQconnectdbParams(const char * const *keywords,
                          const char * const *values,
                          int expand_dbname);

此函数使用从两个NULL终止的数组中获取的参数来打开新的数据库连接。第一个keywords定义为字符串数组,每个字符串都是一个关键字。第二个values给出每个关键字的值。与下面的PQsetdbLogin不同,可以在不更改功能签名的情况下扩展参数集,因此对于新的应用程序编程,首选使用此功能(或其无阻塞模拟PQconnectStartParamsPQconnectPoll)。

当前识别的参数关键字在Section 34.1.2中列出。

expand_dbname非零时,* dbname 关键字值被允许识别为连接字符串。这样只会扩展 dbname 的第一个出现,随后的 dbname *值将作为纯数据库名称进行处理。有关可能的连接字符串格式的更多详细信息,请参见Section 34.1.1

传递的数组可以为空以使用所有默认参数,也可以包含一个或多个参数设置。它们的长度应匹配。处理将在keywords数组中的第一个NULL元素处停止。

如果任何参数是NULL或空字符串,那么将检查相应的环境变量(请参见Section 34.14)。如果也未设置环境变量,那么将使用指示的内置默认值。

通常,从这些数组的开头开始按索引 Sequences 处理关键字。这样的效果是,当重复关键词时,将保留最后处理的值。因此,通过仔细放置* dbname 关键字,可以确定 conninfo *字符串可以覆盖哪些内容,哪些不能覆盖。

PGconn *PQconnectdb(const char *conninfo);

此函数使用从字符串conninfo取得的参数打开新的数据库连接。

传递的字符串可以为空以使用所有默认参数,或者可以包含一个或多个用空格分隔的参数设置,或者可以包含 URI。有关详情,请参见Section 34.1.1

PGconn *PQsetdbLogin(const char *pghost,
                     const char *pgport,
                     const char *pgoptions,
                     const char *pgtty,
                     const char *dbName,
                     const char *login,
                     const char *pwd);

这是PQconnectdb的前身,它具有一组固定的参数。它具有相同的功能,只是缺少的参数将始终采用默认值。为任何默认的固定参数写NULL或一个空字符串。

如果* dbName 包含=符号或具有有效的连接 URI 前缀,则将其当作 conninfo *字符串,使用方式与传递给PQconnectdb的方式完全相同,然后按照指定的方式应用其余参数PQconnectdbParams

PGconn *PQsetdb(char *pghost,
                char *pgport,
                char *pgoptions,
                char *pgtty,
                char *dbName);

这是一个宏,它使用* login pwd *参数的空指针调用PQsetdbLogin。提供它是为了与非常老的程序向后兼容。

PGconn *PQconnectStartParams(const char * const *keywords,
                             const char * const *values,
                             int expand_dbname);

PGconn *PQconnectStart(const char *conninfo);

PostgresPollingStatusType PQconnectPoll(PGconn *conn);

这三个功能用于打开与数据库服务器的连接,从而在执行此操作时,不会在远程 I/O 上阻止应用程序的执行线程。这种方法的重点在于,awaitI/O 完成的过程可以发生在应用程序的主循环中,而不是发生在PQconnectdbParamsPQconnectdb内部,因此应用程序可以与其他活动并行 Management 此操作。

使用PQconnectStartParams时,使用从keywordsvalues数组中获取并由expand_dbname控制的参数进行数据库连接,如上面针对PQconnectdbParams所述。

使用PQconnectStart时,使用从字符串conninfo取得的参数构建数据库连接,如上面针对PQconnectdb所述。

只要满足许多限制,PQconnectStartParamsPQconnectStartPQconnectPoll都不会阻塞:

要开始无阻塞连接请求,请致电PQconnectStartPQconnectStartParams。如果结果为 null,则 libpq 无法分配新的PGconn结构。否则,将返回有效的PGconn指针(尽管尚未表示与数据库的有效连接)。下次通话PQstatus(conn)。如果结果为CONNECTION_BAD,则连接尝试已经失败,通常是由于无效的连接参数所致。

如果PQconnectStartPQconnectStartParams成功,则下一步是轮询 libpq,以便它可以 continue 进行连接序列。使用PQsocket(conn)获取数据库连接基础的套接字的 Descriptors。 (警告:请勿假定PQconnectPoll调用之间的套接字保持不变.)因此循环:如果PQconnectPoll(conn)最后返回PGRES_POLLING_READING,请 await 直到套接字准备好读取(如select()poll()或类似的系统功能所示)。然后再次致电PQconnectPoll(conn)。相反,如果PQconnectPoll(conn)最后返回PGRES_POLLING_WRITING,请 await 套接字准备好写入,然后再次调用PQconnectPoll(conn)。在第一次迭代中,即,如果您尚未调用PQconnectPoll,则其行为就像最后一次返回PGRES_POLLING_WRITING一样。continue 此循环,直到PQconnectPoll(conn)返回PGRES_POLLING_FAILED(表示连接过程已失败)或PGRES_POLLING_OK(表示连接已成功构建)。

在连接期间的任何时间,都可以通过调用PQstatus来检查连接的状态。如果此调用返回CONNECTION_BAD,则连接过程失败;否则,连接过程将失败。如果调用返回CONNECTION_OK,则连接已准备就绪。从如上所述的PQconnectPoll的返回值可以相等地检测到这两个状态。其他状态也可能在异步连接过程中(并且仅在此过程中)发生。这些指示连接过程的当前阶段,并且可能有助于例如向用户提供反馈。这些状态是:

请注意,尽管将保留这些常量(以保持兼容性),但应用程序永远不应依赖于以特定 Sequences 出现的常量,或者根本不依赖于这些记录值之一的状态。应用程序可能会执行以下操作:

switch(PQstatus(conn))
{
        case CONNECTION_STARTED:
            feedback = "Connecting...";
            break;

        case CONNECTION_MADE:
            feedback = "Connected to server...";
            break;
.
.
.
        default:
            feedback = "Connecting...";
}

使用PQconnectPoll时将忽略connect_timeout连接参数;应用程序有责任确定是否已花费过多时间。否则,PQconnectStart后跟PQconnectPoll循环等效于PQconnectdb

请注意,当PQconnectStartPQconnectStartParams返回非空指针时,必须在处理完PQfinish后调用PQfinish,以便处理该结构和任何关联的内存块。即使连接尝试失败或被放弃,也必须这样做。

PQconninfoOption *PQconndefaults(void);

typedef struct
{
    char   *keyword;   /* The keyword of the option */
    char   *envvar;    /* Fallback environment variable name */
    char   *compiled;  /* Fallback compiled in default value */
    char   *val;       /* Option's current value, or NULL */
    char   *label;     /* Label for field in connect dialog */
    char   *dispchar;  /* Indicates how to display this field
                          in a connect dialog. Values are:
                          ""        Display entered value as is
                          "*"       Password field - hide value
                          "D"       Debug option - don't show by default */
    int     dispsize;  /* Field size in characters for dialog */
} PQconninfoOption;

返回连接选项数组。这可用于确定所有可能的PQconnectdb选项及其当前默认值。返回值指向PQconninfoOption结构的数组,该数组以带有空keyword指针的条目结尾。如果无法分配内存,则返回空指针。请注意,当前默认值(val字段)将取决于环境变量和其他上下文。丢失或无效的服务文件将被静默忽略。呼叫者必须将连接选项数据视为只读。

处理 options 数组后,将其传递给PQconninfoFree以释放它。如果不这样做,则每次对PQconndefaults的调用都会泄漏少量内存。

PQconninfoOption *PQconninfo(PGconn *conn);

返回连接选项数组。这可以用来确定所有可能的PQconnectdb选项以及用于连接到服务器的值。返回值指向PQconninfoOption结构的数组,该数组以带有空keyword指针的条目结尾。上面针对PQconndefaults的所有 Comments 也适用于PQconninfo的结果。

PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);

解析连接字符串并以数组形式返回结果选项;或如果连接字符串有问题,则返回NULL。此函数可用于提取提供的连接字符串中的PQconnectdb选项。返回值指向PQconninfoOption结构的数组,该数组的结尾是具有空keyword指针的条目。

所有合法选项将出现在结果数组中,但是连接字符串中不存在的任何选项的PQconninfoOptionval设置为NULL;未插入默认值。

如果errmsg不是NULL,则成功时将*errmsg设置为NULL,否则设置为malloc d 错误字符串以说明问题。 (也可以将*errmsg设置为NULL,并且该函数返回NULL;这表明内存不足.)

处理 options 数组后,将其传递给PQconninfoFree以释放它。如果不这样做,每次调用PQconninfoParse都会泄漏一些内存。相反,如果发生错误并且errmsg不是NULL,请确保使用PQfreemem释放错误字符串。

void PQfinish(PGconn *conn);

请注意,即使服务器连接尝试失败(如PQstatus所示),应用程序也应调用PQfinish以释放PGconn对象使用的内存。在调用PQfinish之后,不得再次使用PGconn指针。

void PQreset(PGconn *conn);

该功能将关闭与服务器的连接,并尝试使用以前使用的所有相同参数来重新构建与同一服务器的新连接。如果工作连接丢失,这对于恢复错误可能很有用。

int PQresetStart(PGconn *conn);

PostgresPollingStatusType PQresetPoll(PGconn *conn);

这些功能将关闭与服务器的连接,并尝试使用以前使用的所有相同参数重新构建与同一服务器的新连接。如果失去有效的连接,这对于恢复错误很有用。它们与PQreset(以上)的不同之处在于它们以非阻塞方式运行。这些功能受到与PQconnectStartParamsPQconnectStartPQconnectPoll相同的限制。

要启动连接重置,请致电PQresetStart。如果返回 0,则重置失败。如果返回 1,则使用与使用PQconnectPoll创建连接完全相同的方式使用PQresetPoll轮询重置。

PGPing PQpingParams(const char * const *keywords,
                    const char * const *values,
                    int expand_dbname);

该函数返回以下值之一:

PGPing PQping(const char *conninfo);

返回值与PQpingParams相同。

34 .1.1. 连接字符串

几个 libpq 函数解析用户指定的字符串以获得连接参数。这些字符串有两种可接受的格式:纯keyword = value字符串和 URI。 URI 通常遵循RFC 3986,除了允许多主机连接字符串,如下所述。

34 .1.1.1. 关键字/值连接字符串

在第一种格式中,每个参数设置的格式均为keyword = value。等号周围的空格是可选的。要写入空值或包含空格的值,请用单引号将其括起来,例如keyword = 'a value'。值中的单引号和反斜杠必须使用反斜杠转义,即\'\\

Example:

host=localhost port=5432 dbname=mydb connect_timeout=10

可识别的参数关键字在Section 34.1.2中列出。

34 .1.1.2. 连接 URI

连接 URI 的一般形式为:

postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]

URI 方案指示符可以是postgresql://postgres://。每个 URI 部分都是可选的。以下示例说明了有效的 URI 语法用法:

postgresql://
postgresql://localhost
postgresql://localhost:5433
postgresql://localhost/mydb
postgresql://user@localhost
postgresql://user:secret@localhost
postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp
postgresql://host1:123,host2:456/somedb?target_session_attrs=any&application_name=myapp

URI 的层次部分的组件也可以作为参数给出。例如:

postgresql:///mydb?host=localhost&port=5433

百分比编码可用于在任何 URI 部分(例如,将=替换为%3D

Section 34.1.2中列出的关键字不对应的所有连接参数都将被忽略,有关它们的警告消息将发送到stderr

为了改善与 JDBC 连接 URI 的兼容性,将参数ssl=true的实例转换为sslmode=require

主机部分可以是主机名或 IP 地址。要指定 IPv6 主机地址,请将其括在方括号中:

postgresql://[2001:db8::1234]/database

主机组件的解释如参数host所述。特别是,如果主机部分为空或以斜杠开头,则选择 Unix 域套接字连接,否则将启动 TCP/IP 连接。但是请注意,斜杠是 URI 层次结构部分中的保留字符。因此,要指定非标准的 Unix 域套接字目录,请在 URI 中省略主机规范,并将主机指定为参数,或者在 URI 的主机组件中对路径进行百分比编码:

postgresql:///dbname?host=/var/lib/postgresql
postgresql://%2Fvar%2Flib%2Fpostgresql/dbname

可以在单个 URI 中指定多个主机组件,每个主机组件都带有一个可选的端口组件。形式为postgresql://host1:port1,host2:port2,host3:port3/的 URI 等效于形式为host=host1,host2,host3 port=port1,port2,port3的连接字符串。将依次尝试每个主机,直到成功构建连接。

34 .1.1.3. 指定多个主机

可以指定要连接的多个主机,以便按给定 Sequences 尝试它们。在关键字/值格式中,hosthostaddrport选项接受以逗号分隔的值列表。在指定的每个选项中必须给定相同数量的元素,例如第一个hostaddr对应于第一个主机名,第二个hostaddr对应于第二个主机名,依此类推。作为 exception,如果仅指定一个port,则它将应用于所有主机。

在连接 URI 格式中,您可以在 URI 的host组件中列出多个以逗号分隔的host:port对。

无论采用哪种格式,单个主机名都可以转换为多个网络地址。一个常见的示例是同时具有 IPv4 和 IPv6 地址的主机。

指定多个主机时,或将一个主机名转换为多个地址时,将按 Sequences 尝试所有主机和地址,直到成功为止。如果无法访问任何主机,则连接失败。如果构建连接成功,但是身份验证失败,则列表中的其余主机不会尝试。

如果使用密码文件,则可以为不同的主机使用不同的密码。列表中的每个主机的所有其他连接选项相同。例如,不可能为不同的主机指定不同的用户名。

34 .1.2. 参数关键字

当前公认的参数关键字是:

还接受以逗号分隔的主机名列表,在这种情况下,将按 Sequences 尝试列表中的每个主机名;列表中的空白项将选择默认行为,如上所述。有关详情,请参见Section 34.1.1.3

使用hostaddr而不是host可以使应用程序避免查找主机名,这在有时间限制的应用程序中可能很重要。但是,对于 GSSAPI 或 SSPI 身份验证方法以及verify-full SSL 证书验证,主机名是必需的。使用以下规则:

请注意,如果host不是网络地址hostaddr上的服务器名称,则身份验证很可能会失败。另外,同时指定hosthostaddr时,使用host标识密码文件中的连接(请参见Section 34.15)。

还接受以逗号分隔的hostaddr值列表,在这种情况下,列表中的每个主机将按 Sequences 尝试。列表中的空白项将导致使用相应的主机名,如果默认的主机名也为空,则使用该默认主机名。有关详情,请参见Section 34.1.1.3

如果没有主机名或主机地址,libpq 将使用本地 Unix 域套接字进行连接。或在没有 Unix 域套接字的计算机上,它将尝试连接到localhost

支持以下不区分大小写的值:

在物理或逻辑复制模式下,只能使用简单查询协议。

有关这些选项如何工作的详细说明,请参见Section 34.18

Unix 域套接字通信将忽略sslmode。如果 PostgreSQL 编译时没有 SSL 支持,则使用选项requireverify-caverify-full会导致错误,而选项allowprefer会被接受,但 libpq 实际上不会尝试进行 SSL 连接。

如果设置为 1,则需要与服务器构建 SSL 连接(相当于sslmode require)。如果服务器不接受 SSL 连接,那么 libpq 将拒绝连接。如果设置为 0(默认值),libpq 将与服务器协商连接类型(等效于sslmode prefer)。仅当 PostgreSQL 使用 SSL 支持编译时,此选项才可用。

如今,SSL 压缩被认为是不安全的,因此不再建议使用它。默认情况下,OpenSSL 1.1.0 禁用压缩,并且许多 os 发行版也在以前的版本中禁用压缩,因此,如果服务器不接受压缩,则将此参数设置为 on 不会有任何效果。另一方面,1.0.0 之前的 OpenSSL 不支持禁用压缩,因此对于那些版本,此参数将被忽略,是否使用压缩取决于服务器。

如果安全不是主要问题,那么如果网络成为瓶颈,压缩可以提高吞吐量。如果 CPU 性能是限制因素,则禁用压缩可以改善响应时间和吞吐量。

上一章 首页 下一章