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

embedded/2025/3/25 21:10:21/

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

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


http://www.ppmy.cn/embedded/176621.html

相关文章

ChatGPT降低论文AIGC重复率的提示词合集(高效降重方法)

&#x1f4a1; 问题&#xff1a;写完毕业论文后&#xff0c;查AIGC率过高&#xff0c;手动降重后仍然很高&#xff0c;该怎么办&#xff1f; &#x1f4cc; 解决方案&#xff1a; 1️⃣ 先查AIGC率&#xff08;找出AI生成的部分&#xff09; 2️⃣ 用ChatGPT优化&#xff08;使…

【10】高效存储MongoDB的用法

目录 一、什么是MongoDB 二、准备工作 &#xff08;1&#xff09;安装MongoDB ​&#xff08;2&#xff09;安装pymongo库 三、连接MongoDB 四、指定数据库 五、指定集合 六、插入数据 &#xff08;1&#xff09; insert 方法 &#xff08;2&#xff09;insert_one(…

【漏洞复现】Next.js 中间件认证绕过漏洞(CVE-2025-29927)漏洞复现

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 0x00 免责声明 本文所述漏洞复现方法仅供安全研究及授权测试使用&#xff1b;任何个人/组织须在合法合规…

浅谈canal实例 在docker里面安装canal镜像 Canal监听MySQL数据库变更并同步更新Redis和Elasticsearch 示例

目录 1. 环境准备 1.1 MySQL配置 1.2 部署Canal Server 2. Spring Boot项目配置 2.1 添加依赖 2.2 配置参数 3. 实现Canal监听与同步 3.1 Canal客户端监听 3.2 同步到Redis 3.3 同步到Elasticsearch 4. 注意事项 在Spring Boot中通过Canal监听MySQL数据库变更并同步…

「0基础学爬虫」爬虫基础之抓包工具的使用

抓包工具概述 抓包工具&#xff0c;顾名思义&#xff0c;就是抓取网络数据包信息的工具。抓包工具最初主要应用于测试工作中&#xff0c;通过抓包工具查看网络数据包&#xff0c;并进行分析&#xff0c;来定位数据传输中的问题。随着不断发展&#xff0c;抓包工具的功能不断拓…

【论文阅读】大型语言模型能否实现软件漏洞的检测与修复?

这篇文章翻译自 CAN LARGE LANGUAGE MODELS FIND AND FIX VULNERABLE SOFTWARE? 大型语言模型能否实现软件漏洞的检测与修复&#xff1f; 先说说结论和一些有意思的发现&#xff0c;以及这篇文章最重要的一个点&#xff1a; 那肯定是可以的&#xff0c; 此前实验已证实GPT-…

水星(MERCURY)监控初始化的恢复和转码方法

水星(MERCURY)的安防监控恢复了很多&#xff0c;其嵌入式文件系统也一直迭代更新。做为数据恢复从业者每天处理最多的就是恢复数据&#xff0c;但是有的时候业务的需要我们不仅仅恢复出数据&#xff0c;还需要能够转码成通用的MP4类文件并要求画面和声音实现“同步”。 故障存…

Ubuntu22.04通过DKMS包安装Intel WiFi系列适配器(网卡驱动)

下载驱动包 访问 backport-iwlwifi-dkmshttps://launchpad.net/ubuntu/source/backport-iwlwifi-dkms 网站&#xff0c;找到适用于Ubuntu 22.04的update版本&#xff08;如backport-iwlwifi-dkms_xxxx_all.deb&#xff09;&#xff0c;下载至本地。 安装驱动 在下载目录中执行以…