此扩展是PostgreSQL的PLPGSQL的完整衬里。它仅利用内部PostgreSQL解析器/评估器,因此您可以准确地看到运行时发生错误。此外,它在例程中解析了SQL,并发现在“创建过程/函数”命令中通常找不到错误。您可以控制许多警告和提示的水平。最后,您可以添加Pragama类型标记以关闭/在许多方面,使您可以隐藏已经知道的消息,或者提醒您以后再进行更深层的清洁。
我创建了这个项目,因为我想发布我在两年中写的代码,当时我尝试编写上游的PostgreSQL检查。它没有完全成功 - 整合到上游需要一些更大的PLPGSQL重构。但是该代码功能齐全,可用于生产(并且用于生产)。因此,我创建了此扩展名,可用于所有PLPGSQL开发人员。
如果您想加入我们的小组以帮助进一步开发此扩展名,请在该PostgreSQL扩展名Google组中注册自己。
我邀请任何想法,补丁,虫子。
支持PostgreSQL PostgreSQL 12-17。
PL/PGSQL功能内的SQL语句已由验证器检查是否有语义错误。这些错误可以通过调用PLPGSQL_CHECK_FUNCTION:
postgres=# CREATE EXTENSION plpgsql_check;
LOAD
postgres=# CREATE TABLE t1(a int, b int);
CREATE TABLE
postgres=#
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE r record;
BEGIN
FOR r IN SELECT * FROM t1
LOOP
RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
END LOOP;
END;
$function$;
CREATE FUNCTION
postgres=# select f1(); -- execution doesn't find a bug due to empty table t1
f1
────
(1 row)
postgres=# x
Expanded display is on.
postgres=# select * from plpgsql_check_function_tb('f1()');
─[ RECORD 1 ]───────────────────────────
functionid │ f1
lineno │ 6
statement │ RAISE
sqlstate │ 42703
message │ record "r" has no field "c"
detail │ [null]
hint │ [null]
level │ error
position │ 0
query │ [null]
postgres=# sf+ f1
CREATE OR REPLACE FUNCTION public.f1()
RETURNS void
LANGUAGE plpgsql
1 AS $function$
2 DECLARE r record;
3 BEGIN
4 FOR r IN SELECT * FROM t1
5 LOOP
6 RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column
7 END LOOP;
8 END;
9 $function$
函数plpgsql_check_function()具有三种可能的输出格式:文本,JSON或XML
select * from plpgsql_check_function('f1()', fatal_errors := false);
plpgsql_check_function
------------------------------------------------------------------------
error:42703:4:SQL statement:column "c" of relation "t1" does not exist
Query: update t1 set c = 30
-- ^
error:42P01:7:RAISE:missing FROM-clause entry for table "r"
Query: SELECT r.c
-- ^
error:42601:7:RAISE:too few parameters specified for RAISE
(7 rows)
postgres=# select * from plpgsql_check_function('fx()', format:='xml');
plpgsql_check_function
────────────────────────────────────────────────────────────────
<Function oid="16400"> ↵
<Issue> ↵
<Level>error</level> ↵
<Sqlstate>42P01</Sqlstate> ↵
<Message>relation "foo111" does not exist</Message> ↵
<Stmt lineno="3">RETURN</Stmt> ↵
<Query position="23">SELECT (select a from foo111)</Query>↵
</Issue> ↵
</Function>
(1 row)
您可以通过函数参数设置警告级别:
funcoid oid功能名称或功能签名 - 功能需要函数规范。 PostgreSQL中的任何功能都可以由OID或名称或签名指定。当您知道OID或完整函数的签名时,可以使用Regpropedure类型参数,例如'fx()'::regprocedure或16799::regprocedure 。可能的替代方法仅在函数名称唯一时才使用名称 - 例如'fx' 。当名称不是唯一的或不存在函数时,它会引起错误。relid DEFAULT 0用触发功能分配的关系的OID。有必要检查任何触发功能。您正在发送扳机操作的表。
fatal_errors boolean DEFAULT true - 停止第一个错误(防止大规模错误报告)
other_warnings boolean DEFAULT true左侧和右侧的agissmenet中的不同属性号显示,变量重叠函数的参数,未使用的变量,不需要的施法等。
extra_warnings boolean DEFAULT true显示警告,例如丢失的RETURN ,阴影变量,死亡代码,从不读取(未使用)函数的参数,未修改的变量,修改后的自动变量,等等。
performance_warnings boolean DEFAULT false - 与性能相关的警告,例如用类型修饰符,铸造,隐式铸造在where子句中(可能是不使用索引的原因),等等。
security_warnings boolean DEFAULT false - 与安全相关的检查,例如SQL注入漏洞检测
compatibility_warnings boolean DEFAULT false - 兼容性与兼容性检查,例如过时的显式设置内部光标名称或光标变量中的内部光标名称。
anyelementtype regtype DEFAULT 'int' - 测试AnyElement类型时要使用的实际类型
anyenumtype regtype DEFAULT '-' - 测试AnyEnum类型时要使用的实际类型
anyrangetype regtype DEFAULT 'int4range' - 测试AnyRange类型时要使用的实际类型
anycompatibletype DEFAULT 'int' - 测试任何观点类型时要使用的实际类型
anycompatiblerangetype DEFAULT 'int4range' - 测试AnyCompatible范围类型时要使用的实际范围类型
without_warnings DEFAULT false - 禁用所有警告(忽略所有XXXX_WARNING参数,快速覆盖)
all_warnings DEFAULT false - 启用所有警告(忽略其他xxx_warning参数,快速阳性)
newtable DEFAULT NULL , oldtable DEFAULT NULL - 新的或旧的过渡表的名称。当在触发功能中使用过渡表时,需要这些参数。
use_incomment_options DEFAULT true当true true时,官方选项处于活动状态
incomment_options_usage_warning DEFAULT false当该false true时,则使用注册选项时会提高警告。
constant_tracing boolean DEFAULT true当正确的情况下,那么保存某些恒定内容的变量可以像常数一样使用(仅在某些简单情况下才能使用,并且变量的内容不应该是模棱两可的)。
当您想检查任何触发器时,必须输入将与触发功能一起使用的关系
CREATE TABLE bar(a int, b int);
postgres=# sf+ foo_trg
CREATE OR REPLACE FUNCTION public.foo_trg()
RETURNS trigger
LANGUAGE plpgsql
1 AS $function$
2 BEGIN
3 NEW.c := NEW.a + NEW.b;
4 RETURN NEW;
5 END;
6 $function$
缺少关系规范
postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR: missing trigger relation
HINT: Trigger relation oid must be valid
正确检查扳机检查(指定关系)
postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
对于具有及时表的触发器,您可以设置oldtable和newtable参数:
create or replace function footab_trig_func()
returns trigger as $$
declare x int;
begin
if false then
-- should be ok;
select count(*) from newtab into x;
-- should fail;
select count(*) from newtab where d = 10 into x;
end if;
return null;
end;
$$ language plpgsql;
select * from plpgsql_check_function('footab_trig_func','footab', newtable := 'newtab');
PLPGSQL_CHECK允许在注释中写入持久设置。这些选项在检查之前从函数源代码中获取。语法是:
@plpgsql_check_option: optioname [=] value [, optname [=] value ...]
评论选项的设置具有最高的优先级,但通常可以通过选项use_incomment_options将其禁用为false 。
例子:
create or replace function fx(anyelement)
returns text as $$
begin
/*
* rewrite default polymorphic type to text
* @plpgsql_check_options: anyelementtype = text
*/
return $1;
end;
$$ language plpgsql;
您可以使用plpgsql_check_function进行质量检查功能/过程和触发器的质量检查。请测试以下查询:
-- check all nontrigger plpgsql functions
SELECT p.oid, p.proname, plpgsql_check_function(p.oid)
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON pronamespace = n.oid
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
WHERE l.lanname = 'plpgsql' AND p.prorettype <> 2279;
或者
-- check all trigger plpgsql functions
SELECT p.proname, tgrelid::regclass, cf.*
FROM pg_proc p
JOIN pg_trigger t ON t.tgfoid = p.oid
JOIN pg_language l ON p.prolang = l.oid
JOIN pg_namespace n ON p.pronamespace = n.oid,
LATERAL plpgsql_check_function(p.oid, t.tgrelid) cf
WHERE n.nspname = 'public' and l.lanname = 'plpgsql';
或者
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0)) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
可以在执行时检查功能-PLPGSQL_CHECK模块必须加载(通过Postgresql.conf)。
plpgsql_check.mode = [ disabled | by_function | fresh_start | every_start ]
plpgsql_check.fatal_errors = [ yes | no ]
plpgsql_check.show_nonperformance_warnings = false
plpgsql_check.show_performance_warnings = false
默认模式为by_function ,这意味着通过调用PLPGSQL_CHECK_FUNCTICT仅在活动模式下完成增强检查。 fresh_start表示冷启动(首先调用功能)。
您可以通过
load 'plpgsql'; -- 1.1 and higher doesn't need it
load 'plpgsql_check';
set plpgsql_check.mode = 'every_start'; -- This scans all code before it is executed
SELECT fx(10); -- run functions - function is checked before runtime starts it
PostgreSQL光标和避难所的变量是增强的字符串变量,它具有相关门户的唯一名称(用于光标实现的Postgres的内部结构)。在Postgresql 16之前,该门户的名称与光标变量的名称相同。 PostgreSQL 16及更高的更改此机制,默认情况下,相关门户将以某些唯一名称命名。它解决了嵌套块中的光标或光标在递归称为函数中的问题。
随着上述更改,撤销器的变量应从另一个避难所变量或某些光标变量(打开光标打开时)获取值。
-- obsolete pattern
DECLARE
cur CURSOR FOR SELECT 1;
rcur refcursor;
BEGIN
rcur := 'cur';
OPEN cur;
...
-- new pattern
DECLARE
cur CURSOR FOR SELECT 1;
rcur refcursor;
BEGIN
OPEN cur;
rcur := cur;
...
当compatibility_warnings Flag处于活动状态时,然后plpgsql_check尝试识别一些渔民分配给避难所的变量或返回器值的返回:
CREATE OR REPLACE FUNCTION public.foo()
RETURNS refcursor
AS $$
declare
c cursor for select 1;
r refcursor;
begin
open c;
r := 'c';
return r;
end;
$$ LANGUAGE plpgsql;
select * from plpgsql_check_function('foo', extra_warnings =>false, compatibility_warnings => true);
┌───────────────────────────────────────────────────────────────────────────────────┐
│ plpgsql_check_function │
╞═══════════════════════════════════════════════════════════════════════════════════╡
│ compatibility:00000:6:assignment:obsolete setting of refcursor or cursor variable │
│ Detail: Internal name of cursor should not be specified by users. │
│ Context: at assignment to variable "r" declared on line 3 │
└───────────────────────────────────────────────────────────────────────────────────┘
(3 rows)
PLPGSQL_CHECK应该在真正静态代码上找到几乎所有错误。当开发人员使用PLPGSQL的动态功能(例如Dynamic SQL或记录数据类型)时,则可以使用误报。这些应该是罕见的 - 在写得很好的代码中 - 然后应重新设计影响的功能,或为此功能禁用PLPGSQL_CHECK。
CREATE OR REPLACE FUNCTION f1()
RETURNS void AS $$
DECLARE r record;
BEGIN
FOR r IN EXECUTE 'SELECT * FROM t1'
LOOP
RAISE NOTICE '%', r.c;
END LOOP;
END;
$$ LANGUAGE plpgsql SET plpgsql.enable_check TO false;
PLPGSQL_CHECK的使用增加了一个小开销(当启用了被动模式时),并且您应仅在开发或制作环境中使用该设置。
该模块未检查在运行时组装的查询。无法识别动态查询的结果 - 因此PLPGSQL_CHECK无法将正确的类型设置为记录变量,并且无法检查相关的SQL和表达式。
当不知道记录的变量类型时,您可以使用Pragma type明确分配它:
DECLARE r record;
BEGIN
EXECUTE format('SELECT * FROM %I', _tablename) INTO r;
PERFORM plpgsql_check_pragma('type: r (id int, processed bool)');
IF NOT r.processed THEN
...
PLPGSQL_CHECK不能用于检测参考光标的结构。 PLPGSQL中的光标引用作为全局光标的名称实现。在检查时间中,名称尚不清楚(并非所有可能性),并且不存在全局光标。对于任何静态分析,这都是一个重要的问题。 PLPGSQL不知道如何为记录变量设置正确的类型,并且无法检查因sql语句和表达式。对于动态SQL来说,解决方案相同。当您使用避难所类型或禁用这些功能的plpgsql_check时,请勿将记录变量用作目标。
CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS $$
DECLARE
rec_var record;
BEGIN
FETCH refcur_var INTO rec_var; -- this is STOP for plpgsql_check
RAISE NOTICE '%', rec_var; -- record rec_var is not assigned yet error
在这种情况下,不应使用记录类型(改用已知的rowtype):
CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS $$
DECLARE
rec_var some_rowtype;
BEGIN
FETCH refcur_var INTO rec_var;
RAISE NOTICE '%', rec_var;
PLPGSQL_CHECK无法通过在PLPGSQL函数运行时创建的临时表验证查询。对于此用例,有必要为此功能创建一个假临时表或禁用PLPGSQL_CHECK 。
实际上,临时表是自行存储的(每个用户)架构,其优先级高于持久表。因此,您可以做(使用以下技巧安全):
CREATE OR REPLACE FUNCTION public.disable_dml()
RETURNS trigger
LANGUAGE plpgsql AS $function$
BEGIN
RAISE EXCEPTION SQLSTATE '42P01'
USING message = format('this instance of %I table doesn''t allow any DML operation', TG_TABLE_NAME),
hint = format('you should use "CREATE TEMP TABLE %1$I(LIKE %1$I INCLUDING ALL);" statement',
TG_TABLE_NAME);
RETURN NULL;
END;
$function$;
CREATE TABLE foo(a int, b int); -- doesn't hold data, ever
CREATE TRIGGER foo_disable_dml
BEFORE INSERT OR UPDATE OR DELETE ON foo
EXECUTE PROCEDURE disable_dml();
postgres=# INSERT INTO foo VALUES(10,20);
ERROR: this instance of foo table doesn't allow any DML operation
HINT: you should to run "CREATE TEMP TABLE foo(LIKE foo INCLUDING ALL);" statement
postgres=#
CREATE TABLE
postgres=# INSERT INTO foo VALUES(10,20);
INSERT 0 1
此技巧会部分模拟全局温度表,并允许进行统计验证。另一种可能性是使用[模板外国数据包装器](https://github.com/okbob/template_fdw)
您可以使用Pragma table并创建短暂的表:
BEGIN
CREATE TEMP TABLE xxx(a int);
PERFORM plpgsql_check_pragma('table: xxx(a int)');
INSERT INTO xxx VALUES(10);
PERFORM plpgsql_check_pragma('table: [pg_temp].zzz(like schemaname.table1 including all)');
...
功能PLPGSQL_SHOW_DEPENDENCY_TB将显示所有功能,运算符和处理函数中使用的关系:
postgres=# select * from plpgsql_show_dependency_tb('testfunc(int,float)');
┌──────────┬───────┬────────┬─────────┬────────────────────────────┐
│ type │ oid │ schema │ name │ params │
╞══════════╪═══════╪════════╪═════════╪════════════════════════════╡
│ FUNCTION │ 36008 │ public │ myfunc1 │ (integer,double precision) │
│ FUNCTION │ 35999 │ public │ myfunc2 │ (integer,double precision) │
│ OPERATOR │ 36007 │ public │ ** │ (integer,integer) │
│ RELATION │ 36005 │ public │ myview │ │
│ RELATION │ 36002 │ public │ mytable │ │
└──────────┴───────┴────────┴─────────┴────────────────────────────┘
(4 rows)
PLPGSQL_SHOW_DEPENDENCY_TB的可选参数是relid , anyelementtype , enumtype , anyrangetype , anycompatibletype和anycompatiblerangetype 。
PLPGSQL_CHECK包含PLPGSQL函数和过程的简单剖面。它可以使用/不访问共享内存。这取决于shared_preload_libraries config。当PLPGSQL_CHECK通过shared_preload_libraries初始化时,它可以分配共享内存,并且函数的配置文件存储在那里。当PLPGSQL_CHECK无法分配共享内存时,该配置文件存储在会话内存中。
由于依赖关系, shared_preload_libraries应该首先包含plpgsql
postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
│ shared_preload_libraries │
╞══════════════════════════╡
│ plpgsql,plpgsql_check │
└──────────────────────────┘
(1 row)
GUC plpgsql_check.profiler启用时,Profiler处于活动状态。 Profiler不需要共享内存,但是如果没有足够的共享内存,则剖面仪仅限于活动会话。可以通过调用函数plpgsql_check_profiler(true)激活profiler,并通过使用false参数(或以字母on , off )调用相同的函数来激活。
在执行任何PLPGSQL函数之前,应初始化PLPGSQL_CHECK。只有早期初始化才能确保正确的作品和示踪剂的正确工作。当您不使用shared_preloaded_libraries时,可以改用命令load 'plpgsql_check' 。
当shared_preload_libraries初始化PLPGSQL_CHECK时,可以使用另一种GUC来配置Profiler使用的共享内存量: plpgsql_check.profiler_max_shared_chunks 。这定义了可以存储在共享内存中的最大语句块数量。对于每个PLPGSQL函数(或过程),整个内容分为30个语句的块。如果需要,可以使用多个块来存储单个功能的整体内容。一个块是1704字节。此GUC的默认值为15000,对于包含PLPGSQL中数十万个陈述的大型项目应该足够,并且将消耗约24MB的内存。如果您的项目不需要那么多块,则可以将此参数设置为较小的数字,以减少内存使用情况。最小值为50(应消耗约83kb的内存),最大值为100000(应消耗约163MB的内存)。更改此参数需要一个PostgreSQL重新启动。
剖面人员还将为包含表达式或优化语句的每个指令检索查询标识符。请注意,这需要安装PG_STAT_STATENTEMES,或其他类似的第三方扩展名)。查询标识符检索有一些局限性:
注意:共享配置文件的更新可以降低较高负载下的服务器的性能。
该配置文件可以通过函数plpgsql_profiler_function_tb显示:
postgres=# select lineno, avg_time, source from plpgsql_profiler_function_tb('fx(int)');
┌────────┬──────────┬───────────────────────────────────────────────────────────────────┐
│ lineno │ avg_time │ source │
╞════════╪══════════╪═══════════════════════════════════════════════════════════════════╡
│ 1 │ │ │
│ 2 │ │ declare result int = 0; │
│ 3 │ 0.075 │ begin │
│ 4 │ 0.202 │ for i in 1..$1 loop │
│ 5 │ 0.005 │ select result + i into result; select result + i into result; │
│ 6 │ │ end loop; │
│ 7 │ 0 │ return result; │
│ 8 │ │ end; │
└────────┴──────────┴───────────────────────────────────────────────────────────────────┘
(9 rows)
结果的时代是在米利环境中。
每个语句的配置文件(不是每行)可以通过函数plpgsql_profiler_function_statements_tb显示。
CREATE OR REPLACE FUNCTION public.fx1(a integer)
RETURNS integer
LANGUAGE plpgsql
1 AS $function$
2 begin
3 if a > 10 then
4 raise notice 'ahoj';
5 return -1;
6 else
7 raise notice 'nazdar';
8 return 1;
9 end if;
10 end;
11 $function$
postgres=# select stmtid, parent_stmtid, parent_note, lineno, exec_stmts, stmtname
from plpgsql_profiler_function_statements_tb('fx1');
┌────────┬───────────────┬─────────────┬────────┬────────────┬─────────────────┐
│ stmtid │ parent_stmtid │ parent_note │ lineno │ exec_stmts │ stmtname │
╞════════╪═══════════════╪═════════════╪════════╪════════════╪═════════════════╡
│ 0 │ ∅ │ ∅ │ 2 │ 0 │ statement block │
│ 1 │ 0 │ body │ 3 │ 0 │ IF │
│ 2 │ 1 │ then body │ 4 │ 0 │ RAISE │
│ 3 │ 1 │ then body │ 5 │ 0 │ RETURN │
│ 4 │ 1 │ else body │ 7 │ 0 │ RAISE │
│ 5 │ 1 │ else body │ 8 │ 0 │ RETURN │
└────────┴───────────────┴─────────────┴────────┴────────────┴─────────────────┘
(6 rows)
所有存储的配置文件都可以通过调用函数plpgsql_profiler_functions_all显示:
postgres=# select * from plpgsql_profiler_functions_all();
┌───────────────────────┬────────────┬────────────┬──────────┬─────────────┬──────────┬──────────┐
│ funcoid │ exec_count │ total_time │ avg_time │ stddev_time │ min_time │ max_time │
╞═══════════════════════╪════════════╪════════════╪══════════╪═════════════╪══════════╪══════════╡
│ fxx(double precision) │ 1 │ 0.01 │ 0.01 │ 0.00 │ 0.01 │ 0.01 │
└───────────────────────┴────────────┴────────────┴──────────┴─────────────┴──────────┴──────────┘
(1 row)
有两个用于清洁存储的配置文件的功能: plpgsql_profiler_reset_all()和plpgsql_profiler_reset(regprocedure) 。
PLPGSQL_CHECK提供了两个功能:
plpgsql_coverage_statements(name)plpgsql_coverage_branches(name)还有另一个非常好的PLPGSQL PROFILER -https://github.com/glynastill/plprofiler
我的扩展名设计为简单的使用和实用。或多或少。
PLPROFILER更为复杂。它构建呼叫图,从此图中可以创建执行时间的火焰图。
这两个扩展都可以与内置的PostgreSQL的功能 - 跟踪功能一起使用。
set track_functions to 'pl';
...
select * from pg_stat_user_functions;
PLPGSQL_CHECK提供了一个追踪可能性 - 在此模式下,您可以在开始或结束功能(TERSE和默认的杂语)和开始或结束语句(冗长的杂语)上看到通知。对于默认和冗长的冗长,显示了函数参数的内容。当冗长的详细性时,显示相关变量的内容。
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 ->> start of inline_code_block (Oid=0)
NOTICE: #2 ->> start of function fx(integer,integer,date,text) (Oid=16405)
NOTICE: #2 call by inline_code_block line 1 at PERFORM
NOTICE: #2 "a" => '10', "b" => null, "c" => '2020-08-03', "d" => 'stěhule'
NOTICE: #4 ->> start of function fx(integer) (Oid=16404)
NOTICE: #4 call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: #4 "a" => '10'
NOTICE: #4 <<- end of function fx (elapsed time=0.098 ms)
NOTICE: #2 <<- end of function fx (elapsed time=0.399 ms)
NOTICE: #0 <<- end of block (elapsed time=0.754 ms)
#之后的数字是执行帧计数器(此数字与错误上下文堆栈的深度有关)。它允许配对功能的启动和结束。
通过将plpgsql_check.tracer设置为on来启用跟踪。注意 - 使这种行为能对绩效产生重大负面影响(与剖面师不同)。您可以为示踪剂plpgsql_check.tracer_errlevel使用的输出设置一个级别(默认值为notice )。输出内容受plpgsql_check.tracer_variable_max_length配置变量指定的长度限制。可以通过调用函数plpgsql_check_tracer(true)激活示踪剂,并通过使用false参数(或以字母on , off )调用相同的函数来禁用。
首先,Superuser应通过将设置set plpgsql_check.enable_tracer to on;或plpgsql_check.enable_tracer to on in postgresql.conf 。这是安全保障。示踪剂显示了PLPGSQL变量的内容,然后可以将一些安全性敏感信息显示给无私的用户(当他运行安全定义器函数时)。其次,应加载扩展名plpgsql_check 。可以通过执行某些plpgsql_check函数或通过命令load 'plpgsql_check'; 。您可以使用Configuration的选项shared_preload_libraries , local_preload_libraries或session_preload_libraries 。
在简短的详细模式下,输出减少了:
postgres=# set plpgsql_check.tracer_verbosity TO terse;
SET
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 start of inline code block (oid=0)
NOTICE: #2 start of fx (oid=16405)
NOTICE: #4 start of fx (oid=16404)
NOTICE: #4 end of fx
NOTICE: #2 end of fx
NOTICE: #0 end of inline code block
在详细模式下,输出扩展了有关语句详细信息:
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 ->> start of block inline_code_block (oid=0)
NOTICE: #0.1 1 --> start of PERFORM
NOTICE: #2 ->> start of function fx(integer,integer,date,text) (oid=16405)
NOTICE: #2 call by inline_code_block line 1 at PERFORM
NOTICE: #2 "a" => '10', "b" => null, "c" => '2020-08-04', "d" => 'stěhule'
NOTICE: #2.1 1 --> start of PERFORM
NOTICE: #2.1 "a" => '10'
NOTICE: #4 ->> start of function fx(integer) (oid=16404)
NOTICE: #4 call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: #4 "a" => '10'
NOTICE: #4.1 6 --> start of assignment
NOTICE: #4.1 "a" => '10', "b" => '20'
NOTICE: #4.1 <-- end of assignment (elapsed time=0.076 ms)
NOTICE: #4.1 "res" => '130'
NOTICE: #4.2 7 --> start of RETURN
NOTICE: #4.2 "res" => '130'
NOTICE: #4.2 <-- end of RETURN (elapsed time=0.054 ms)
NOTICE: #4 <<- end of function fx (elapsed time=0.373 ms)
NOTICE: #2.1 <-- end of PERFORM (elapsed time=0.589 ms)
NOTICE: #2 <<- end of function fx (elapsed time=0.727 ms)
NOTICE: #0.1 <-- end of PERFORM (elapsed time=1.147 ms)
NOTICE: #0 <<- end of block (elapsed time=1.286 ms)
示踪剂的一个特殊功能是当plpgsql_check.trace_assert on时,示踪ASSERT 。当plpgsql_check.trace_assert_verbosity DEFAULT时,当Assert表达式为FALSE时,将显示所有函数或过程的变量。当此配置为VERBOSE时,将显示所有PLPGSQL帧的所有变量。此行为独立于plpgsql.check_asserts值。尽管在PLPGSQL运行时禁用了断言,但可以使用它。
postgres=# set plpgsql_check.tracer to off;
postgres=# set plpgsql_check.trace_assert_verbosity TO verbose;
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE: "a" => '10', "res" => null, "b" => '20'
NOTICE: #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE: #0 PL/pgSQL function inline_code_block line 1 at PERFORM
ERROR: assertion failed
CONTEXT: PL/pgSQL function fx(integer) line 12 at ASSERT
SQL statement "SELECT fx(a)"
PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
SQL statement "SELECT fx(10,null, 'now', e'stěhule')"
PL/pgSQL function inline_code_block line 1 at PERFORM
postgres=# set plpgsql.check_asserts to off;
SET
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #4 PLpgSQL assert expression (false) on line 12 of fx(integer) is false
NOTICE: "a" => '10', "res" => null, "b" => '20'
NOTICE: #2 PL/pgSQL function fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: "a" => '10', "b" => null, "c" => '2020-08-05', "d" => 'stěhule'
NOTICE: #0 PL/pgSQL function inline_code_block line 1 at PERFORM
DO
示踪剂可以显示亚transaction缓冲液ID( nxids )的用法。显示的tnl编号是事务嵌套级数(对于PLPGSQL,它取决于具有异常处理程序的块的深处)。
PLPGSQL的光标只是SQL光标的名称。 SQL光标的生命周期与相关PLPGSQL的光标变量的范围没有连接。 SQL光标在交易端被自我披露,但是对于长时间的交易和太多的打开光标,可能为时已晚。当不需要光标时,最好明确关闭光标(通过密闭语句)。没有它,可能会有重大的内存问题。
当Open语句尝试使用尚未关闭的光标时,警告会提出。可以通过将plpgsql_check.cursors_leaks to off禁用此功能。当例程被重新称为例程时,此检查不活动
完成功能后,可以立即检查未封闭的光标。默认情况plpgsql_check.strict_cursors_leaks to on启用。
任何未封闭的光标都有一次。
如果将plugin_debugger (PLPGSQL调试器)与plpgsql_check一起使用,则应在plugin_debugger之后初始化plpgsql_check (因为plugin_debugger不支持PL/PGSQL的DEBUG API)。例如( postgresql.conf ):
shared_preload_libraries = 'plugin_debugger,plpgsql,plpgsql_check'
示踪剂打印变量或函数参数的内容。对于安全定义器函数,此内容可以容纳安全敏感数据。这就是默认情况下禁用示踪剂的原因,仅应使用超级用户权利plpgsql_check.enable_tracer启用示踪剂。
您可以在检查功能的“ pragma”功能中配置PLPGSQL_CHECK行为。这是PL/SQL或Pragma功能的ADA语言的类比。 PLPGSQL不支持PRAGMA,但是PLPGSQL_CHECK检测名为plpgsql_check_pragma的功能,并从该函数的参数中获取选项。这些PLPGSQL_CHECK选项对这组语句的末尾有效。
CREATE OR REPLACE FUNCTION test()
RETURNS void AS $$
BEGIN
...
-- for following statements disable check
PERFORM plpgsql_check_pragma('disable:check');
...
-- enable check again
PERFORM plpgsql_check_pragma('enable:check');
...
END;
$$ LANGUAGE plpgsql;
功能plpgsql_check_pragma是返回一个的不变函数。它由plpgsql_check扩展定义。您可以声明替代plpgsql_check_pragma功能如下:
CREATE OR REPLACE FUNCTION plpgsql_check_pragma(VARIADIC args[])
RETURNS int AS $$
SELECT 1
$$ LANGUAGE sql IMMUTABLE;
在声明中使用Pragma函数在函数级别上也在顶部块集的部分选项。
CREATE OR REPLACE FUNCTION test()
RETURNS void AS $$
DECLARE
aux int := plpgsql_check_pragma('disable:extra_warnings');
...
也支持Pragma的较短语法:
CREATE OR REPLACE FUNCTION test()
RETURNS void AS $$
DECLARE r record;
BEGIN
PERFORM 'PRAGMA:TYPE:r (a int, b int)';
PERFORM 'PRAGMA:TABLE: x (like pg_class)';
...
echo:str打印字符串(用于测试)。在字符串中,可以使用“变量”:@@ id,@@ name,@@ signature
status:check , status:tracer , status:other_warnings , status:performance_warnings , status:extra_warnings ,状态, status:security_warnings此输出当前值(例如启用了启用其他_WARNINGS)
enable:check , enable:tracer , enable:other_warnings , enable:performance_warnings , enable:extra_warnings ,enable, enable:security_warnings
disable:check , disable:tracer , disable:other_warnings , disable:performance_warnings , disable:extra_warnings , disable:security_warnings :可用于从任何elelement函数返回以返回提示。只需在返回声明之前将布拉格马放在返回声明之前。
type:varname typename或type:varname (fieldname type, ...) - 将类型设置为记录类型的变量
table: name (column_name type, ...)或table: name (like tablename) - 创建临时临时表(如果要指定模式,则只允许pg_temp架构。
sequence: name - 创建短暂的临时序列
assert-schema: varname检查时间断言 - 确保由变量指定的模式有效
assert-table: [ varname_schema, ] , varname确保由变量指定的表名(通过恒定跟踪)有效
assert-column: [varname_schema, ], varname_table , varname确保列由变量启用是有效的
Pragmas enable:tracer和disable:tracer在12岁及以上的Postgres处活跃
PLPGSQL_CHECK不支持(PLPGSQL_CHECK的更新)。您应该在安装此扩展程序的新版本之前将其删除。
您需要一个开发环境才能进行后Ql扩展:
make clean
make install
结果:
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 clean
rm -f plpgsql_check.so libplpgsql_check.a libplpgsql_check.pc
rm -f plpgsql_check.o
rm -rf results/ regression.diffs regression.out tmp_check/ log/
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 all
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -I. -I./ -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE -c -o plpgsql_check.o plpgsql_check.c
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -shared -o plpgsql_check.so plpgsql_check.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags
[pavel@localhost plpgsql_check]$ su root
Password: *******
[root@localhost plpgsql_check]# make USE_PGXS=1 install
/usr/bin/mkdir -p '/usr/local/pgsql/lib'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755 plpgsql_check.so '/usr/local/pgsql/lib/plpgsql_check.so'
/usr/bin/install -c -m 644 plpgsql_check.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644 plpgsql_check--0.9.sql '/usr/local/pgsql/share/extension/'
[root@localhost plpgsql_check]# exit
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 installcheck
/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/test/regress/pg_regress --inputdir=./ --psqldir='/usr/local/pgsql/bin' --dbname=pl_regression --load-language=plpgsql --dbname=contrib_regression plpgsql_check_passive plpgsql_check_active plpgsql_check_active-9.5
(using postmaster on Unix socket, default port)
============== dropping database "contrib_regression" ==============
DROP DATABASE
============== creating database "contrib_regression" ==============
CREATE DATABASE
ALTER DATABASE
============== installing plpgsql ==============
CREATE LANGUAGE
============== running regression test queries ==============
test plpgsql_check_passive ... ok
test plpgsql_check_active ... ok
test plpgsql_check_active-9.5 ... ok
=====================
All 3 tests passed.
=====================
有时,成功的汇编可能需要Libicu -Dev包(PostgreSQL 10及更高 - 当PG汇编并获得ICU支持时)
sudo apt install libicu-dev
您可以查看预编译的dll库http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-is-aveck-is-avableable-for-microsoft.html,http://okbob.bob..blog.bob.spot.com/2023/2023/1023/1023/1023/1023/compiled-dlll-opppgsqsqsql.----54
或通过自我编译:
plpgsql_check.dll到PostgreSQL14libplpgsql_check.control和plpgsql_check--2.1.sql至PostgreSQL14shareextension meson setup buildcd buildninjasudo ninja install针对PostgreSQL 10的汇编需要Libicu!
版权(C)Pavel Stehule([email protected])
特此免费授予获得此软件副本和相关文档文件副本(“软件”)的任何人,以无限制处理该软件,包括无限制的使用权,复制,复制,修改,合并,合并,发布,分发,分发,分发,订婚,和/或允许软件的副本,并允许对以下条件提供以下条件,以下是以下条件。
上述版权通知和此许可通知应包含在软件的所有副本或大量部分中。
该软件是“原样”提供的,没有任何形式的明示或暗示保证,包括但不限于适销性,特定目的的适用性和非侵权的保证。在任何情况下,作者或版权持有人都不应对任何索赔,损害赔偿或其他责任责任,无论是在合同,侵权的诉讼中还是其他责任,是由软件,使用或与软件中的使用或其他交易有关的。
如果您喜欢,请发送明信片以地址
Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
我邀请任何问题,评论,错误报告,邮件地址上的补丁[email protected]