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 SYSTEMALTER 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_fileparse_extension_control_file 两个函数读取并解析插件控制文件中的信息,通常包括扩展的名称、版本、依赖关系等;

然后,调用 InsertExtensionTuple 函数将控制文件中包含的插件元数据信息插入到系统表 pg_extension 中;

接着,调用 execute_extension_scriptexecute_sql_string 函数,根据获取到的 SQL 脚本路径,读取并执行 SQL 脚本;执行过程与 PostgreSQL 中一般的查询处理过程一致:

  1. 调用 pg_parse_query 将 sql 转换成 parsetree_list;
  2. 执行 pg_analyze_and_rewrite 对 SQL 脚本进行解析和重写;
  3. 调用 pg_plan_queries() 函数生成查询计划;
  4. 调用 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_listget_ext_ver_info 函数获取插件的所有版本列表和具体版本信息;

然后,调用 find_install_pathfind_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 函数中使用 OidFunctionCall1OidFunctionCall1Coll 调用指定函数的执行代码,该函数中使用 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;}
}

动态库加载过程中使用的 dlopendlsym 函数是 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)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis写sql

批量查询 <select id"getPreIds" resultType"java.lang.String"parameterType"java.util.List">SELECT pre_batch_id FROM public.mine_data_quality_check_record WHERE deleted0<if test"list ! null">AND pre_batch…

codeshell安装配置

codeshell安装配置 1 注意事项1.1 Python版本问题 2 codeshell环境搭建2.1 codeshell使用软件各版本2.2 软件下载2.3 codeshell使用环境安装2.3.1 python-3.10.9-amd64.exe安装2.3.2 Anaconda3-2022.10-Windows-x86_64.exe安装2.3.3 创建环境2.3.4 Pytorch安装2.3.5 transforme…

C++初阶 入门(2)

目录 一、缺省函数 1.1什么是缺省函数 1.2为什么要有缺省函数 1.3使用缺省函数 1.4测试代码 二、函数重载 2.1什么是函数重载 2.2为什么要有函数重载 2.3什么情况构成函数重载 2.4函数重载例子及代码 三、引用 3.1什么是引用 3.2如何引用 ​3.3常引用(可略过) 3…

CCF CSP认证历年题目自练Day38

题目 试题编号&#xff1a; 201409-3 试题名称&#xff1a; 字符串匹配 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   给出一个字符串和多行文字&#xff0c;在这些文字中找到字符串出现的那些行。你的程序还需支持大小写敏感…

酒类商城小程序怎么做

随着互联网的快速发展&#xff0c;线上购物越来越普及。酒类商品也慢慢转向线上销售&#xff0c;如何搭建一个属于自己的酒类小程序商城呢&#xff1f;下面就让我们一起来看看吧&#xff01; 一、登录乔拓云平台 首先&#xff0c;我们需要进入乔拓云平台的后台&#xff0c;点击…

《向量数据库》——Zilliz X Dify.AI ,快速打造知识库 AI 应用

Zilliz 大模型生态矩阵再迎新伙伴!近日,Zilliz 和 Dify.AI 达成合作,Zilliz 旗下的产品 Zilliz Cloud、Milvus 与开源 LLMOps 平台 Dify 社区版进行了深度集成。 01. Zilliz Cloud v.s. Dify Dify 作为开源的 LLMs App 技术栈,在此前已支持丰富多元的大型语言模型的接入,…

【React Router】React Router学习笔记

React Router学习笔记 React Router1.什么是React Router?2.为什么要用React Router?3.基础3.1 路由配置3.2 路由匹配原理3.3 History3.3.1 browerHistory3.3.2 hashHistory3.3.3 createMemoryHistory3.3.4 实现示例 3.4 默认路由(IndexRoute)与IndexLink3.4.1 IndexRoute3.4…

[SQL开发笔记]WHERE子句 : 用于提取满足指定条件的记录

SELECT DISTINCT语句用户返回列表的唯一值&#xff1a;这是一个很特定的条件&#xff0c;假设我需要考虑很多中限制条件进行查询呢&#xff1f;这时我们就可以使用WHERE子句进行条件的限定 一、功能描述&#xff1a; WHERE子句用于提取满足指定条件的记录&#xff1b; 二、WH…

