欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > PostgreSQL的学习心得和知识总结(一百七十二)|深入理解PostgreSQL数据库之连接服务文件的使用和实现

PostgreSQL的学习心得和知识总结(一百七十二)|深入理解PostgreSQL数据库之连接服务文件的使用和实现

2025/3/28 12:34:57 来源:https://blog.csdn.net/weixin_43949535/article/details/146406245  浏览:    关键词:PostgreSQL的学习心得和知识总结(一百七十二)|深入理解PostgreSQL数据库之连接服务文件的使用和实现

注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL master源码开发而成


深入理解PostgreSQL数据库之连接服务文件的使用和实现

  • 文章快速说明索引
  • 功能使用背景说明
  • 功能实现源码解析



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、连接服务文件的使用和实现


学习时间:

2025年03月20日 20:00:54


学习产出:

1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0

postgres=# select version();version                                     
---------------------------------------------------------------------------------PostgreSQL 18devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.1.0, 64-bit
(1 row)postgres=##-----------------------------------------------------------------------------#SQL> select * from v$version;          BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
CON_ID 0#-----------------------------------------------------------------------------#mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.06 sec)mysql>

功能使用背景说明

以下内容来自于PostgreSQL中文手册:

  • 34.17. 连接服务文件,点击前往

连接服务文件允许 libpq 连接参数与一个单一服务名称关联:

  1. 那个服务名称可以在一个 libpq 连接字符串里面指定,与其相关的设置将被使用。
  2. 这允许在不重新编译 libpq-using 应用的前提下修改连接参数。
  3. 服务名称也可以被使用PGSERVICE环境变量来指定。

服务名称可以在每个用户的服务文件或系统范围的文件中定义:

  1. 如果同一个服务名称存在于用户文件和系统文件中,则用户文件优先。默认情况下,每个用户的服务文件名为~/.pg_service.conf
  2. 在Microsoft Windows上,它的名称为%APPDATA%\postgresql\.pg_service.conf(其中%APPDATA%指用户配置文件夹中的应用数据子目录)。
  3. 可以通过设置环境变量PGSERVICEFILE来指定不同的文件名。系统范围的文件名为pg_service.conf
  4. 默认情况下,在PostgreSQL安装的etc目录中寻找 (使用pg_config --sysconfdir来准确识别此目录)。可以通过设置环境变量 PGSYSCONFDIR来指定另一个目录,但不能指定不同的文件名。

或者服务文件使用一种 INI 文件 格式,其中小节名是服务名并且参数是连接参数。列表见中文手册:第 34.1.2 节,点击前往。在PostgreSQL安装的share/pg_service.conf.sample中提供了一个例子文件:

[postgres@localhost:~/test/share]$ pwd
/home/postgres/test/share
[postgres@localhost:~/test/share]$ cat pg_service.conf.sample
#
#       Connection configuration file
#
# A service is a set of named connection parameters.  You may specify
# multiple services in this file.  Each starts with a service name in
# brackets.  Subsequent lines have connection configuration parameters of
# the pattern "param=value" or LDAP URLs starting with "ldap://"
# to look up such parameters.  A sample configuration for postgres is
# included in this file.  Lines beginning with '#' are comments.
#
# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and
# rename it pg_service.conf.
#
#
#[postgres]
#dbname=postgres
#user=postgres
[postgres@localhost:~/test/share]$

从服务文件中获取的连接参数与从其他来源获取的参数相结合(这个很重要,下面我们对这三种设置进行调试一下):

  1. 服务文件设置覆盖相应的环境变量
  2. 然后反过来可以由连接字符串中直接给出的值覆盖
# comment
[mydb]
host=somehost
port=5433
user=admin

例如,使用上面的服务文件,连接字符串service=mydb port=5434将使用主机somehost,端口5434, 用户admin,以及由环境变量或内置默认所设置的其他参数。


下面来看一个使用的案例,使用psql如下:

