作者
digoal
日期
2020-07-15
标签
PostgreSQL , plpgsql , 函数 , 存储过程 , 调试 , debug
背景
pgadmin plpgsql存储过程调试依赖pldebugger插件:
https://git.postgresql.org/gitweb/?p=pldebugger.git;a=summary
实际上不需要pldebugger以及pgadmin图形化模式, 在text模式下也能调试存储过程.
如果不需要控制, 只是打印一下执行过程中的变量, 直接raise就可以.
例子:
```
create or replace function test() returns void as $$
declare
begin
for i in 1..10 loop
raise notice 'i: %', i;
end loop;
end;
$$ language plpgsql strict;
postgres=# select test();
NOTICE: i: 1
NOTICE: i: 2
NOTICE: i: 3
NOTICE: i: 4
NOTICE: i: 5
NOTICE: i: 6
NOTICE: i: 7
NOTICE: i: 8
NOTICE: i: 9
NOTICE: i: 10
test
(1 row)
```
如果需要加入控制, 可以使用如下设计方法:
1、断点控制表
每条记录代表一个断点, 通过更新状态字段的内容, 开或者关代表启用和停用断点.
2、断点控制函数
读取断点控制表的状态, 响应断点状态.
3、被调试函数
植入断点控制函数
开发者执行通过修改断点控制表的值来控制断点,
打印变量
例子:
外部控制表
create table bk (
n name primary key, -- 断点名称
status boolean default true -- true表示开启断点, false表示退出断点
);
内部控制表
create table bk_tmp ( -- 临时状态, 用于断点重复出现的情况
n name primary key, -- 断点名称
status boolean default true -- true表示开启断点, false表示退出断点
);
断点函数
```
create or replace function bkf(name) returns void as $$
declare
v_stat1 boolean;
v_stat2 boolean;
begin
select status into v_stat1 from bk where n=$1;
select status into v_stat2 from bk_tmp where n=$1;
if v_stat1 is null then
raise notice 'The % bk name not set, or bk deleted, will skip this bk and continue!', $1;
return;
end if;
if (v_stat1 is not null and v_stat2 is null) then
insert into bk_tmp values ($1) on conflict (n) do nothing;
end if;
loop
select status into v_stat1 from bk where n=$1; -- 查看当前断点状态
select status into v_stat2 from bk_tmp where n=$1; -- 查看当前断点状态
if v_stat1 <> v_stat2 then -- 用户退出断点
update bk_tmp set status=v_stat1 where n=$1;
return; -- 返回
end if;
if v_stat1 is null then -- 用户删除断点
delete from bk_tmp where n=$1;
raise notice 'The % bk deleted, exit this bk!', $1;
return;
end if;
perform pg_sleep(1); -- 睡眠
end loop;
end;
$$ language plpgsql strict;
```
打印变量 -- 可以支持, 需要查询pl stack context的接口.
修改变量 -- 可以支持, 需要修改pl stack context的接口.
暂时不支持以上两个接口, 所以打印应该放在主函数内.
调试如下函数, 植入断点, 在断点前打印你想了解的变量, 或者修改你想了解的变量.
create or replace function testf (int, int) returns boolean as $$
declare
res int :=0;
begin
for i in $1..$2 loop
res := i+res;
raise notice 'res: %', res; -- 打印变量
perform bkf('testbk'); -- 使用testbk这个断点名称
end loop;
if res >10000 then return true; else return false; end if;
end;
$$ language plpgsql strict;
在外部控制表插入断点名称
insert into bk values ('testbk');
执行函数, 查看断点效果
select testf(1,100);
效果如下:
postgres=# select testf(1,100);
NOTICE: res: 1
中断 -- 遇到断点
update bk set status =not status where n='testbk'; -- 继续
继续
NOTICE: res: 3
中断
update bk set status =not status where n='testbk'; -- 继续
继续
NOTICE: res: 6
中断
delete from bk where n='testbk'; -- 删除断点
删除断点后得到如下效果:
```
...
NOTICE: The testbk bk deleted, exit this bk!
NOTICE: res: 21
NOTICE: The testbk bk name not set, or bk deleted, will skip this bk and continue!
NOTICE: res: 28
NOTICE: The testbk bk name not set, or bk deleted, will skip this bk and continue!
NOTICE: res: 36
...
NOTICE: res: 5050
NOTICE: The testbk bk name not set, or bk deleted, will skip this bk and continue!
testf
f
(1 row)
```
PostgreSQL 许愿链接
您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.
9.9元购买3个月阿里云RDS PostgreSQL实例
PostgreSQL 解决方案集合
德哥 / digoal's github - 公益是一辈子的事.





