postgres中的hook机制

news2025/1/11 16:41:58

瀚高数据库
目录
环境
文档用途
详细信息

环境

系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:14,13,12,11

文档用途

1.了解使用postgres中的hook机制,在不更改内核代码的前提下完成一些定制化需求;

2.从底层理解插件的加载过程,尤其是对于shared_preload_libraries配置文件选项。

详细信息

1.hooks的功能
PostgreSQL提供了一些钩子(hooks)机制,可以在服务器处理的特定时刻插入自定义代码,从而实现创建模块的方式,而无需修改核心代码。这些工具包括扩展(extensions)和自定义后台工作进程(custom worker backgrounds)。通过利用这些工具,PostgreSQL开发人员可以在服务器的不同环节插入自己的代码逻辑,扩展服务器的功能或实现自定义的行为,而不需要修改核心代码。这样可以使开发过程更加灵活和可扩展。

利用钩子(hooks),你可以实现许多功能,如创建自定义的查询规划器(custom planner)、输出自定义的解释计划(EXPLAIN)、运行个性化版本的实用工具,或者在执行层面控制查询处理。钩子机制允许你在特定的环节插入自定义的代码逻辑,以便在不修改核心代码的情况下对PostgreSQL进行定制化扩展。通过利用这些钩子,你可以修改查询规划过程、自定义解释计划的输出、添加个性化的实用工具或者控制查询在执行阶段的行为,从而满足特定的需求或实现定制化的功能。

2.从插件角度看hooks

/* contrib/pg_stat_statements/pg_stat_statments.c */



PG_MODULE_MAGIC;

/*

 * Module load callback

 */

void

_PG_init(void)

{

	/*

	 * In order to create our shared memory area, we have to be loaded via

	 * shared_preload_libraries.  If not, fall out without hooking into any of

	 * the main system.  (We don't throw error here because it seems useful to

	 * allow the pg_stat_statements functions to be created even when the

	 * module isn't active.  The functions must protect themselves against

	 * being called then, however.)

	 */

	if (!process_shared_preload_libraries_in_progress)

		return;



	/*

	 * Inform the postmaster that we want to enable query_id calculation if

	 * compute_query_id is set to auto.

	 */

	EnableQueryId();



   /* 此处省略了用户自定义GUC变量 */



	/*

	 * Install hooks.

	 */

	prev_shmem_request_hook = shmem_request_hook;

	shmem_request_hook = pgss_shmem_request;

	prev_shmem_startup_hook = shmem_startup_hook;

	shmem_startup_hook = pgss_shmem_startup;

	prev_post_parse_analyze_hook = post_parse_analyze_hook;

	post_parse_analyze_hook = pgss_post_parse_analyze;

	prev_planner_hook = planner_hook;

	planner_hook = pgss_planner;

	prev_ExecutorStart = ExecutorStart_hook;

	ExecutorStart_hook = pgss_ExecutorStart;

	prev_ExecutorRun = ExecutorRun_hook;

	ExecutorRun_hook = pgss_ExecutorRun;

	prev_ExecutorFinish = ExecutorFinish_hook;

	ExecutorFinish_hook = pgss_ExecutorFinish;

	prev_ExecutorEnd = ExecutorEnd_hook;

	ExecutorEnd_hook = pgss_ExecutorEnd;

	prev_ProcessUtility = ProcessUtility_hook;

	ProcessUtility_hook = pgss_ProcessUtility;

}

2.1 启动过程中处理需要预加载的动态库

 /*

  * process any libraries that should be preloaded at postmaster start

  */

 process_shared_preload_libraries();



 /* 省略初始化SSL库和计算Maxbackends */



 /*

  * Give preloaded libraries a chance to request additional shared memory.

  */

 process_shmem_requests();

2.1.1 读取libxxxx.so文件内容

/*

 * process any libraries that should be preloaded at postmaster start

 */

void

process_shared_preload_libraries(void)

{

     // process_shared_preload_libraries_xxxxx作为标志,用于判断

	process_shared_preload_libraries_in_progress = true;

	load_libraries(shared_preload_libraries_string,

				   "shared_preload_libraries",

				   false);

	process_shared_preload_libraries_in_progress = false;

	process_shared_preload_libraries_done = true;

}