报错解决:libcudart.so和libprotobuf.so链接库未找到

报错解决&#xff1a;libcudart.so和libprotobuf.so链接库未找到 libcudart.so链接库未找到原因解决方法 libprotobuf.so链接库未找到原因解决方法 此博客介绍了博主在编译软件包时遇到的两个报错&#xff0c;主要是libcudart和libprotobuf两个动态链接库未找到的问题&#xff…

Nginx安装配置项目部署然后加SSL

个人操作笔记记录 第一步&#xff1a;把 nginx 的源码包nginx-1.8.0.tar.gz上传到 linux 系统 第二步&#xff1a;解压缩 tar zxvf nginx-1.8.0.tar.gz 第三步&#xff1a;进入nginx-1.8.0目录 使用 configure 命令创建一 makeFile 文件。 直接复制过去运行 ./configur…

【RocketMQ系列十二】RocketMQ集群核心概念之主从复制生产者负载均衡策略消费者负载均衡策略

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

KingBase库模式表空间和客户端认证(kylin)

库、模式、表空间 数据库 数据库基集簇与数据库实例 KES集簇是由单个KES实例管理的数据库的集合KES集簇中的库使用相同的全局配置文件和监听端口、共享相关的进程和内存结构同一数据库集簇中的进程、相关的内存结构统称为实例 数据库 数据库是一个长期存储在计算机内的、有…

【Tensorflow 2.12 简单智能商城商品推荐系统搭建】

Tensorflow 2.12 简单智能商城商品推荐系统搭建 前言架构数据召回排序部署调用结尾 前言 基于 Tensorflow 2.12 搭建一个简单的智能商城商品推荐系统demo~ 主要包含6个部分&#xff0c;首先是简单介绍系统架构&#xff0c;接着是训练数据收集、处理&#xff0c;然后是召回模型、…

redis分布式锁的应用

redis 作为分布式锁的东西 分布式锁的应用 redis,zk,数据库这些都可以实现分布式锁 我们今天主要基于redis实现的分布式锁&#xff0c;而且要求性能要好 基于一个小的业务场景来说&#xff0c;就比如说秒杀中的减库存&#xff0c;防止超卖这种代码就会有并发问题,比方说3个线程…

uni-app开发

uni-app 官方手册&#xff1a;uni-app官网 一&#xff1a;tarBar&#xff1a;一级导航栏&#xff0c;即 tab 切换时显示对应页。 在pages.json文件里写入如下代码&#xff1a; 此效果&#xff1a;

编译工具链 之一 基本概念、组成部分、编译过程、命名规则

编译工具链将程序源代码翻译成可以在计算机上运行的可执行程序。编译过程是由一系列的步骤组成的&#xff0c;每一个步骤都有一个对应的工具。这些工具紧密地工作在一起&#xff0c;前一个工具的输出是后一个工具的输入&#xff0c;像一根链条一样&#xff0c;我们称这一系列工…

Unity 单例-接口模式

单例-接口模式 使用接口方式实现的单例可以继承其它类&#xff0c;更加方便 using System.Collections; using System.Collections.Generic; using UniRx; using UniRx.Triggers; using UnityEngine; namespace ZYF {public interface ISingleton<TMono> where TMono : M…

力扣第55题 跳跃游戏 c++ 贪心 + 覆盖 加暴力超时参考

题目 55. 跳跃游戏 中等 相关标签 贪心 数组 动态规划 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &…

Unity编辑器扩展 --- AssetPostprocessor资源导入自动设置

unity导入资源的编辑器设置: 防止策划资源乱导入,资源导入需要的格式&#xff0c;统一资源管理 AssetPostprocessor资源导入管线 AssetPostprocessor用于在资源导入时自动做一些设置&#xff0c;比如当导入大量图片时&#xff0c;自动设置图片的类型&#xff0c;大小等。Ass…

AlDente Pro for Mac: 掌控电池充电的终极解决方案

你是否曾经为了保护你的MacBook的电池&#xff0c;而苦恼于无法控制它的充电速度&#xff1f;AlDente Pro for Mac 是一款专为Mac用户设计的电池管理工具&#xff0c;它能帮助你解决这个问题。 AlDente Pro for Mac 是一款电池最大充电限制软件&#xff0c;它能够让你自由地设…