Skip to content

PL/Java / PL/v8 简介 ​

写到这里,本教程覆盖的过程语言都来自 PostgreSQL 官方核心发行版:PL/pgSQL、PL/Perl、PL/Python(以及 PL/Tcl)。但数据库插件生态远不止这些——Java 团队想用 Java 写存储过程,前端团队想直接复用 JavaScript 函数,这两类需求都很常见。

本页介绍两个最具代表性的外部过程语言:PL/Java 与 PL/v8。它们不在核心发行版里,需要自己编译安装,代价明显更高,但换来的是各自生态独有的能力。本页讲清它们的定位、安装路径、核心用法,以及什么时候值得用、什么时候应该放弃。

前置知识

阅读本节前,建议先了解:PL/Python 基础、PL/Perl 基础、触发器概述

基础概念 ​

「内置」与「外部」的分界线 ​

先建立一个准确的心智模型。官方文档 Appendix H 说得很明确:

PostgreSQL includes several procedural languages with the base distribution: PL/pgSQL, PL/Tcl, PL/Perl, and PL/Python.

In addition, there are a number of procedural languages that are developed and maintained outside the core PostgreSQL distribution. A list of procedural languages is maintained on the PostgreSQL wiki. Note that some of these projects are not released under the same license as PostgreSQL.

分类过程语言来源文档位置
核心发行版PL/pgSQL、PL/Tcl、PL/Perl、PL/PythonPostgreSQL 官方正文 Chapter 40–44
外部项目PL/Java、PL/v8、PL/Lua、PL/R、PL/sh…社区/第三方Appendix H.3 仅列链接

这条分界线不是学术分类,它直接决定三件实际的事:

差异点核心发行版语言外部过程语言
谁负责打包官方源码树,--with-xxx 编译独立仓库、独立版本号、独立发布节奏
升级/兼容随 PostgreSQL 大版本一起演进需自行确认对 PG 18 的支持情况
官方稳定性承诺有无
许可证PostgreSQL License各自不同(有的不是 PostgreSQL License)

「外部」意味着官方不保证

官方原文:Note that some of these projects are not released under the same license as PostgreSQL. 使用外部过程语言前,必须自行评估:它是否支持你当前的 PG 大版本、维护是否活跃、许可证是否可接受。

为什么要用外部过程语言 ​

如果 PL/pgSQL + PL/Python 已经够用,为什么还要引入外部语言?因为这些语言背后是完整的生态:

语言背后的生态典型诉求
PL/JavaMaven 中央仓库、JVM、企业 Java 中间件复用已有的 Java 业务库、与企业 Java 体系一致
PL/v8npm、现代 JS/TS 工具链、JSON 原生支持前后端共享校验/格式化逻辑、JSON 处理

但引入的代价同样真实:

text
外部过程语言的成本结构
─────────────────────────────────────────────
引入成本   ├─ 编译/安装(不在标准仓库里,常常要自己 build)
           ├─ 版本绑定(PG 大版本 + 语言运行时版本)
           └─ 部署一致性(每台数据库服务器都要有同样的运行时)
运行成本   ├─ 内存占用(JVM 堆 / V8 isolate 常驻后端进程)
           ├─ 单点阻塞(语言层死循环会挂住整个连接)
           └─ 排障难度(跨 PG 内核 / 语言运行时 / 你的库 三层)

结论:外部过程语言适合「非它不可」的场景,不适合「顺手用一下」。

PL/Java 是什么 ​

PL/Java 是一个开源扩展,官方项目页原文:PL/Java is a free open-source extension for PostgreSQL that allows stored procedures, triggers, and functions to be written in the Java language and executed in the backend.

它的核心卖点不是「能用 Java」,而是这句话:Backend functions and triggers are written in Java using a directly-connected, efficient version of the standard Java JDBC API that PL/Java transparently provides, with enhanced capabilities found in the PL/Java API.

也就是说:

  • JDBC API 被进程内直连版本替代——没有网络往返、没有额外连接;
  • 在 JDBC 之上还有 PL/Java 自己的增强 API(org.postgresql.pljava);
  • Java 侧可以直接操作 SQL 结果集,写法与普通 JDBC 高度相似。

PL/v8 是什么 ​

PL/v8(PLV8) 是另一个开源扩展。官方文档首页原文:PLV8 is a trusted Javascript language extension for PostgreSQL. It can be used for stored procedures, triggers, etc. 以及 PLV8 is a shared library that provides a PostgreSQL procedural language powered by V8 Javascript Engine.

