PL/Scheme 代码即数据

John Doe 十月 26, 2022

有时将数据引入代码很有用,而其他时候,将代码引入数据是有用的。

存储过程和应用代码

软件开发者经常会纠结的一个问题是,一类业务逻辑是应该放到存储过程实现,还是放在应用代码中实现?

将业务逻辑放到存储过程实现,开发者担心会对数据库产品产生依赖。存储过程的语法并没有一套 SQL 标准,数据库厂商对于存储过程都有自己的语法定义和特殊实现。如果以后业务访问流量和数据量上涨,需要对数据库进行分布式改造,那还需要将存储过程再转换成应用代码。很多具有互联网行业从业经验的开发者,一般都会有这样的担心。

一些大型互联网公司,在刚起步时因为人力投入不足和业务流量也可控,选择将业务逻辑放到存储过程实现,快速构建起了早期的产品业务架构。当业务发展到一定规模后,因为业务架构已不适用和商业数据库支付成本等问题,不得已将存储过程再转换成应用代码。此后更是确立研发规章,要完全抛弃数据库存储过程,就像是患上了“存储过程恐惧症”。

而在传统IT行业里面,有些软件厂商倒是不怎么排斥使用存储过程,主要有几点原因:

  1. 基于存储过程的应用软件产品,更具有通用性。对于客户的定制化需求,只需要在客户现场新增或修改存储过程实现,并不需要去修改代码发布版本。
  2. 应用软件产品的通用性越高,业务覆盖的客户越多,企业发展也越好。
  3. 应用软件产品的通用性越高,需要雇佣的软件开发者也更少,因为不需要频繁地去修改代码发布版本。
  4. 培养和雇佣一个会写存储过程的工作人员,人力成本往往比一个软件开发者要低。

面向对象编程

在软件开发者群体中,面向对象的编程思想是非常受到欢迎的。当他们构造完一个接口或抽象类后,往往都会产生一种发自内心的欣喜之情,恨不得立刻要告诉全世界:快看,我刚刚构造了一个多么完美的抽象类,以后老板再让我实现相关的业务需求时,我就只需要轻松添加一个具体实现的类就可以了!

但是,不管面向对象的编程思想是多么的先进,最后,你还是需要修改代码发布版本才能满足新的业务需求。

代码即数据

PostgreSQL 的过程语言赋予了软件开发者一种新的能力,你可以在数据库中使用常规的编程语言实现业务逻辑。甚至,你还可以结合数据库关系模型的基础能力,应用面向对象的编程思想。

下面我们用一个示例,介绍如何基于面向对象的编程思想,将代码存放在数据库中:

CREATE DOMAIN scheme_code AS TEXT CHECK(VALUE ~ '^\(.*\)$');

CREATE FUNCTION calc(code scheme_code, x numeric)
RETURNS numeric
AS $$
  (apply (eval (read (open-input-string code)))
    (list x))
$$ LANGUAGE plscheme;

CREATE TABLE shape (
  type    text,
  length  scheme_code,
  area    scheme_code
);

其中表shape包括的列如下:

名称 描述
type 形状的名称
length 计算形状的长度的代码
area 计算形状的面积的代码

我们可以将各种形状的相关信息插入到数据表shape

type length area
triangle (lambda (x) (* x 3)) (lambda (x) (/ (* x x) 2))
square (lambda (x) (* x 4)) (lambda (x) (* x x))
circle (lambda (r) (* 2 3.14 r)) (lambda (r) (* 3.14 r r))

有关 PL/Scheme 的更多详细信息,请参阅 PL/Scheme 文档