[postgres@localhost:~/test/bin]$ cat ~/.pg_service.conf 
#
#       Connection configuration file
#
# A service is a set of named connection parameters.  You may specify
# multiple services in this file.  Each starts with a service name in
# brackets.  Subsequent lines have connection configuration parameters of
# the pattern "param=value" or LDAP URLs starting with "ldap://"
# to look up such parameters.  A sample configuration for postgres is
# included in this file.  Lines beginning with '#' are comments.
#
# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and
# rename it pg_service.conf.
#
#
[sname01]
dbname=postgres
user=postgres
port=5432[sname02]
dbname=postgres
user=postgres
port=5432[postgres@localhost:~/test/bin]$ ./psql 
psql (18devel)
Type "help" for help.postgres=# \q
[postgres@localhost:~/test/bin]$ ./psql service=sname01
psql (18devel)
Type "help" for help.postgres=# \q
[postgres@localhost:~/test/bin]$ ./psql service=sname02
psql (18devel)
Type "help" for help.postgres=# \q
[postgres@localhost:~/test/bin]$

注:我这里修改一下PostgreSQL的源码,如下:

[postgres@localhost:~/postgres_gitee → master]$ git diff src/interfaces/libpq/exports.txt src/interfaces/libpq/fe-connect.c src/interfaces/libpq/libpq-fe.h > ./1.patch
[postgres@localhost:~/postgres_gitee → master]$ 
[postgres@localhost:~/postgres_gitee → master]$ cat 1.patch 
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index d5143766858..8188b8fe13e 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -210,3 +210,4 @@ PQsetAuthDataHook         207PQgetAuthDataHook         208PQdefaultAuthDataHook     209PQfullProtocolVersion     210
+PQapplicationname         211
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..c42d44af6bc 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -7393,6 +7393,14 @@ PQservice(const PGconn *conn)return conn->pgservice;}+char *
+PQapplicationname(const PGconn *conn)
+{
+       if (!conn)
+               return NULL;
+       return conn->appname;
+}
+char *PQuser(const PGconn *conn){
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 34ddfdb1831..0279c95101b 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -397,6 +397,7 @@ extern int  PQrequestCancel(PGconn *conn);/* Accessor functions for PGconn objects */extern char *PQdb(const PGconn *conn);extern char *PQservice(const PGconn *conn);
+extern char *PQapplicationname(const PGconn *conn);extern char *PQuser(const PGconn *conn);extern char *PQpass(const PGconn *conn);extern char *PQhost(const PGconn *conn);
[postgres@localhost:~/postgres_gitee → master]$

使用libpq的接口,如下:

#include <iostream>#include "libpq-fe.h"using namespace std;int main()
{// const char*conninfo="host=localhost port=5432 user=postgres dbname=postgres password=1";const char *conninfo = "service=sname02";const char *createTableCmd = "create temp table t1(id int, name text);";PGconn *conn = PQconnectdb(conninfo);if (PQstatus(conn) == CONNECTION_OK){PGresult *result = NULL;cout << "连接PostgreSQL数据库 成功!" << endl;result = PQexec(conn, createTableCmd);if (result != NULL && PQresultStatus(result) == PGRES_COMMAND_OK){cout << "创建表成功" << endl;}else{cout << "创建表失败, errorMessage是" << PQerrorMessage(conn);}char *service = PQservice(conn);char *appname = PQapplicationname(conn);cout << "PQservice:" << (service == NULL ? "NULL" : service) << endl;cout << "PQapplicationname:" << (appname == NULL ? "NULL" : appname) << endl;PQfinish(conn);cout << "与PostgreSQL数据库连接 关闭!" << endl;}else{cout << "连接失败!" << endl;}return 0;
}/* g++ libpqtest.cpp -lpq -L/home/postgres/test/lib -I/home/postgres/test/include -o main -g -O0 */
/* export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PG_HOME/lib */

编译执行该libpq应用,如下:

[postgres@localhost:~]$ g++ libpqtest.cpp -lpq -L/home/postgres/test/lib -I/home/postgres/test/include -o main -g -O0
[postgres@localhost:~]$ 
[postgres@localhost:~]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PG_HOME/lib
[postgres@localhost:~]$ 
[postgres@localhost:~]$ ./main 
连接PostgreSQL数据库 成功!
创建表成功
PQservice:sname02
PQapplicationname:NULL
与PostgreSQL数据库连接 关闭!
[postgres@localhost:~]$

于是如上,这就允许了:在不重新编译 libpq 应用的前提下修改连接参数,如下:

image-20250320134849148

或者,如下:

image-20250320135328841


功能实现源码解析

在开始之前,我们修改一下相关内容:

修改1:

postgres=# create user u1 with superuser password '1';
CREATE ROLE
postgres=# \duList of rolesRole name |                         Attributes                         
-----------+------------------------------------------------------------postgres  | Superuser, Create role, Create DB, Replication, Bypass RLSu1        | Superuserpostgres=# \c postgres u1
You are now connected to database "postgres" as user "u1".
postgres=#

修改2:

image-20250320140334199

修改3:

[postgres@localhost:~/postgres_gitee → master]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PG_HOME/lib
[postgres@localhost:~/postgres_gitee → master]$ export PGAPPNAME=song01
[postgres@localhost:~/postgres_gitee → master]$

开始调试,如下:

输入的连接字符串,如下:

// conninfo0x400ba0 "service=sname02 user=u1"

image-20250320141740365

解释一下:

  1. 在解析上面连接字符串中得到了service,因为我这里不设置PGSERVICEPGSERVICEFILE 因此找的service文件就是家目录下的
  2. 大家在使用时可以根据自己的需要自行去配置

接下来就是在函数parseServiceFile中解析service文件,来匹配对应的service:

image-20250320142359556

然后就开始解析该service的具体配置,如下:

image-20250320142819546

继续后面的userportapplication_name

这里需要注意的是:因为在连接字符串中已经指定了user,所以这里的服务文件中的值不能覆盖,如下:

image-20250320143750045


在处理service的逻辑parseServiceInfo()->parseServiceFile()之后,就要进入到下面for循环处理尚未指定的option:

// src/interfaces/libpq/fe-connect.c/** Add the default values for any unspecified options to the connection* options array.* 将任何未指定选项的默认值添加到连接选项数组。** Defaults are obtained from a service file, environment variables, etc.* 默认值从服务文件、环境变量等获取。** Returns true if successful, otherwise false; errorMessage, if supplied,* is filled in upon failure.  Note that failure to locate a default value* is not an error condition here --- we just leave the option's value as* NULL.* 如果成功则返回 true,否则返回 false;* 如果提供了 errorMessage,则在失败时填写。* 请注意,无法找到默认值在这里不属于错误情况 --- 我们只是将选项的值保留为 NULL。*/
static bool
conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
{.../** If there's a service spec, use it to obtain any not-explicitly-given* parameters.  Ignore error if no error message buffer is passed because* there is no way to pass back the failure message.* 如果有服务规范,则使用它来获取任何未明确给出的参数。* 如果没有传递错误消息缓冲区,则忽略错误,因为没有办法传回失败消息。*/if (parseServiceInfo(options, errorMessage) != 0 && errorMessage)return false;.../** Get the fallback resources for parameters not specified in the conninfo* string nor the service.* 获取 conninfo 字符串或服务中未指定的参数的后备资源。*/for (option = options; option->keyword != NULL; option++){...}...
}

这里说明一下各相关option的值:

user: u1 // 来源于conninfo
dbname: postgres // 来源于service文件
port: 5432 // 来源于service文件
application_name: whatever // 来源于service文件

从上面可以看待配置的环境变量PGAPPNAME并没有起作用,因为该option.val已经从service文件中获取到了。下面我们将service文件中的对应行删掉,(这次在上面for循环的处理)如下:

image-20250320151330177

其结果如下:

image-20250320151501341

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词