两个关键点:

  1. 基于 V8——就是 Chrome / Node.js 用的那个引擎,所以语法能力与你在 Node 里写的 JS 基本一致;
  2. trusted——官方文档首页明确写它是可信语言扩展。这意味着它的权限模型设计目标是让普通用户也能用。

官方还标注了版本兼容:PLV8 works with most versions of Postgres, version 13 and above.

四者横向对比 ​

维度PL/pgSQLPL/PythonPL/JavaPL/v8
是否核心发行版✅✅❌ 外部❌ 外部
是否有可信变体✅❌(只有 plpython3u)取决于部署配置✅ 官方称 trusted
安装复杂度极低(内置)低(装包)高(编译 native + JAR)高(编译 V8)
运行时内存极小小大(JVM 堆)中(V8 isolate)
生态成熟度最高高中(企业 Java 场景)中(JS/JSON 场景)
最适合通用业务逻辑文本/科学计算复用 Java 库JSON / 前后端共享逻辑

语法与安装 ​

PL/Java:安装路径 ​

PL/Java 的安装比核心语言重得多。项目文档把安装拆成两本书的篇幅,核心步骤是:

text
PL/Java 安装流程
───────────────────────────────────────────────
① 准备环境
   ├─ JDK(版本需与 PL/Java 版本要求匹配)
   ├─ Maven
   └─ PostgreSQL 服务端开发包(pg_config、头文件)

② 构建(项目文档推荐 Maven)
   └─ 产出两个东西:
        ├─ native 库(.so / .dll)
        └─ JAR 文件

③ 让 PostgreSQL 知道它们在哪
   └─ 需配置:native 库路径 + PL/Java 的 JAR + Java 运行时自身的 native 库

④ 在数据库里安装
   └─ 执行 SQL 侧安装脚本 / install_jar()

官方项目页对构建产物的描述:The build produces a native code library (file with name ending in .so, .dll, etc., depending on the platform) and a JAR file. PostgreSQL must be configured to know where these are, in addition to the native library for the Java runtime itself.

先找预编译包

官方原文:There may be a prebuilt distribution available for your platform. You can check for that on the wiki prebuilt packages page.

在动手编译前,先确认发行版仓库或项目预编译页有没有现成包——能省掉整个 JVM/V8 工具链的折腾。

PL/Java:源码与注解 ​

PL/Java 的典型工作流是「Java 源码 → 编译 → JAR → 安装进数据库」:

text
Java 源码
  │  用注解声明「哪些方法/类型要暴露给 PostgreSQL」
  ▼
org.postgresql.pljava.annotation.*
  │  编译
  ▼
class 文件 + SQLJ 部署描述符(deployment descriptor)
  │  打包
  ▼
JAR 文件
  │  install_jar(...)
  ▼
PostgreSQL 中的函数 / 触发器 / 类型

官方对注解的说明:PL/Java source files can use Java annotations from the org.postgresql.pljava.annotation package to identify the methods and types that should be seen in PostgreSQL.

关于部署描述符,官方原文:When the sources are compiled, the Java compiler will also write an SQLJ deployment descriptor containing the SQL statements that must be executed when installing and uninstalling the compiled Java code in the PostgreSQL backend.

最后一步:When the compiled Java code and the deployment descriptor file are stored together in a JAR file, PL/Java's install_jar function will both load the code into PostgreSQL and execute the necessary SQL commands in the deployment descriptor, making the new types/functions/triggers available for use.

PL/Java 能暴露给 SQL 的对象类型比核心语言更丰富:

可暴露对象说明
存储过程 / 函数基本能力
触发器行级与语句级
聚合自定义聚合函数
操作符自定义操作符
类型自定义数据类型

PL/v8:安装与验证 ​

PL/v8 的安装流程比 PL/Java 短一些,因为它只需要编译 native 库(内置 V8),没有 Java 侧的 JAR 管理:

sql
-- 语言存在后,在目标数据库里安装
CREATE EXTENSION plv8;

官方给出两种验证方式:

sql
-- PLV8 2.0.0 及以上
SELECT plv8_version();
sql
-- 所有版本通用
DO $$ plv8.elog(NOTICE, plv8.version); $$ LANGUAGE plv8;

从源码构建的大致步骤(官方文档):

bash
git clone https://github.com/plv8/plv8
cd plv8
make
sudo make install
make installcheck

