PL/Scheme
PL/Scheme 是一个用于 Scheme 编程语言的 PostgreSQL 过程语言处理程序。 PL/Scheme 在后台使用 Chibi Scheme 作为其 Scheme 解释器。 凭借大量内置的 SRFI 和 Chibi Scheme 的完全 R7RS 兼容性,PL/Scheme 可以以 Lisp 风格启动 PostgreSQL 过程。PL/Scheme 默认是没有启用的,但是可以用CREATE EXTENSION plscheme
为特定数据库启用它。
要用 PL/Scheme 语言创建一个函数,可使用标准的 CREATE FUNCTION 语法:
CREATE FUNCTION funcname (argument-types) RETURNS return-type
-- 函数相关属性
AS $$
; PL/Scheme 函数体
$$ LANGUAGE plscheme;
函数的主体就是普通的 Scheme 代码。事实上,PL/Scheme 的粘合代码会把它包裹在一个 Scheme 子程序中。一个 PL/Scheme 函数会在一种标量上下文中被调用,因此它无法返回列表。如下文所述,可以通过返回引用来返回非标量值(数组、记录和集合)。
在一个 PL/Scheme 过程中,任何从 Scheme 代码返回的值都会被忽略。
PL/Scheme 也支持用 DO 语句调用的匿名代码块:
DO $$
; PL/Scheme 代码
$$ LANGUAGE plscheme;
一个匿名代码块没有参数,并且它返回的任何值都会被抛弃。否则其行为就像一个函数。
CREATE FUNCTION
命令的语法要求函数体被写作一个字符串常量。通常对字符串常量使用美元符号引用(见 第 4.1.2.4 节)最方便。如果选择使用转义字符串语法E''
,必须双写任何在函数体中使用的单引号'
和反斜线\
(见 第 4.1.2.1 节)。
参数和结果的处理和在任何其他 Scheme 子程序中一样:参数会以 Scheme 变量的形式传递到函数体中,并且函数中计算的最后一个表达式会作为结果值返回。
例如,一个返回两个整数值中较大值的函数可以定义为:
CREATE FUNCTION scheme_max (a integer, b integer) RETURNS integer
AS $$
(if (> a b) a b)
$$ LANGUAGE plscheme;
一般来讲,PL/Scheme 的目标是提供在 PostgreSQL 和 Scheme 世界之间的一种“自然的”映射。这包括下面介绍的数据映射规则。
在调用一个 PL/Scheme 函数时,它的参数会被从 PostgreSQL 的数据类型转换成相应的 Scheme 类型:
- PostgreSQL 的
boolean
被转换成 Scheme 的boolean
。 - PostgreSQL 的
smallint
和int
被转换成 Scheme 的fixnum
。PostgreSQL 的bigint
和oid
也被转换成 Scheme 的fixnum
。 - PostgreSQL 的
real
和double
被转换成 Scheme 的flonum
。 - PostgreSQL 的
numeric
被转换成 Scheme 的flonum
。 - PostgreSQL 的
bytea
被转换成 Scheme 的bytevector
。 - 包括 PostgreSQL 字符串类型在内的所有其他数据类型会被转换成一个 Scheme 的
string
。 - 对于非标量数据类型,请见下文。
当一个 PL/Scheme 函数返回时,会按照下列规则把它的返回值转换成该函数声明的 PostgreSQL 返回数据类型:
- 当 PostgreSQL 返回类型是
boolean
时,返回值会被根据 Scheme 规则计算真假。 - 当 PostgreSQL 返回类型是
bytea
时,返回值会被使用相应的 Scheme 内建机制转换成bytea
。 - 对于所有其他 PostgreSQL 返回类型,返回值被使用 Scheme 内建的
string
转换成一个串,并且结果会被传递给 PostgreSQL 数据类型的输入函数。 - 对于非标量数据类型,请见下文。
注意所声明的 PostgreSQL 返回类型和实际返回对象的 Scheme 数据类型之间的逻辑失配不会被标志,无论怎样该值都会被转换。
如果一个 SQL 空值被传递给一个函数,该参数值将作为 Scheme 中的()
出现。例如,上面函数和参数中展示的scheme_max
的函数定义对于空值输入将会返回错误的回答。我们可以为函数定义增加STRICT
让 PostgreSQL 做得更加合理:如果一个空值被传入,该函数将根本不会被调用,而只是自动地返回一个空结果。此外,我们可以在函数体中检查空输入:
CREATE FUNCTION scheme_max (a integer, b integer) RETURNS integer
AS $$
(if (or (null? a) (null? b))
'()
(if (> a b) a b))
$$ LANGUAGE plscheme;
如前所示,要从一个 PL/Scheme 函数返回一个 SQL 空值,可返回值()
。不管该函数严格与否都可以这样做。
SQL 数组会被作为一个 Scheme 向量传递到 PL/Scheme 中。要从一个 PL/Scheme 函数中返回出一个 SQL 数组值,可返回一个 Scheme 向量:
CREATE OR REPLACE FUNCTION return_arr() RETURNS int[]
AS $$
(vector 1 2 '() 3)
$$ LANGUAGE plscheme;
SELECT return_arr();
return_arr
--------------------
[0:3]={1,2,NULL,3}
(1 row)
我们可以在 PL/Scheme 函数或者匿名代码块中导入 Scheme 提供的标准模块,例如:
DO $$
(import (scheme small)
(srfi 1))
$$ LANGUAGE plscheme;
这个导入可以作用于当前会话对 PL/Scheme 函数或者匿名代码块的执行。
默认语言是来自 R7RS 的(scheme base)
库,它主要是 R5RS 的超集。
解释器默认区分大小写,跟 R6RS 和 R7RS 一样,但与 R5RS 不同。默认配置包括了完整的数值类型体系:fixnum
、flonum
、bignum
、精确有理数和复数。此列表包括 R7RS 定义的标准库:
- (scheme base) - R7RS 基础库
- (scheme case-lambda) - R7RS case-lambda 库
- (scheme char) - R7RS 字符库
- (scheme complex) - R7RS 复数库
- (scheme cxr) - R7RS CxR 访问器库
- (scheme eval) - R7RS eval 库
- (scheme file) - R7RS 文件库
- (scheme inexact) - R7RS 非精确数值库
- (scheme lazy) - R7RS 惰性求值库
- (scheme load) - R7RS 代码加载库
- (scheme process-context) - R7RS 进程上下文库
- (scheme read) - R7RS 代码对象读入库
- (scheme repl) - R7RS 读取/评估/打印循环库
- (scheme time) - R7RS 时间库
- (scheme write) - R7RS 代码对象生成库
- (scheme r5rs) - R5RS 标准库,它是其他库的一个包装
- (scheme small) - R7RS 标准库,它包含了上面所有的库
Chibi Scheme 中提供了许多 SRFI。请注意,SRFI 0、6、23、46 和 62 已内置到默认环境中,因此无需导入它们。此列表包括流行的 SRFI 或标准 Chibi 模块中使用到的 SRFI:
- (srfi 0) - cond-expand: 基于特征的条件扩展构造
- (srfi 1) - 列表库
- (srfi 2) - and-let*
- (srfi 6) - 基础字符串端口
- (srfi 8) - receive: 绑定到多个值
- (srfi 9) - define-record-type
- (srfi 11) - let-values/let*-values: 接收多个值的语法
- (srfi 14) - 字符集库
- (srfi 16) - case-lambda: 具有可变数量参数的过程的语法
- (srfi 18) - 多线程支持
- (srfi 23) - 错误报告机制
- (srfi 26) - 不柯里化的特殊参数表示法
- (srfi 27) - 随机位的来源
- (srfi 33) - 位运算符
- (srfi 38) - 读写共享结构的数据
- (srfi 39) - 参数对象
- (srfi 41) - 流
- (srfi 46) - 基础语法规则扩展
- (srfi 55) - require-extension
- (srfi 62) - S 表达式形式的注释
- (srfi 69) - 基础哈希表
- (srfi 95) - 排序和合并
- (srfi 98) - 环境访问
- (srfi 99) - ERR5RS 标准的记录
- (srfi 101) - 纯函数式可随机访问的配对和列表
- (srfi 111) - boxes
- (srfi 113) - 集合和包
- (srfi 115) - Scheme 正则表达式
- (srfi 116) - 不可变列表库
- (srfi 117) - 可变队列
- (srfi 121) - 数据生成器
- (srfi 124) - ephemerons
- (srfi 125) - 通用哈希表
- (srfi 127) - 惰性序列
- (srfi 128) - 组合比较器
- (srfi 129) - Unicode 字符大小写函数库
- (srfi 130) - 基于游标的字符串库
- (srfi 132) - 排序库
- (srfi 133) - 向量库
- (srfi 134) - 不可变的双向队列
- (srfi 135) - 不可变的文本
- (srfi 139) - 语法参数
- (srfi 141) - 整数除法
- (srfi 142) - 按位运算
- (srfi 143) - 精确整数
- (srfi 144) - 浮点型数字
- (srfi 145) - 假设
- (srfi 147) - 自定义宏转换器
- (srfi 151) - 位运算符
- (srfi 154) - 一等的动态范围
- (srfi 158) - 数据生成器和聚合器
- (srfi 160) - 同质数值向量库
- (srfi 165) - Monad 计算环境
- (srfi 166) - Monad 格式化
- (srfi 188) - 语法关键字的拼接绑定结构
额外的非标准模块被放置在(chibi)
模块命名空间中。
- (chibi app) - 统一选项解析和配置
- (chibi ast) - 抽象语法树和其他内部数据类型
- (chibi base64) - Base64 编码和解码
- (chibi bytevector) - 字节向量处理函数库
- (chibi config) - 通用配置管理
- (chibi crypto md5) - MD5 哈希
- (chibi crypto rsa) - RSA 公钥加密
- (chibi crypto sha2) - SHA-2 哈希
- (chibi diff) - LCS 算法和差异函数库
- (chibi disasm) - 虚拟机的反汇编器
- (chibi doc) - Chibi 文档函数库
- (chibi edit-distance) - 一个 levenshtein 距离实现
- (chibi equiv) - 一个保证终止的
equal?
版本 - (chibi filesystem) - 文件系统和文件描述符对象的接口
- (chibi generic) - CLOS 风格的面向对象编程的通用方法
- (chibi heap-stats) - 用于收集堆统计信息的函数库
- (chibi io) - 各种 I/O 扩展和自定义端口
- (chibi iset base) - 紧凑整数集
- (chibi iset constructors) - 紧凑整数集的构造
- (chibi iset iterators) - 遍历紧凑整数集
- (chibi json) - JSON 读写
- (chibi loop) - 快速且可扩展的循环语法
- (chibi match) - 直观且广泛支持的模式匹配语法
- (chibi math prime) - 素数函数库
- (chibi memoize) - 过程记忆
- (chibi mime) - 将 MIME 文件解析为 SXML
- (chibi modules) - 模块系统访问函数库
- (chibi net) - 简单的网络接口
- (chibi net http-server) - 带有 servlet 支持的简单 HTTP 服务器
- (chibi net servlet) - 用于 HTTP 服务器或 CGI 的 HTTP servlet
- (chibi parse) - 语法方便的解析器组合器
- (chibi pathname) - 用于分解和操作路径名的函数库
- (chibi process) - 创建进程和处理信号的接口
- (chibi repl) - 功能齐全的读取/评估/打印循环
- (chibi scribble) - 用于编写本手册的涂鸦式语法解析器
- (chibi string) - 基于游标的字符串库 (SRFI 130 的前身)
- (chibi stty) - ioctl 的高级接口
- (chibi sxml) - SXML 函数库
- (chibi system) - 访问主机系统和当前用户信息
- (chibi temp-file) - 临时文件和目录创建
- (chibi test) - 一个简单的单元测试框架
- (chibi time) - 获取当前系统时间的接口
- (chibi trace) - 跟踪过程调用的函数库
- (chibi type-inference) - 一个易于使用的类型推理系统
- (chibi uri) - 解析和构造 URI 的函数库
- (chibi weak) - 具有弱引用的数据结构
使用raise
函数报告消息以及抛出错误。
(raise level message)
level
选项指定了错误的严重性。允许的级别有debug
、log
、info
、notice
, warning
以及exception
。exception
会抛出一个错误(通常会中止当前事务)。其他级别仅仅是产生不同优先级的消息。不管一个特定优先级的消息是被报告给客户端、还是写到服务器日志、亦或是二者同时都做,这都由 log_min_messages 和 client_min_messages 配置变量控制。详见第 19 章。
在这个例子中,我们报告了一个notice
级别的简单消息:
(raise "notice" "Notice me.")