2.1.2 调试过程
1.查看调用栈,load_libraries函数的参数分别如下,libraries是配置文件中用逗号分隔的动态库:

image.png

  1. 解析动态库列表,将每个动态库存放到数组中:本例只有1个pg_stat_statments,所以只有下标为0的位置有值。

image.png

  1. 下一步通过foreach宏遍历这个数组,对于其中的每一个元素分别调用load_file

image.png

load_file函数定义:

参数restricted限定了动态库所在位置,要到 l i b / p l u g i n s 下查找该动态库,如果为 f a l s e 则在 lib/plugins下查找该动态库,如果为false则在 lib/plugins下查找该动态库,如果为false则在lib下查找,在当前环境下restricted为false。

/*

 * This function loads a shlib file without looking up any particular

 * function in it.  If the same shlib has previously been loaded,

 * unload and reload it.

 *

 * When 'restricted' is true, only libraries in the presumed-secure

 * directory $libdir/plugins may be referenced.

 */

void

load_file(const char *filename, bool restricted)

{

	char	   *fullname;



	/* Apply security restriction if requested */

	if (restricted)

		check_restricted_library_name(filename);



	/* Expand the possibly-abbreviated filename to an exact path name */

	fullname = expand_dynamic_library_name(filename);



	/* Load the shared library */

	(void) internal_load_library(fullname);



	pfree(fullname);

}
  1. 由上面可知目前filename为"pg_stat_statments",下一步要确定该动态库的绝对路径。

image.png

  1. 根据动态库的绝对路径,进行动态库的加载,处理的过程分为2步:PG_MODULE_MAGIC这个函数将返回动态库的元数据信息,进行校验;再者就是执行_PG_init,保存老的钩子函数,安装该插件的钩子函数,此时就钩到内核中了。

下面代码来源:/src/backend/utils/fmgr/dfmgr.c:internal_load_library

遍历file_list链表,根据libname确定该动态库是否已经被加载过。

/*

 * Scan the list of loaded FILES to see if the file has been loaded.

 */

 for (file_scanner = file_list;file_scanner != NULL &&strcmp(libname, file_scanner->filename) != 0;file_scanner = file_scanner->next)		;

image.png

上述根据libname可能不准确,获取文件元数据信息,根据设备号和inode号再次确认

if (file_scanner == NULL)
{

   /*

    * Check for same files - different paths (ie, symlink or link)

    */

   if (stat(libname, &stat_buf) == -1)

	ereport(ERROR,

	     (errcode_for_file_access(),

		 errmsg("could not access file \"%s\": %m",

		  libname)));



    for (file_scanner = file_list;

			 file_scanner != NULL &&

			 !SAME_INODE(stat_buf, *file_scanner);

			 file_scanner = file_scanner->next)

			;

}
  1. 如果根据5的检查没有加载过该动态库,则打开该动态库文件。

file_scanner->handle是指向动态库文件的句柄,就像用FILE *接收fopen()的返回值一样。

RTLD_NOW:是对符号表中undefined的符号进行解析,这些符号比如变量或者函数不在本文件中声明。解析要发生在dlopen返回之前,因为我们马上要调用该动态库文件的函数,否则可能会报错:symbol unresolved…;

RTLD_GLOBAL:该文件中的符号对之后加载的动态库可见。

file_scanner = (DynamicFileList *)malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);

if (file_scanner == NULL)

	ereport(ERROR,

	           (errcode(ERRCODE_OUT_OF_MEMORY),

		errmsg("out of memory")));



MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));

strcpy(file_scanner->filename, libname);

file_scanner->device = stat_buf.st_dev;

#ifndef WIN32

	file_scanner->inode = stat_buf.st_ino;

#endif

file_scanner->next = NULL;

file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
  1. 继6之后开始校验

dlsym根据句柄和符号名(这里是一个函数名 “Pg_magic_func”)返回该符号在动态库文件加载到内存之后所在的地址。拿到这个地址就可以调用该函数了。

/* Check the magic function to determine compatibility */

 magic_func = (PGModuleMagicFunction)dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);

image.png

image.png

用readelf查看该动态库文件可以看出地址并不一样,因为动态库编译时一般都采用-fPIC(postion-independent code)选项,生成位置无关代码。