官方提示:This will download v8 and compile it as well. —— 也就是说 make 会顺带编译 V8 引擎本身,这是耗时的主要原因。

多个 PostgreSQL 版本共存时要指定 PG_CONFIG

官方原文:If you have multiple versions of PostgreSQL installed like 9.5 and 9.6, Plv8 will only be built for PostgreSQL 9.6. This is because make calls pg_config to get the version number, which will always be the latest version installed. If you need to build Plv8 for PostgreSQL 9.5 while you have 9.6 installed pass make the PG_CONFIG variable to your 9.5 version of pg_config.

bash
make PG_CONFIG=/usr/lib/postgresql/18/bin/pg_config
sudo make install PG_CONFIG=/usr/lib/postgresql/18/bin/pg_config

PL/v8:升级 ​

官方原文:As of PLV8 version 2.3.3, you can use upgrade scripts to upgrade your installation from any version higher than 1.5.0.

sql
ALTER EXTENSION plv8 UPDATE TO '3.2.0';

但有一个容易困惑的行为,官方也点明了:Note that until the database has been restarted, the old version of PLV8 will still be loaded, though SELECT plv8_version() will return the new version. This is an artifact of how Postgres manages extensions.

也就是说:升级后必须重启数据库(或至少重连到新后端)才能真正切换实现。

详细说明 ​

PL/v8:运行时配置参数 ​

PL/v8 提供了一组 GUC 参数,官方列出如下:

变量说明默认值
plv8.start_procPL/v8 首次使用时执行一次的 PL/v8 函数名无
plv8.icu_dataICU 数据文件目录(编译启用 ICU 支持时)无
plv8.v8_flagsV8 引擎初始化标志无
plv8.execution_timeoutV8 执行超时(编译时启用 EXECUTION_TIMEOUT 才有)300 秒
plv8.memory_limit每个连接上每用户的堆内存上限,单位 MB256

关于执行超时,官方原文:By default, the execution timeout is not compiled, but when configured it has a timeout of 300 seconds (5 minutes). You can override this by setting the plv8.execution_timeout variable. It can be set between 1 second and 65536 seconds, but cannot be disabled.

构建时启用:

bash
make EXECUTION_TIMEOUT=1

PL/v8 函数里的死循环会挂住连接

V8 是单线程执行的。一个没写出口的 while (true) {} 会让当前后端进程永久卡死,直到语句超时或管理员手工终止连接。若不编译 EXECUTION_TIMEOUT,连超时保护都没有。生产环境务必开启,并且同时设置 statement_timeout。

PL/v8:启动过程与全局状态 ​

PL/v8 提供启动钩子,可以在运行时初始化时先跑一段自己的初始化代码:

sql
SET plv8.start_proc = 'plv8_init';
SELECT plv8_test(10);

官方解释:If this variable is set when the runtime is initialized, before the function call of plv8_test() another plv8 function plv8_init() is invoked. In such initialization function, you can add any properties to plv8 object to expose common values or assign them to the this property. In the initialization function, the receiver this is specially pointing to the global object, so the variables that are assigned to the this property in this initialization are visible from any subsequent function as global variables.

有三条重要推论:

  1. 只能由管理员设置该变量:This can only be set by someone with administrator access.
  2. 必须在任何 PL/v8 动作之前设置:Remember CREATE FUNCTION also starts the plv8 runtime environment, so make sure to SET this GUC before any plv8 actions including CREATE FUNCTION.
  3. this 的生命周期很短:each function invocation has a different this object. 想跨函数共享,必须挂在全局 plv8 对象上。

PL/v8:运行时隔离模型 ​

官方对隔离模型的描述很关键:

In PLV8, each session has one global JS runtime context. This enables function invocations at low cost, and sharing common object among the functions. However, for the security reasons, if the user switches to another with SET ROLE command, a new JS runtime context is initialized and used separately. This prevents the risk of unexpected information leaking.

text
单个后端会话
├── JS 运行时上下文 A(角色 user1)
│     ├── plv8 全局对象
│     ├── 缓存的常量 / 连接 / 编译后对象
│     └── 函数调用复用同一上下文  ← 低成本
└── JS 运行时上下文 B(SET ROLE 到 user2)  ← 隔离,防信息泄漏

这与 PL/Perl 的 %_SHARED 按角色隔离是同一设计思路,只是各自实现不同。

PL/v8:SPI 与内置函数 ​

PL/v8 提供一组内置能力,从官方目录可以看清全貌:

