PostgreSQL7.0手册-用户手册-10. PL/pgSQL - SQL 过程语言
用 EXIT 语句退出循环,最后赋值的行仍然在循环后可以被访问.
EXIT [ label ] [ WHEN expression ];
如果没有标记 label ,最内层的循环将被结束并且 END LOOP 后面的语句将被接着执行.如果给出了标记 label ,该标记必须是当前或者上层嵌套的循环块.那么命名的循环或者语句块被终止并且控制落到循环/语句块的对应 END 的后面一条语句.
触发器过程
PL/pgSQL 可以用于定义触发器过程.它们通过通常的 CREATE FUNCTION 命令创建为没有参数并且返回 OPAQUE 类型的函数.
做为触发器过程的函数有一些 Postgres 特有的细节说明.
首先他们有一些在顶层的声明段里自动定义的特殊变量.有如下这些
NEW
数据类型是 RECORD;该变量保存着行(ROW)一级的触发器在 INSERT/UPDATE 操作时的新的数据库行.
OLD
数据类型是 RECORD;该变量保存着行(ROW)一级的触发器在 INSERT/UPDATE 操作时的旧的数据库行。
TG_NAME
数据类型是 name;该变量包含实际触发的触发器名.
TG_WHEN
数据类型是 text;是一个由触发器定义决定的字符串,要么是 'BEFORE' 要么是 'AFTER'.
TG_LEVEL
数据类型是 text;是一个由触发器定义决定的字符串,要么是 'ROW' 要么是 'STATEMENT'.
TG_OP
数据类型是 text;是一个说明触发器实际进行的操作的字符串,可以是 'INSERT','UPDATE' 或者 'DELETE'.
TG_RELID
数据类型是 oid;是导致触发器调用的表的对象标识(OID).
TG_RELNAME
数据类型是 name;是激活触发器调用的表的名称.
TG_NARGS
数据类型是 integer;是在 CREATE TRIGGER 语句里面赋予触发器过程的参数的个数.
TG_ARGV[]
数据类型是 text 的数组;是 CREATE TRIGGER 语句里的参数.下标从 0 开始记数,并且可以由一个表达式来表示.非法下标(< 0 或 >= tg_nargs)导致一个 NULL 值的返回.
其次,它们必须返回 NULL 或者是一个与导致触发器运行的表的记录/行完全一样的结构的数据.AFTER 类型的触发器可以总是返回一个没有意义的 NULL 值.BEFORE 类的触发器如果返回一个 NULL,将发送一个信号给触发器管理器忽略对实际行的操作.否则,返回的记录/行将代替插入/更新操作中的行.我们可能用一个值直接代替 NEW 里的数值并且返回之或者我们也可以构建一个完全新的返回记录/行.
例外
Postgres 不具有一个很好的例外处理模块.当分析器,规划器(调度器)/优化器或者执行器认为一个语句不能在处理下去了,整个事务都退出并且系统跳回主循环等待从客户应用过来的下一个查询.
我们可以'钩'在错误机制上来提示这种情况的发生.但是目前我们没有能力告诉(用户)是什么导致了退出(输入/输出转换错误,浮点数错误,分析错误).并且此时的数据库后端可能处在一种不连贯的状态,所以退回到上层执行器或执行更多的命令可能摧毁整个数据库.而且此时事务退出的信息可能已经发送给了客户端应用,所以继续操作没有任何意义.
因此,PL/pgSQL 在函数或触发器操作时遇到退出的唯一一项操作是在 DEBUG 级别运行时输出一些附加的日志信息,报告在哪个函数和在那里(行号和语句类型)出了错.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
例子
这里是几个例子用以说明书写 PL/pgSQL 函数是多么地容易.对于更复杂的例子,程序员们可以看看用于 PL/pgSQL 回归测试的程序.
书写 PL/pgSQL 的一个很痛苦的细节是单引号的使用.CREATE FUNCTION 的函数原文本必须是一个语言字符串.在一个语言字符串里的单引号要么是两个单引号或者是用反斜杠转意.我们仍然在寻找一种优美的代替物.同时,我们应该象下面例子那样使用双引号.将来任何版本的 Postgres 对此的任何解决方法都将保持向下兼容.
一些简单的 PL/pgSQL 函数
下面的两个 PL/pgSQL 函数与 C 语言里讨论的对应函数是一样的.
CREATE FUNCTION add_one (int4) RETURNS int4 AS '
BEGIN
RETURN $1 + 1;
END;
' LANGUAGE 'plpgsql';
CREATE FUNCTION concat_text (text, text) RETURNS text AS '
BEGIN
RETURN $1 || $2;
END;
' LANGUAGE 'plpgsql';
复合类型的 PL/pgSQL 函数
同样下面的是等效于 C 函数样例的 PL/pgSQL 函数.
CREATE FUNCTION c_overpaid (EMP, int4) RETURNS bool AS '
DECLARE
emprec ALIAS FOR $1;
sallim ALIAS FOR $2;
BEGIN
IF emprec.salary ISNULL THEN
RETURN ''f'';
END IF;
RETURN emprec.salary > sallim;
END;
' LANGUAGE 'plpgsql';
PL/pgSQL 触发器过程
下面的触发器的作用是:任何时候表中插入或更新了行,当前的用户名和时间都记录入行中.并且它保证给出了雇员名称并且薪水是一个正数.
CREATE TABLE emp (
empname text,
salary int4,
last_date datetime,
last_user name);
CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
BEGIN
-- 检查是否给出了 empname 和 salary
IF NEW.empname ISNULL THEN
RAISE EXCEPTION ''empname cannot be NULL value'';
END IF;
IF NEW.salary ISNULL THEN
RAISE EXCEPTION ''% cannot have NULL salary'', NEW.empname;
END IF;
-- 我们必须付帐给谁?
IF NEW.salary < 0 THEN
RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname;
END IF;
EXIT [ label ] [ WHEN expression ];
如果没有标记 label ,最内层的循环将被结束并且 END LOOP 后面的语句将被接着执行.如果给出了标记 label ,该标记必须是当前或者上层嵌套的循环块.那么命名的循环或者语句块被终止并且控制落到循环/语句块的对应 END 的后面一条语句.
触发器过程
PL/pgSQL 可以用于定义触发器过程.它们通过通常的 CREATE FUNCTION 命令创建为没有参数并且返回 OPAQUE 类型的函数.
做为触发器过程的函数有一些 Postgres 特有的细节说明.
首先他们有一些在顶层的声明段里自动定义的特殊变量.有如下这些
NEW
数据类型是 RECORD;该变量保存着行(ROW)一级的触发器在 INSERT/UPDATE 操作时的新的数据库行.
OLD
数据类型是 RECORD;该变量保存着行(ROW)一级的触发器在 INSERT/UPDATE 操作时的旧的数据库行。
TG_NAME
数据类型是 name;该变量包含实际触发的触发器名.
TG_WHEN
数据类型是 text;是一个由触发器定义决定的字符串,要么是 'BEFORE' 要么是 'AFTER'.
TG_LEVEL
数据类型是 text;是一个由触发器定义决定的字符串,要么是 'ROW' 要么是 'STATEMENT'.
TG_OP
数据类型是 text;是一个说明触发器实际进行的操作的字符串,可以是 'INSERT','UPDATE' 或者 'DELETE'.
TG_RELID
数据类型是 oid;是导致触发器调用的表的对象标识(OID).
TG_RELNAME
数据类型是 name;是激活触发器调用的表的名称.
TG_NARGS
数据类型是 integer;是在 CREATE TRIGGER 语句里面赋予触发器过程的参数的个数.
TG_ARGV[]
数据类型是 text 的数组;是 CREATE TRIGGER 语句里的参数.下标从 0 开始记数,并且可以由一个表达式来表示.非法下标(< 0 或 >= tg_nargs)导致一个 NULL 值的返回.
其次,它们必须返回 NULL 或者是一个与导致触发器运行的表的记录/行完全一样的结构的数据.AFTER 类型的触发器可以总是返回一个没有意义的 NULL 值.BEFORE 类的触发器如果返回一个 NULL,将发送一个信号给触发器管理器忽略对实际行的操作.否则,返回的记录/行将代替插入/更新操作中的行.我们可能用一个值直接代替 NEW 里的数值并且返回之或者我们也可以构建一个完全新的返回记录/行.
例外
Postgres 不具有一个很好的例外处理模块.当分析器,规划器(调度器)/优化器或者执行器认为一个语句不能在处理下去了,整个事务都退出并且系统跳回主循环等待从客户应用过来的下一个查询.
我们可以'钩'在错误机制上来提示这种情况的发生.但是目前我们没有能力告诉(用户)是什么导致了退出(输入/输出转换错误,浮点数错误,分析错误).并且此时的数据库后端可能处在一种不连贯的状态,所以退回到上层执行器或执行更多的命令可能摧毁整个数据库.而且此时事务退出的信息可能已经发送给了客户端应用,所以继续操作没有任何意义.
因此,PL/pgSQL 在函数或触发器操作时遇到退出的唯一一项操作是在 DEBUG 级别运行时输出一些附加的日志信息,报告在哪个函数和在那里(行号和语句类型)出了错.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
例子
这里是几个例子用以说明书写 PL/pgSQL 函数是多么地容易.对于更复杂的例子,程序员们可以看看用于 PL/pgSQL 回归测试的程序.
书写 PL/pgSQL 的一个很痛苦的细节是单引号的使用.CREATE FUNCTION 的函数原文本必须是一个语言字符串.在一个语言字符串里的单引号要么是两个单引号或者是用反斜杠转意.我们仍然在寻找一种优美的代替物.同时,我们应该象下面例子那样使用双引号.将来任何版本的 Postgres 对此的任何解决方法都将保持向下兼容.
一些简单的 PL/pgSQL 函数
下面的两个 PL/pgSQL 函数与 C 语言里讨论的对应函数是一样的.
CREATE FUNCTION add_one (int4) RETURNS int4 AS '
BEGIN
RETURN $1 + 1;
END;
' LANGUAGE 'plpgsql';
CREATE FUNCTION concat_text (text, text) RETURNS text AS '
BEGIN
RETURN $1 || $2;
END;
' LANGUAGE 'plpgsql';
复合类型的 PL/pgSQL 函数
同样下面的是等效于 C 函数样例的 PL/pgSQL 函数.
CREATE FUNCTION c_overpaid (EMP, int4) RETURNS bool AS '
DECLARE
emprec ALIAS FOR $1;
sallim ALIAS FOR $2;
BEGIN
IF emprec.salary ISNULL THEN
RETURN ''f'';
END IF;
RETURN emprec.salary > sallim;
END;
' LANGUAGE 'plpgsql';
PL/pgSQL 触发器过程
下面的触发器的作用是:任何时候表中插入或更新了行,当前的用户名和时间都记录入行中.并且它保证给出了雇员名称并且薪水是一个正数.
CREATE TABLE emp (
empname text,
salary int4,
last_date datetime,
last_user name);
CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
BEGIN
-- 检查是否给出了 empname 和 salary
IF NEW.empname ISNULL THEN
RAISE EXCEPTION ''empname cannot be NULL value'';
END IF;
IF NEW.salary ISNULL THEN
RAISE EXCEPTION ''% cannot have NULL salary'', NEW.empname;
END IF;
-- 我们必须付帐给谁?
IF NEW.salary < 0 THEN
RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname;
END IF;