在明确获得magic_func地址之后,调用该函数,该函数返回一个magic_data_ptr类型的指针,指向一个用于校验的结构体:

if (magic_func)	

{

 	const Pg_magic_struct *magic_data_ptr = (*magic_func) ();

	if (magic_data_ptr->len != magic_data.len || memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)

	{

		/* copy data block before unlinking library */

		Pg_magic_struct module_magic_data = *magic_data_ptr;



		/* try to close library */

		dlclose(file_scanner->handle);

		free(file_scanner);



		/* issue suitable complaint */

		incompatible_module_error(libname, &module_magic_data);

	}

}

image.png

这些结构体的信息在pg_config_manual.h有详细的说明。

  1. 如果说动态库文件中有_PG_init函数,跟处理magic_func一样:调用扩展的函数,install hooks
PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");

if (PG_init)

    (*PG_init) ();

image.png

image.png

  1. 剩下就是将这个动态库文件元数据信息加入到链表,返回handle, 如果已经被加载过的动态库文件,直接走这里返回handle, 这部分调试过程省略。

10.根据2.1,之后会调用process_shmem_requests();之前pg_stat_statements有函数钩到这里,所以会在此处调用pg_stat_statments中的相关函数

image.png

image.png

后边就是插件计算需要的共享内存的大小,调试过程省略。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1524403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Visual Studio 2022 出现:E1696无法打开 源 文件 “stdafx.h“;E0020未定义标识符 “_TCHAR“错误解决办法

今天在用自己电脑上的visual studio2022写并行计算实验报告时出现了这种错误,但我在机房电脑vs2010版本运行时没有任何问题,于是我在网上找了解决办法,现总结如下。 1.报错E1696无法打开 源 文件 "stdafx.h",解决办法&a…

移动通信网络AT指令