分类成员
工具函数plv8.elog、plv8.quote_literal、plv8.nullable、plv8.quote_ident、plv8.find_function、plv8.version、plv8.memory_usage、plv8.run_script
SPI 数据库访问plv8.execute、plv8.prepare、PreparedPlan.execute、PreparedPlan.cursor、PreparedPlan.free、Cursor.fetch、Cursor.move、Cursor.close、plv8.subtransaction
窗口函数 APIWindowObject.get_current_position、get_partition_row_count、set_mark_position、rows_are_peers、get_func_arg_in_partition、get_func_arg_in_frame、get_func_arg_in_current、get_partition_local、set_partition_local
运维过程plv8_info()、plv8_reset()
外部工具PLV8ify、Equinox

原生数据类型自动映射(官方列表),前提是目标 PG 类型属于以下之一且 JS 值「看起来兼容」:

text
OID, bool, INT3, INT4, INT8, FLOAT4, FLOAT8, NUMERIC,
DATE, TIMESTAMP, TIMESTAMPTZ, BYTEA, JSON, JSONB
数组类型只支持一维;JS 对象在适用时映射为 tuple;
此外还支持 ANYELEMENT、ANYARRAY 等多态类型。

PL/v8:运维过程 plv8_info 与 plv8_reset ​

plv8_info() 面向长连接后端服务,用于排查内存增长。官方原文:Get information about all the current running environments on a specific connection from all users. Can be run by superuser only.

sql
SELECT plv8_info();

输出为 JSON,包含 total_heap_size、used_heap_size、heap_size_limit、external_memory、number_of_native_contexts、contexts 等字段。官方还给出一个换算关系:Note: "number_of_native_contexts" = "contexts".length + 2。

plv8_reset() 用于重置上下文:

sql
-- 重置指定上下文
SELECT plv8_reset('my context');

-- 重置该用户的全部上下文
SELECT plv8_reset();

-- 超级用户模拟其他用户后重置
SET ROLE "some_user";
SELECT plv8_reset();
RESET ROLE;

官方对指定上下文重置的说明:Will reset the context and re-create globalThis on a next invocation of that context.

实战示例 ​

示例一:PL/v8 标量函数 ​

官方示例:把两个数组拼成一个 JSON 对象。

sql
CREATE FUNCTION plv8_test(keys TEXT[], vals TEXT[]) RETURNS JSON AS $$
  var o = {};
  for (var i = 0; i < keys.length; i++) {
    o[keys[i]] = vals[i];
  }
  return o;
$$ LANGUAGE plv8 IMMUTABLE STRICT;

SELECT plv8_test(ARRAY['name', 'age'], ARRAY['Tom', '29']);
text
           plv8_test
-------------------------------
 {"name":"Tom","age":"29"}
(1 row)

官方补充了一个细节:If argument names are omitted in the creation of the function, they will be available in the function as $1, $2, etc.

示例二:PL/v8 集合返回 ​

sql
CREATE TYPE rec AS (i integer, t text);

CREATE FUNCTION set_of_records() RETURNS SETOF rec AS
$$
  // plv8.return_next() stores records in an internal tuplestore,
  // and return all of them at the end of function.
  plv8.return_next( { "i": 1, "t": "a" } );
  plv8.return_next( { "i": 2, "t": "b" } );

  // You can also return records with an array of JSON.
  return [ { "i": 3, "t": "c" }, { "i": 4, "t": "d" } ];
$$
LANGUAGE plv8;

SELECT * FROM set_of_records();
text
 i | t
---+---
 1 | a
 2 | b
 3 | c
 4 | d
(4 rows)

官方提醒:If the argument object to return_next() has extra properties that are not defined by the argument, return_next() raises an error.

示例三:PL/v8 触发器 ​

官方示例,展示了 PL/v8 触发器可见的特殊变量:

sql
CREATE FUNCTION test_trigger() RETURNS TRIGGER AS
$$
  plv8.elog(NOTICE, "NEW = ", JSON.stringify(NEW));
  plv8.elog(NOTICE, "OLD = ", JSON.stringify(OLD));
  plv8.elog(NOTICE, "TG_OP = ", TG_OP);
  plv8.elog(NOTICE, "TG_ARGV = ", TG_ARGV);
  if (TG_OP == "UPDATE") {
    NEW.i = 102;
    return NEW;
  }
$$
LANGUAGE "plv8";

