PostgreSQL 插件 CREATE EXTENSION 原理
PostgreSQL 提供了丰富的数据库内核编程接口,允许开发者在不修改任何 Postgres 核心代码的情况下以插件的形式将自己的代码融入内核,扩展数据库功能。本文探究了 PostgreSQL 插件的一般源码组成,梳理插件的源码内容和实现方式;并介绍了 PostgreSQL 内核源码中处理 CREATE EXTENSION 创建插件的实现原理。
01 PostgreSQL 插件源码组成
PostgreSQL 中运行 CREATE EXTENSION
命令创建指定插件,最少需要两个文件:
- 插件控制文件 extension_name.control:这个文件必须放置在 PostgreSQL 安装目录中的
$PGHOME/share/postgresql/extension
目录下,该文件的后缀必须为 .control且文件名与插件名称相同。 - SQL 脚本文件 extension_name–version.sql:一个插件至少有一个或多个 SQL 脚本文件,这些文件通常也放在 PostgreSQL 安装目录中的
$PGHOME/share/postgresql/extension
目录下,但是可以通过控制文件可以为脚本文件指定不同的目录,这些文件的名称由插件名称和插件版本两个部分组成。
实现复杂功能的插件还有实现具体功能的源码文件,通常是 .c
文件,这些文件包含了插件功能实现的实际代码。这些源码文件通过 Makefile 文件使用 make 工具编译成动态库文件 extension_name.so
,这个动态库文件名称通常也与插件名称一致,
最后,Makefile 文件中根据安装选择配置,将上述提到的三种文件复制到 PostgreSQL 指定的目录下完成安装过程。
下面,我们以 pg_qualstats 插件(pg_qualstats:https://github.com/powa-team/pg_qualstats 用于收集和展示查询语句的 WHERE 和 JOIN 等过滤条件相关的统计信息)为例详细了解一下插件中必有的控制文件、SQL 脚本文件和 Makefile 文件中的具体内容:
1.1 控制文件 control
PostgreSQL 插件的 control 文件是一个文本文件,用于描述插件的元数据信息和安装过程中的操作;这个文件必须要位于插件的根目录下,并且后缀必须命名为 control。
pg_qualstats.control
文件的内容如下,包括描述插件的作用和用途的 comment;指定插件默认版本号的 default_version;和指定插件的共享库文件路径和名称的 module_pathname;以及指定插件是否可以在不同的 PostgreSQL 安装路径下运行的 relocatable。
comment = 'An extension collecting statistics about quals'
default_version = '2.1.0'
module_pathname = '$libdir/pg_qualstats'
relocatable = false
除了上面这些字段,control 文件还有如下字段用于设置插件元数据信息:
- directory:指定插件的安装目录;默认情况下,插件会被安装到
$PGHOME/share/postgresql/extension
目录下;如果需要安装到其他目录,可以通过这个参数进行设置。- default_version:指定插件的默认版本号;如果没有指定版本号,则默认为 1.0。
- comment:插件的注释信息,用于描述插件的作用和用途。
- encoding:指定插件的编码格式;默认情况下,插件会使用数据库的编码格式;如果需要使用其他编码格式,可以通过这个参数进行设置。
- module_pathname:指定插件的共享库文件路径和名称。
- requires:指定插件所依赖的其他插件。
- superuser:指定可以安装和卸载插件的超级用户;默认情况下,只有超级用户可以安装和卸载插件;如果需要允许其他用户进行安装和卸载操作,可以通过这个参数进行设置。
- relocatable:指定插件是否可以在不同的 PostgreSQL 安装路径下运行。
- schema:指定插件的安装模式,默认使用 public 模式。
1.2 SQL 脚本文件
插件的 sql 脚本是插件的核心部分,它定义了插件的功能和行为;用户可以根据插件的 sql 脚本来了解插件的使用方法和功能,以及如何集成插件到自己的应用程序中。
例如 pg_qualstats--2.1.0.sql
文件的内容包括该插件所需函数和视图的声明
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_qualstats" to load this file. \quitCREATE FUNCTION @extschema@.pg_qualstats_reset()
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C;CREATE FUNCTION @extschema@.pg_qualstats_example_query(bigint)
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C;
通常 PostgreSQL 插件的 sql 脚本一般包含以下内容:
- 创建插件所需的表、函数、视图等对象:这些对象是插件的核心功能实现,是插件的主要部分。
- 创建插件所需的配置项:插件通常需要一些配置项来控制其行为,例如日志级别、缓存大小等;这些配置项可以通过
ALTER SYSTEM
或ALTER DATABASE
命令进行设置。- 创建插件所依赖的其他插件:插件的 sql 脚本可以使用
CREATE EXTENSION
命令方便地加载和卸载其他插件。- 提供一些示例代码:插件的 sql 脚本通常会提供一些示例代码,以便用户可以更好地理解插件的使用方法和功能;这些示例代码通常包含一些 SQL 查询语句,用于演示插件的使用方法和效果。
1.3 makefile 与动态库文件
实现复杂功能的插件通常还会有实现具体功能的源码文件,这些源码文件通过 Makefile 文件使用 make 工具编译成动态库文件 extension_name.so
,并与其他文件一起安装到 postgresql 中。
例如,pg_qualstats.c 文件中就是使用 C 语言编码,实现了大量复杂功能处理逻辑。
Makefile 文件除了进行源码文件的编译工作,还会配置其他的查询编译安装选项
例如,pg_qualstats 插件中的 Makefile 文件中,EXTENSION 字段指定额插件的名称;MODULES 指定需要编译的插件源码文件;PG_CONFIG 指定了 PostgreSQL 的 pg_config 命令的路径;DATA 则是指定需要安装的插件数据文件的名称;PGXS 指定 PostgreSQL 扩展构建系统的路径。
EXTENSION = pg_qualstats
EXTVERSION = $(shell grep default_version $(EXTENSION).control | sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/")
TESTS = $(wildcard test/sql/*.sql)
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS = --inputdir=test
MODULES = $(patsubst %.c,%,$(wildcard *.c))
PG_CONFIG ?= pg_configall:release-zip: allgit archive --format zip --prefix=pg_qualstats-$(EXTVERSION)/ --output ./pg_qualstats-$(EXTVERSION).zip HEADunzip ./pg_qualstats-$(EXTVERSION).ziprm ./pg_qualstats-$(EXTVERSION).zipsed -i -e "s/__VERSION__/$(EXTVERSION)/g" ./pg_qualstats-$(EXTVERSION)/META.jsonzip -r ./pg_qualstats-$(EXTVERSION).zip ./pg_qualstats-$(EXTVERSION)/rm ./pg_qualstats-$(EXTVERSION) -rfDATA = $(wildcard *--*.sql)
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
除了上面介绍的字段,插件的 Makefile 文件还有如下字段用于配置插件编译安装选项:
- EXTENSION:指定插件的名称;使用 CREATE EXTENSION 命令安装插件时,使用该名称引用插件。
- EXTVERSION:指定插件的版本号;使用 CREATE EXTENSION 命令安装插件时,则该版本号将与插件关联。
- REGRESS:指定需要运行的插件测试脚本文件。
- REGRESS_OPTS:指定运行插件测试时的选项;例如,可以使用 -k 选项指定只运行包含特定关键字的测试用例。
- TESTS:指定需要运行的插件测试文件;与 REGRESS 不同的是,TESTS 不包括插件的初始化和清理过程。
- MODULES:指定需要编译的插件源码文件。
- PG_CONFIG:指定 PostgreSQL 的 pg_config 命令的路径。
- DATA:指定需要安装的插件数据文件。
- DOCS:指定需要安装的插件文档文件的名称。
- PGXS:指定 PostgreSQL 扩展构建系统的路径。
- CFLAGS:指定 C 编译器的选项。
02 实现一个简单的插件
为了更好的理解,PostgreSQL 插件的实现方式,这里给出一个简单的插件实现 demo 更好地体会插件实现与安装过程
我们创建一个名为 char_count
的简单插件,该插件的功能是计算给定字符串的指定字符的数量。该插件提供了一个函数 char_count(TEXT, CHAR)
,这个函数有两个输入参数,第一个是字符串,第二个是需要统计数量的字符;函数的返回值是一个整数,表示字符串中所需字符的出现次数。
2.1 使用 plpqsql 实现插件
使用 CREATE EXTENSION
创建插件地最基础构成是上一节说明地三个文件:控制文件,SQL 脚本文件和 Makefile 文件
下面我们分别实现这三个文件:
(1)控制文件 char_count.control
首先编写控制文件,主要设置了 comment,default_version,module_pathname 和 relocatable 四个选项
#char_count extension
comment = 'function to count number of specified characters'
default_version = '1.0'
module_pathname = '$libdir/char_count'
relocatable = true
(2)SQL 脚本文件 char_count–1.0.sql
SQL 脚本文件的命名中注意与插件名称和版本一致,脚本中主要定义了 char_count(TEXT, CHAR)
这个函数,这个函数有两个输入参数,第一个 TEXT
是目标字符串,第二个 CHAR
是需要统计数量的字符;函数的返回值 charCount
是一个整数,表示字符串中指定字符的出现次数。
\echo Use "CREATE EXTENSION char_count" to load this file. \quitCREATE OR REPLACE FUNCTION char_count(TEXT, CHAR) RETURNS INTEGER AS $$
DECLAREcharCount INTEGER := 0;i INTEGER := 0;inputText TEXT := $1;targetChar CHAR := $2;
BEGIN
WHILE i <= length(inputText) LOOPIF substring( inputText from i for 1) = targetChar THENcharCount := charCount + 1;END IF;i := i + 1;END LOOP;RETURN(charCount);
END;
$$ LANGUAGE plpgsql;
(3)Makefile
Makefile 文件中我们指定了几个关键的字段,插件名称 EXTENSION,DATA 指定需要安装的 SQL 脚本文件,PG_CONFIG 和 PGXS分别指定当前 pg 安装的 pg_config 命令路径与插件构建所需的系统路径。
EXTENSION = char_count
DATA = char_count--1.0.sql
PGFILEDESC = "char_count - count number of specified character"
REGRESS = char_countifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/char_count
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
完成基本文件准备之后,一个插件就写完了,现在我们来安装这个简单的插件
$ tree char_count
char_count
├── Makefile
├── char_count--1.0.sql
└── char_count.control0 directories, 3 files
$ cd char_count
$ make install USE_PGXS=1
/usr/bin/mkdir -p '/home/randy/soft/postgresql/share/extension'
/usr/bin/mkdir -p '/home/randy/soft/postgresql/share/extension'
/usr/bin/install -c -m 644 .//char_count.control '/home/randy/soft/postgresql/share/extension/'
/usr/bin/install -c -m 644 .//char_count--1.0.sql '/home/randy/soft/postgresql/share/extension/'
$ pg_ctl -D $PGDATA -l $PGLOG/logfile start
waiting for server to start.... done
server started
$ psql -Upostgres -dpostgres -p5432
Password for user postgres:
psql (12.7)
Type "help" for help.postgres=# CREATE EXTENSION char_count;
CREATE EXTENSION
postgres=# \dxList of installed extensionsName | Version | Schema | Description
------------+---------+------------+--------------------------------------------------char_count | 1.0 | public | function to count number of specified charactersplpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(2 rows)postgres=# select char_count('121321321312311111132132131','1');char_count
------------14
(1 row)postgres=# select char_count('Hello, World!','l');char_count
------------3
(1 row)
这个简单插件的安装使用过程如下,确保 pg_config 的路径被配置到系统环境变量中之后,我们使用 make install 命令安装该插件
然后使用 psql 登陆到 postgres 之后使用 CREATE EXTENSION
命令创建插件 char_count
;之后就可以直接使用 SQL 语句调用 char_count(TEXT, CHAR)
函数了
2.2 使用 C 语言实现插件
上面我们使用 SQL 语句直接在 SQL 脚本中实现了 char_count(TEXT, CHAR)
函数,我们也可以使用 C 语言实现函数功能,然后在 SQL 脚本文件中声明该函数,这也是 PostgreSQL 实现插件更加常见的方式
使用 C 语言实现插件,我们需要对上面的实现过程做如下修改:
- 添加 C 语言源码文件
char_count.c
- 修改
char_count.control
控制文件,仅声明函数 - 修改 Makefile 文件添加 MODULES 指定源码文件
首先,我们添加 char_count.c
源码文件内容如下,实现字符计数功能
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"PG_MODULE_MAGIC;PG_FUNCTION_INFO_V1(char_count);Datum
char_count(PG_FUNCTION_ARGS)
{int charCount = 0;int i = 0;text * inputText = PG_GETARG_TEXT_PP(0);text * targetChar = PG_GETARG_TEXT_PP(1);int inputText_sz = VARSIZE(inputText)-VARHDRSZ;int targetChar_sz = VARSIZE(targetChar)-VARHDRSZ;char * cp_inputText = NULL;char * cp_targetChar = NULL;if ( targetChar_sz > 1 ){elog(ERROR, "arg1 must be 1 char long");}cp_inputText = (char *) palloc ( inputText_sz + 1);cp_targetChar = (char *) palloc ( targetChar_sz + 1);memcpy(cp_inputText, VARDATA(inputText), inputText_sz);memcpy(cp_targetChar, VARDATA(targetChar), targetChar_sz);elog(INFO, "arg0 length is %d, value %s", (int)strlen(cp_inputText), cp_inputText );elog(INFO, "arg1 length is %d, value %s", (int)strlen(cp_targetChar), cp_targetChar );while ( i < strlen(cp_inputText) ){if( cp_inputText[i] == cp_targetChar[0] )charCount++;i++;}pfree(cp_inputText);pfree(cp_targetChar);PG_RETURN_INT32(charCount);
}
然后,修改 char_count.control
控制文件,仅声明 char_count
函数
\echo Use "CREATE EXTENSION char_count" to load this file. \quit
CREATE FUNCTION char_count_c(TEXT, CHAR) RETURNS INTEGER
AS '$libdir/char_count'
LANGUAGE C IMMUTABLE STRICT
最后,修改 Makefile 文件添加 MODULES 指定源码文件
EXTENSION = char_count
DATA = char_count--1.0.sql
PGFILEDESC = "char_count - count number of specified character"
REGRESS = char_count
MODULES = char_countifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/char_count
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
因为,增加了源码文件,所以在安装过程中,我们需要使用 make
命令将源码文件编译为动态库文件 char_count.so
;然后再使用 make install
命令将控制文件 char_count.control
和脚本文件 char_count--1.0.sql
复制到 $PGHOME/share/postgresql/extension
目录,将 char_count.so
文件复制到 $PGHOME/lib
目录下
cd char_count
make && make install
03 PostgreSQL 创建插件实现原理
PostgreSQL 内核中使用 CREATE EXTENSION
创建插件时,首先,会解析插件的控制文件,取出需要执行的 sql 脚本版本号,同时根据控制文件的插件元数据信息创建 extension_oid
一起存入到系统表 pg_extension 中。
然后,通过解析并执行相对应的 sql 脚本文件,完成插件所需的视图和函数等内容的创建。如果使用的是源码文件创建的函数,在调用 CREATE FUNCTION
创建函数时,会通过加载插件动态库 so 文件来调用具体函数。
下面,通过控制文件处理过程,SQL 脚本文件路径查找过程和动态库函数调用过程三个部分详细介绍创建插件实现原理。
3.1 控制文件处理过程
PostgreSQL 中使用 CREATE EXTENSION
创建插件时控制文件的处理过程如下图所示:
ProcessUtilitySlow
函数解析到的 SQL 命令是 CREATE EXTENSION
时,就会调用 CreateExtension
函数执行具体插件创建过程;
该过程中使用 read_extension_control_file
和 parse_extension_control_file
两个函数读取并解析插件控制文件中的信息,通常包括扩展的名称、版本、依赖关系等;
然后,调用 InsertExtensionTuple
函数将控制文件中包含的插件元数据信息插入到系统表 pg_extension 中;
接着,调用 execute_extension_script
和 execute_sql_string
函数,根据获取到的 SQL 脚本路径,读取并执行 SQL 脚本;执行过程与 PostgreSQL 中一般的查询处理过程一致:
- 调用 pg_parse_query 将 sql 转换成 parsetree_list;
- 执行 pg_analyze_and_rewrite 对 SQL 脚本进行解析和重写;
- 调用 pg_plan_queries() 函数生成查询计划;
- 调用 ExecutorRun 对每一个 parse tree 进行执行。
上述过程中各函数作用的具体说明如下:
- ProcessUtilitySlow():处理 SQL 命令的函数,它会对传入的SQL命令进行解析和执行;如果 SQL 命令是创建插件的命令,就会调用CreateExtension() 函数执行相关逻辑。
- CreateExtension():解析 SQL 命令中的插件信息,然后调用 CreateExtensionInternal() 函数进行实际的创建操作。
- CreateExtensionInternal():读取插件控制文件中的信息,然后插入插件元组到 pg_extension 表中,最后执行插件脚本。
- read_extension_control_file():读取扩展控制文件中的信息,包括扩展的名称、版本、依赖关系等。
- parse_extension_control_file():解析扩展控制文件中的信息,取出调用具体版本,文件名等信息。
- InsertExtensionTuple():这个函数会将插件元组插入到 pg_extension 表,把控制文件的信息保存到 Tuple 中。
- execute_extension_script():调用 execute_sql_string() 函数执行扩展脚本
- execute_sql_string(): 解析 sql 脚本文件并执行
3.2 SQL 脚本文件路径查找过程
PostgreSQL 中使用 CREATE EXTENSION
创建插件时处理完控制文件之后,会执行插件的 SQL 脚本,而这些脚本文件则需要根据控制文件中提供的插件版本等信息查找文件路径,该过程如下图所示:
在 CreateExtensionInternal 函数中处理插件控制文件的后,调用 get_ext_ver_list
和 get_ext_ver_info
函数获取插件的所有版本列表和具体版本信息;
然后,调用 find_install_path
和 find_update_path
函数查找某个需要执行脚本所在的路径,并通过 get_extension_script_filename
函数根据 SQL 脚本文件命名规则获取 sql 具体文件名称。
上述过程中各函数作用的具体说明如下:
- get_ext_ver_list() :获取指定插件的所有版本列表
- get_ext_ver_info() :获取指定插件的某个版本的信息;根据目标节点选择最短路径以及相对应的起始节点
- find_install_path() :根据插件名称和版本信息,在 PostgreSQL 的插件目录中查找对应的插件目录
- find_update_path() :当前插件版本和要升级到的版本,查找升级脚本所在的目录,使用最短路径算法 Dijkstra 实现
3.3 动态库函数创建过程
如果 PostgreSQL 插件中使用的是源码文件创建具体功能函数,那么在执行 SQL 脚本中使用 CREATE FUNCTION
创建函数时,会通过如下图所示过程加载插件动态库 so 文件来调用具体函数:
ProcessUtilitySlow
解析得到的 SQL 语句是创建函数的命令时,就会调用 CreateFunction
函数来执行具体过程,该函数中调 ProcedureCreate
创建一个新的函数,包括函数的名称、参数、返回值类型、语言、代码等内容;
ProcedureCreate
函数中使用 OidFunctionCall1
和 OidFunctionCall1Coll
调用指定函数的执行代码,该函数中使用 fmgr 提供的接口 fmgr_info
获取函数信息和函数指针;
然后,在 load_external_function
函数中调用 internal_load_library
函数根据函数指针在插件动态库中获取指定的函数实现逻辑
上述过程中各函数作用的具体说明如下:
- ProcessUtilitySlow():处理 SQL 命令的函数,它会对传入的 SQL 命令进行解析和执行;如果 SQL 命令是创建函数的命令,就会调用 CreateFunction() 函数继续处理相应逻辑。
- CreateFunction():解析 SQL 命令中的函数信息,并调用 ProcedureCreate() 函数创建函数。
- ProcedureCreate():创建一个新的函数,包括函数的名称、参数、返回值类型、语言、代码等。通过调用 fmgr_info() 函数获取函数的信息,并调用 OidFunctionCall1() 或 OidFunctionCall1Coll() 函数执行函数代码。
- OidFunctionCall1() 和 OidFunctionCall1Coll():这两个函数用于调用指定函数的执行代码,调用 fmgr 提供的接口。
- fmgr_info()、fmgr_info_cxt_security() 和 fmgr_info_C_lang():这些函数都是获取函数信息的函数;其中,fmgr_info() 函数获取函数的信息,包括输入输出参数、返回值类型、函数实现等;fmgr_info_cxt_security() 函数获取函数的安全上下文;fmgr_info_C_lang() 函数获取函数的实现语言和实现方式。
- load_external_function():加载指定的外部函数库,并返回函数指针。
- internal_load_library()、dlopen()、dlsym() 和 LoadLibrary():这些函数都是加载外部函数库的函数;其中,internal_load_library() 函数是 PostgreSQL 自己实现的加载函数库的函数,先判断是否 load,如果没有则通过调用 dlopen() 函数进行 load,然后使用 dlsym() 查找相关函数;
- LoadLibrary() 函数是 Windows 系统中的加载函数库的函数。
3.4 动态库函数调用过程
使用 CREATE EXTENSION
完成插件创建之后,就可以调用插件提供的函数了
PostgreSQL 中所有的函数调用都会调用 OidInputFunctionCall
函数进行处理,该函数实现如下,首先使用 fmgr_info
函数从插件动态库获取函数指针,然后调用 InputFunctionCall
函数调用指定函数。
Datum
OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
{FmgrInfo flinfo;fmgr_info(functionId, &flinfo);return InputFunctionCall(&flinfo, str, typioparam, typmod);
}
fmgr_info
中获取函数指针的过程和以及其他相关信息,对于系统函数则从 Tuple 中获取;对于动态库中函数,则调用 fmgr_info_C_lang
来获取函数指针
fmgr_info_C_lang
的实现如下所示,和动态库创建过程中的动态库加载过程一样,都是通过调用 load_external_function
函数加载动态库并获取函数指针
static void
fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
{CFuncHashTabEntry *hashentry;PGFunction user_fn;const Pg_finfo_record *inforec;bool isnull;/** See if we have the function address cached already*/hashentry = lookup_C_func(procedureTuple);if (hashentry){user_fn = hashentry->user_fn;inforec = hashentry->inforec;}else{Datum prosrcattr,probinattr;char *prosrcstring,*probinstring;void *libraryhandle;prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,Anum_pg_proc_prosrc, &isnull);if (isnull)elog(ERROR, "null prosrc for C function %u", functionId);prosrcstring = TextDatumGetCString(prosrcattr);probinattr = SysCacheGetAttr(PROCOID, procedureTuple,Anum_pg_proc_probin, &isnull);if (isnull)elog(ERROR, "null probin for C function %u", functionId);probinstring = TextDatumGetCString(probinattr);/* Look up the function itself */user_fn = load_external_function(probinstring, prosrcstring, true,&libraryhandle);/* Get the function information record (real or default) */inforec = fetch_finfo_record(libraryhandle, prosrcstring);/* Cache the addresses for later calls */record_C_func(procedureTuple, user_fn, inforec);pfree(prosrcstring);pfree(probinstring);}switch (inforec->api_version){case 1:/* New style: call directly */finfo->fn_addr = user_fn;break;default:/* Shouldn't get here if fetch_finfo_record did its job */elog(ERROR, "unrecognized function API version: %d",inforec->api_version);break;}
}
动态库加载过程中使用的 dlopen
, dlsym
函数是 dlfcn 库提供一组函数接口:dlopen
可以在运行时动态加载共享库 、 dlsym
可以查找动态链接库中的符号即函数或变量、而 dlclose
用于关闭已加载的动态库。
这几个函数在 PostgreSQL 后台进程运行过程中的作用如下图所示,使用 dlopen
在运行时将插件动态库加载到后台进程的内存映射区域,然后使用 dlsym
在动态库中查找函数并返回函数指针,这样进程在调用该函数时就根据函数指针执行动态库中的函数实现。
如果文章对你有帮助,欢迎一键三连 👍 ⭐️ 💬 。如果还能够点击关注,那真的是对我最大的鼓励 🔥 🔥 🔥 。
参考资料
PostgreSQL: Documentation: 12: 37.17. Packaging Related Objects into an Extension
PostgreSQL数据库扩展包——原理CreateExtension扩展控制文件解析_mb62de8abf75c00的技术博客_51CTO博客
Postgres源码分析——CREATE EXTENSION - 墨天轮 (modb.pro)
Postgres中新增扩展包的方法和原理-CSDN博客
编写Postgres扩展之一:基础 - Tacey Wong - 博客园 (cnblogs.com)
A Guide to Create User-Defined Extension Modules to Postgres - Highgo Software Inc.
如何为PostgreSQL创建一个内置函数? · 小wing的驿站 (xiaowing.github.io)
相关文章:

PostgreSQL 插件 CREATE EXTENSION 原理
PostgreSQL 提供了丰富的数据库内核编程接口,允许开发者在不修改任何 Postgres 核心代码的情况下以插件的形式将自己的代码融入内核,扩展数据库功能。本文探究了 PostgreSQL 插件的一般源码组成,梳理插件的源码内容和实现方式;并介…...
Android常见分区
一、Google官方标准分区 1. Boot分区 包含Linux内核和一个最小的root文件系统(装载到ramdisk中),用于挂载系统和其他的分区并开始Runtime。正如名字所代表的意思(注:boot的意思是启动),这个分区使Android设备可以启动…...
华为鸿蒙4谷歌GMS安装教学
目录 问题描述 参考视频 教学视频1 配套文档 教学视频2 资源包(配套视频1) 设备未经 play 保护机制认证 问题描述 很多国外的最新应用需要再Google商店才能下载比如ChatGPT 华为手机不支持 Google Play 服务的原因主要是由于谷歌服务框架(GMS)未…...

原型设计工具:Balsamiq Wireframes 4.7.4 Crack
原型设计工具:Balsamiq Wireframes是一种快速的低保真UI 线框图工具,可重现在记事本或白板上绘制草图但使用计算机的体验。 它确实迫使您专注于结构和内容,避免在此过程后期对颜色和细节进行冗长的讨论。 线框速度很快:您将产生更多想法&am…...

Nginx Proxy代理
代理原理 反向代理产生的背景: 在计算机世界里,由于单个服务器的处理客户端(用户)请求能力有一个极限,当用户的接入请求蜂拥而入时,会造成服务器忙不过来的局面,可以使用多个服务器来共同分担成…...

SparkSQL之LogicalPlan概述
逻辑计划阶段在整个流程中起着承前启后的作用。在此阶段,字符串形态的SQL语句转换为树结构形态的逻辑算子树,SQL中所包含的各种处理逻辑(过滤、剪裁等)和数据信息都会被整合在逻辑算子树的不同节点中。逻辑计划本质上是一种中间过…...
Ubuntu 安装 kubectl、kubeadm 和 kubelet
你需要在每台机器上安装以下的软件包: kubeadm:用来初始化集群的指令。 kubelet:在集群中的每个节点上用来启动 Pod 和容器等。 kubectl:用来与集群通信的命令行工具。 kubeadm 不能帮你安装或者管理 kubelet 或 kubectl&#…...
C语言获取文件长度
C语言获取文件长度 文章目录 C语言获取文件长度一、使用标准库方法二、使用Linux系统调用 一、使用标准库方法 #include <stdio.h>long get_file_size(const char * filename ){long size 0;FILE * fp fopen(filename,"rb");if( fp NULL ) {printf("o…...

【面试经典150 | 哈希表】快乐数
文章目录 写在前面Tag题目来源题目解读解题思路方法一:哈希集合判重方法二:快慢指针判重 其他语言python3 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为…...

ETL实现实时文件监听
一、实时文件监听的作用及应用场景 实时文件监听是一种监测指定目录下的文件变化的技术,当产生新文件或者文件被修改时,可实时提醒用户并进行相应处理。这种技术广泛应用于数据备份、日志管理、文件同步和版本控制等场景,它可以帮助用户及时…...

Openssl数据安全传输平台003:Protobuf - 部署
文章目录 Github代码仓库位置一、Windows环境配置生成库文件之后—>参考3.3 配置VS1. 先将平台设置为所有平台2. 配置属性 >> C/C >> 常规 >> 附加包含目录3. 配置属性 >> C/C >> 预处理器 >> 预处理器定义,添加4. 配置属性 >> C…...

Proteus仿真--一种智能频率计的设计与制作(AVR单片机+proteus仿真)
本文介绍一种基于AVR单片机实现的一种智能频率计Proteus仿真实现(完整仿真源文件及代码见文末链接) 简介 硬件电路主要分为单片机主控模块、频率计模块、LCD1602液晶显示模块以及串口模块 (1)单片机主控模块:单片机…...

CAS是“Compare and Swap“(比较并交换)
CAS是"Compare and Swap"(比较并交换) 一,介绍 CAS是"Compare and Swap"(比较并交换)的缩写,是一种多线程同步的原子操作。它基于硬件的原子性保证,用于解决并发环境下的…...

前端数据可视化之【series、series饼图配置】配置项
目录 🌟Echarts配置项🌟series🌟饼图 type:pie🌟写在最后 🌟Echarts配置项 ECharts开源来自百度商业前端数据可视化团队,基于html5 Canvas,是一个纯Javascript图表库,提供直观&…...

03.MySQL事务及存储引擎笔记
事务 查看/设置事务 select autocommit; --查看当前数据库的事务状态,1表示开启,0表示关闭 set autocommit 0; --关闭自动事务提交采用关闭自动事务提交我们就可以手动进行事务提交,但是这种设置方式是对整个数据库起作用,一些可…...

input框输入中文时,输入未完成触发事件。Vue中文输入法不触发input事件?
前言 在做搜索输入框时,产品期待实时搜索,就是边输入边搜索,然而对于中文输入法出现的效果,不同的产品可能有不同的意见,有的觉得输入未完成也应该触发搜索。但有的却认为应该在中文输入完成后再触发搜索。我发现在vu…...

ArmSoM-RK3588编解码之mpp解码demo解析:mpi_dec_test
1. 简介 [RK3588从入门到精通] 专栏总目录 mpi_dec_test 是rockchip官方解码 demo 本篇文章进行mpi_dec_test 的代码解析,解码流程解析 2. 环境介绍 硬件环境: ArmSoM-W3 RK3588开发板 软件版本: OS:ArmSoM-W3 Debian11 3.…...

v-for列表渲染
一、v-for迭代数组 <li v-for"(e,index) in emp" :key"e.id">编号{{index1}} 名字{{e.name}} 年龄{{e.age}} </li> e 是循环数组中的每个元素的别名index 是当前循环的下表,从0开始:key 的作用: 是为了给 Vue 一个提示…...

【引流技术】最新头条全自动引流脚本,解放双手自动引流【引流脚本+技术教程】
软件功能: 评论点赞 适用于自己做头条号,去别人评论区截留,点赞,别人会收到提醒,达到回访效果 文章/视频评论 可以自己发布引流文章或视频,引导进你主页或者私信你,达到引流效果 设备需求: 安卓手机8.1及以上系统 文章分享者:Linxiaoyu…...

智能PDU的“智能”体现在哪些方面?
智能PDU是一种用于管理和监控数据中心或其他设施中的电源分配设备,通过引入以太网络、语音服务等新颖的通讯手段,增加了传统机柜PDU插座所不能提供的智能管理控制模块和控制芯片,同时兼具电源分配和管理功能。智能PDU是当今现代化IDC数据中心…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...

Linux入门课的思维导图
耗时两周,终于把慕课网上的Linux的基础入门课实操、总结完了! 第一次以Blog的形式做学习记录,过程很有意思,但也很耗时。 课程时长5h,涉及到很多专有名词,要去逐个查找,以前接触过的概念因为时…...
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
Nginx1.24编译时,报LuaJIT2.x错误, configuring additional modules adding module in /www/server/nginx/src/ngx_devel_kit ngx_devel_kit was configured adding module in /www/server/nginx/src/lua_nginx_module checking for LuaJIT 2.x ... not…...

【Linux】使用1Panel 面板让服务器定时自动执行任务
服务器就是一台24小时开机的主机,相比自己家中不定时开关机的主机更适合完成定时任务,例如下载资源、备份上传,或者登录某个网站执行一些操作,只需要编写 脚本,然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...