AT 命令是用来控制 TE(如 PC 等用户终端)和 MT(如移动台等移动终端)之间交互 的规则 AT 命令的返回值包括两部分,响应信息和结果码。 PLMN 移动通信网络PLMN = MCC + MNC,PLMN由MCC移动国家码和MNC移动网络码组成,例如:中国移动GSM的PLMN为:46000(MCC:460, M…

Internet Download Manager(IDM下载) v6.42.3 绿色版介绍

互联网下载管理器是一个广泛使用的软件,它可以帮助用户更好地管理和加速他们的下载。最新版本v6.42.3已经发布,它带来了一系列新功能和改进,让用户更加方便和快速地下载他们需要的文件。 新版本的互联网下载管理器增加了对最新浏览器的支持&…

通过键盘对机械臂进行操作

1 #include<myhead.h>2 #include<linux/input.h>3 #define SER_PORT 88884 #define SER_IP "192.168.116.225"5 #define CLI_PORT 99996 #define CLI_IP "192.168.65.129"7 int main(int argc, const char *argv[])8 {9 //1、创建用于连接…

题目:特殊的三角形(蓝桥OJ 3008)

问题描述&#xff1a; 解题思路&#xff1a; 可以先求出1~1e6每个位置是否有解&#xff0c;后计算前缀和再求出不同区间的和。&#xff08;时间复杂度小&#xff09; 进行dfs操作&#xff1a;依次组合1~1e6所有元素。并计算每一个组合的乘积&#xff0c;在该乘积位置的cnt加一。…

2024.3.15

1.单向循环链表 代码&#xff1a; #include"loop.h" //创建单向循环链表 loop_p create_loop_list() {loop_p H (loop_p)malloc(sizeof(loop));if(HNULL){printf("空间申请失败\n");return NULL;}H->len0;H->nextH;return H; } //创建节点 loop_p…

AJAX概念和axios使用、URL、请求方法和数据提交、HTTP协议、接口、form-serialize插件

AJAX概念和axios使用 AJAX概念 AJAX就是使用XMLHttpRequest对象与服务器通信&#xff0c;它可以使用JSON、XML、HTML和text文本等格式发送和接收数据&#xff0c;AJAX最吸引人的就是它的异步特性&#xff0c;也就是说它可以在不重新刷新页面的情况下与服务器通信&#xff0c;…

2024全国水科技大会-协办单位:海南立昇净水科技实业有限公司

海南立昇净水科技实业有限公司 集膜技术研发、生产和销售服务于一体的集团型高科技企业。30年来一直专注于“膜”技术研发&#xff0c;始终坚持走自主科技创新之路。在国内构建起由26家直属分公司和超过500家经销代理商组成的营销服务网络&#xff0c;立昇净水设备被广泛应用于…

前端学习之css伪元素选择器

伪元素选择器 &#xff08;注释是对各个内容的解释与理解&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>伪元素选择器</title><!-- 双冒号开头一般都称为伪元素&#xff0c;…

STL容器之list类

文章目录 STL容器之list类1、list的介绍2、list的使用2.1、list的常见构造2.2、list的iterator的使用2.3、list空间增长问题2.4、list的增删查改2.5、list迭代器失效问题 3、list的模拟实现&#xff08;含反向迭代器&#xff09; STL容器之list类 1、list的介绍 list是序列容器…

C++初阶:1_C++入门

C入门 零.本节知识点安排目的 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff1a; 补充C语言语法的不足&#xff0…

【S5PV210_视频编解码项目】裸机开发:实现按键的外部中断处理

加粗样式本文所作内容&#xff1a; 基于S5PV210芯片实现按键的外部中断处理程序&#xff0c;搭建中断处理流程框架 S5PV210对于中断处理的操作流程 1 外部中断得到触发&#xff1a; 1&#xff09;外部中断在初始化阶段得到使能 2&#xff09;外界达到了外部中断的触发条件 …

Spring注解方式整合三层架构

注解方式特点 1. 完全注解方式指的是去掉xml文件&#xff0c;使用配置类 注解实现 2. xml文件替换成使用Configuration注解标记的类 3. 标记IoC注解&#xff1a;Component,Service,Controller,Repository 4. 标记DI注解&#xff1a;Autowired Qualifier Resource Value 5. &…

html--简历

文章目录 html html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"maximum-scale1.0,minimum-scale1.0,user-scalable0,widthdevice-width,initial-scale1.0&qu…

SqlServer2008(R2)(二)SqlServer2008(R2)安装和卸载注意事项整理

二、注意事项 1、 安装数据中心版 说明&#xff1a;此激活版仅用于测试和学习使用。 这是官方的下载页面&#xff08;需要付费订阅&#xff09;&#xff1a; http://msdn.microsoft.com/zh-cn/subscriptions/downloads/default.aspx 数据中心版&#xff1a; PTTFM-X467G-P7RH…

在Windows系统上搭建MongoDB-这篇文章刚刚好

在Windows系统上搭建MongoDB集群 文章目录 1.下载MongoDB2.集群描述3.构建集群文件目录4.新建配置文件5.启动MongoDB服务6.配置集群7.集群测试8.设置密码和开启认证一、安装MongoDB 1.下载MongoDB 去MongoDB官网下载解压版免安装的压缩包。 https://www.mongodb.com/try/do…

金枪鱼群优化算法TSO优化BiLSTM-ATTENTION实现风力发电功率预测(matlab)

金枪鱼群优化算法TSO优化BiLSTM-ATTENTION实现风力发电功率预测&#xff08;matlab&#xff09; TSO-BiLSTM-Attention金枪鱼群算法优化长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 金枪鱼群优化算法&#xff08;Tuna Swarm Optimization&#xff0c;TSO)是一…

一种动态联动的实现方法

安防领域中的联动规则 有安防领域相关的开发经历的人知道&#xff0c;IPCamera可以配置使能“侦测”功能&#xff0c;并且指定仅针对图像传感器的某个区载进行侦测。除了基本的“移动侦测"外&#xff0c;侦测的功能点还有细化的类别&#xff0c;如人员侦测、车辆侦测、烟…

python之前端css样式(一)

css ID选择器 #c1{color:red;#边框为红色border:1px solid red; } <div id"c2">中国移动</div> 类选择器 .xx{color:blue; } <div class"xx">中国联通</div> 标签选择器 li{color: pink; } <ul><li>北京</li…

C++作业day4

成员函数版本实现算术运算符的重载 全局函数版本实现算术运算符的重载 #include <iostream>using namespace std;class Person {friend const Person operator-(const Person &L,const Person &R); private:int a;int b; public:Person() {}Person(int a,int …