CREATE TRIGGER test_trigger
  BEFORE INSERT OR UPDATE OR DELETE
  ON test_tbl FOR EACH ROW
  EXECUTE PROCEDURE test_trigger('foo', 'bar');

PL/v8 触发器函数可见的触发状态变量(官方列表):

text
NEW, OLD, TG_NAME, TG_WHEN, TG_LEVEL,
TG_OP, TG_RELID, TG_TABLE_NAME, TG_TABLE_SCHEMA, TG_ARGV

官方说明:If the trigger type is an INSERT or UPDATE, you can assign properties of NEW variable to change the actual tuple stored by this operation.

示例四:PL/v8 类型化数组加速 ​

PL/v8 为特定数组类型提供「类型化数组」直通访问,避免逐个元素转换:

PL/v8 域类型映射的 PG 类型
plv8_int2arrayint2[]
plv8_int4arrayint4[]
plv8_float4arrayfloat4[]
plv8_float8arrayfloat8[]

官方示例:

sql
CREATE FUNCTION int4sum(ary plv8_int4array) RETURNS int8 AS $$
  var sum = 0;
  for (var i = 0; i < ary.length; i++) {
    sum += ary[i];
  }
  return sum;
$$ LANGUAGE plv8 IMMUTABLE STRICT;

SELECT int4sum(ARRAY[1, 2, 3, 4, 5]);
text
 int4sum
---------
      15
(1 row)

官方限制:For these typed arrays, only 1-dimensional arrays without any NULL elements. There is currently no way to create such typed array inside PLV8 functions, only arguments can be typed array.

示例五:PL/v8 缓存初始化(start_proc) ​

把昂贵的一次性准备工作放进 start_proc,避免每次调用重复执行:

sql
-- 必须在任何 plv8 动作(包括 CREATE FUNCTION)之前设置
SET plv8.start_proc = 'plv8_init';

CREATE FUNCTION plv8_init() RETURNS void AS $$
  // 在初始化函数里,this 指向全局对象
  this.app_config = { retry: 3, tz: 'Asia/Shanghai' };
  this.re_email = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
$$ LANGUAGE plv8;

CREATE FUNCTION is_email(v text) RETURNS boolean AS $$
  if (v === null) return null;
  return this.re_email.test(v);
$$ LANGUAGE plv8 IMMUTABLE STRICT;

SELECT is_email('a@b.com'), is_email('not-an-email');
text
 is_email | is_email
----------+----------
 t        | f
(1 row)

start_proc 设置时机

CREATE FUNCTION 本身就会启动 PL/v8 运行时。所以 SET plv8.start_proc 必须在这之前执行,否则初始化函数不会生效。

示例六:PL/v8 内存巡检 ​

长连接场景下,定期检查 JS 堆增长情况:

sql
SELECT plv8_info();
json
[
  {
    "user": "app_rw",
    "total_heap_size": 1327104,
    "total_physical_size": 474336,
    "used_heap_size": 386680,
    "heap_size_limit": 270008320,
    "external_memory": 0,
    "number_of_native_contexts": 2,
    "contexts": []
  }
]

若 used_heap_size 持续上升,考虑排查全局对象上的缓存是否无界增长,必要时用 plv8_reset() 重建上下文。

示例七:PL/Java 的定位判断(不含完整代码) ​

PL/Java 的完整示例代码在官方项目文档中(Hello, world 与各模块 JavaDoc),本页不重复。这里给出判断是否该用 PL/Java 的决策清单:

text
是否应该用 PL/Java?
───────────────────────────────────────────────
① 已有大量经过验证的 Java 业务库,重写成本极高?
   → 是:PL/Java 有优势
   → 否:继续看②

② 团队主语言是 Java,且已具备 JVM 运维能力?
   → 是:PL/Java 可维护
   → 否:引入即成为长期负担

③ 逻辑是重计算、能用 PL/pgSQL 或 PL/Python 表达?
   → 是:不要用 PL/Java
   → 否:继续看④

④ 能接受「每次 PG 大版本升级都要重新验证 PL/Java 兼容性」?
   → 是:可用
   → 否:放弃

注意事项 ​

  • 外部过程语言不在官方核心发行版内,不受官方稳定性承诺覆盖。 升级 PostgreSQL 大版本前,必须确认外部语言已支持新版本。

  • 许可证各不相同。 官方原文:some of these projects are not released under the same license as PostgreSQL. 引入前必须过法务。

  • PL/Java 的安装是「native 库 + JAR + Java 运行时 native 库」三件套。 缺任何一个都启动不了,且路径必须让 PostgreSQL 能找到。

  • PL/Java 与 PL/v8 都要在每台数据库服务器上安装同样的运行时。 主从、多节点部署时这是最容易遗漏的一致性风险。

  • PL/v8 的函数里不要写无出口的循环。 会永久挂住后端连接。生产环境必须编译 EXECUTION_TIMEOUT 并同时设置 statement_timeout。

  • plv8.execution_timeout 不能禁用。 官方原文:It can be set between 1 second and 65536 seconds, but cannot be disabled. 取值下限 1 秒,上限 65536 秒。

  • plv8.memory_limit 单位是 MB,默认 256。 官方原文:Memory limit for the per-user heap usage on each connection, in MB.

  • plv8.start_proc 只能由管理员设置,且必须在任何 PL/v8 动作之前。 CREATE FUNCTION 也会启动运行时。

  • this 不跨函数调用持久。 需要共享的数据必须挂在全局 plv8 对象上。

  • SET ROLE 会创建新的 JS 运行时上下文。 这意味着跨角色无法共享缓存,同时也意味着内存占用可能成倍增长。

  • PL/v8 升级后必须重启数据库才真正生效。 plv8_version() 会先返回新版本号,但旧实现仍在内存中。

  • PL/v8 的类型化数组只支持一维、且不能含 NULL 元素。 官方原文:only 1-dimensional arrays without any NULL elements.

  • plv8.return_next() 的对象不能有多余属性。 否则直接报错。

  • PL/Java 与 PL/v8 的内存都是「常驻后端进程」的。 JVM 堆与 V8 isolate 不会因为函数调用结束而释放。

  • 两者都不能替代连接池/中间件做 I/O 密集型工作。 在后端进程里做网络请求会占住数据库连接。

  • 不要用外部过程语言写「所有业务逻辑」。 引入成本应该由少数几个高价值函数来分摊。

最佳实践 ​

  • 先在核心语言里找答案。 官方四件套(PL/pgSQL、PL/Tcl、PL/Perl、PL/Python)能覆盖绝大多数场景,外部语言只在有明确不可替代理由时才引入。

    text
    决策顺序:SQL → PL/pgSQL → PL/Python / PL/Perl → 外部过程语言
  • 外部语言只承载「薄」逻辑:复用既有库、JSON 转换、格式校验。 不要把它当作完整的应用运行环境。

  • 把外部语言的函数集中放在独立 schema 下,并收紧 EXECUTE 授权。

    sql
    CREATE SCHEMA IF NOT EXISTS ext;
    REVOKE ALL ON SCHEMA ext FROM PUBLIC;
    GRANT  USAGE ON SCHEMA ext TO app_rw;
  • 为 PL/v8 明确设置超时与内存上限。

    sql
    -- postgresql.conf
    -- plv8.execution_timeout = '10s'   -- 需编译 EXECUTION_TIMEOUT
    -- plv8.memory_limit = 128
  • 用 SET 明确设置 plv8.start_proc,并保证它在建函数之前执行。

    sql
    SET plv8.start_proc = 'ext.plv8_init';
  • 把「数据库服务器上的语言运行时版本」纳入配置管理与监控。 例如记录 Java 版本、V8 版本、PL/Java 与 PL/v8 的扩展版本。

    sql
    SELECT plv8_version();
    
    SELECT extname, extversion
    FROM   pg_extension
    WHERE  extname IN ('plv8', 'pljava');
  • 在 CI 里验证外部语言函数。 这类函数最容易在 PG 大版本升级或运行时升级后静默失效。

    bash
    psql -v ON_ERROR_STOP=1 -c "SELECT ext.is_email('a@b.com');"
  • PL/v8 的全局缓存必须有上限或过期策略。 官方提供 plv8_reset() 作为兜底手段。

  • 优先用「预编译/预打包」的发行版包,而不是在生产机上现场编译。 编译 V8 或 JVM 工具链会显著拉长部署时间并引入不可复现的构建差异。

  • 给关键的外部语言函数写降级方案。 一旦扩展不可用,业务应能回退到核心语言实现的等价函数。

下一节

本节完成后,「存储过程与触发器」这一部分就全部结束了。接下来进入服务端配置与性能调优,从最核心的配置文件开始:postgresql.conf 配置。

参考链接 ​

PL/Java ​

PL/v8 ​

PostgreSQL 官方 ​