이 확장자는 PostgreSQL 용 PLPGSQL을위한 전체 라이터입니다. 내부 PostgreSQL 파서/평가자 만 활용하므로 런타임에 오류가 정확히 발생합니다. 또한 루틴 내부의 SQL을 구문 분석하고 "프로 시저/기능 작성"명령 중에는 일반적으로 찾을 수없는 오류를 찾습니다. 많은 경고와 힌트의 수준을 제어 할 수 있습니다. 마지막으로, Pragama 유형 마커를 추가하여 이미 알고있는 메시지를 숨기거나 나중에 더 깊은 청소를 위해 돌아 오도록 상기시켜 줄 수 있습니다.
나는 PostgreSQL 업스트림에 대한 강화 된 확인을 작성하려고 할 때 2 년 동안 작성한 코드를 게시하고 싶었 기 때문에이 프로젝트를 설립했습니다. 완전히 성공하지 못했습니다. 업스트림에 통합하려면 더 큰 PLPGSQL 리팩토링이 필요합니다. 그러나 코드는 완전히 작동하며 생산에 사용될 수 있으며 생산에 사용됩니다. 그래서 저는 모든 PLPGSQL 개발자가 사용할 수 있도록이 확장을 만들었습니다.
이 확장의 추가 개발을 돕기 위해 그룹에 가입하려면 PostgreSQL Extension Hacking Google Group에 자신을 등록하십시오.
나는 아이디어, 패치, 버그보고를 초대합니다.
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 또는 Complete Function의 서명을 알고 있으면 'fx()'::regprocedure 또는 16799::regprocedure 와 같은 복구 유형 매개 변수를 사용할 수 있습니다. 가능한 대안은 기능의 이름이 'fx' 와 같이 독특한 경우 이름 만 사용하는 것입니다. 이름이 고유하지 않거나 함수가 존재하지 않으면 오류가 발생합니다. relid DEFAULT 0 트리거 함수와 할당 된 관계의 OID. 트리거 기능을 확인해야합니다. 트리거가 작동한다는 점에서 테이블을 보내고 있습니다.
fatal_errors boolean DEFAULT true 첫 번째 오류 중지 (대규모 오류 보고서를 방지)
other_warnings boolean DEFAULT true - 왼쪽과 오른쪽의 과제에서 다른 속성 번호와 같은 경고 표시 기능의 매개 변수, 사용되지 않는 변수, 원치 않는 캐스팅 등을 중첩시킵니다.
extra_warnings boolean DEFAULT true - 누락 된 RETURN , 그림자 변수, 죽은 코드, 읽지 않은 (사용하지 않은) 함수의 매개 변수, 수정되지 않은 변수, 수정 된 자동 변수 등과 같은 경고 표시
performance_warnings boolean DEFAULT false - 유형 수정 자, 캐스팅, 위치의 암시 적 캐스트 (인덱스가 사용되지 않는 이유가 될 수 있음) 등의 암시 적 캐스트 등의 성과 관련 경고 등
security_warnings boolean DEFAULT false - SQL 주입 취약성 감지와 같은 보안 관련 점검
compatibility_warnings boolean DEFAULT false - 냉담한 명시 적 명시 적 명시 적으로 내부 커서 이름 또는 커서 변수의 내부 커서 이름과 같은 호환성 관련 점검.
anyelementtype regtype DEFAULT 'int' - Ade Element 유형을 테스트 할 때 사용할 실제 유형
anyenumtype regtype DEFAULT '-' - AnyEnum 유형을 테스트 할 때 사용할 실제 유형
anyrangetype regtype DEFAULT 'int4range' - ANDERANGE 유형을 테스트 할 때 사용할 실제 유형
anycompatibletype DEFAULT 'int' - 대응 가능한 유형을 테스트 할 때 사용할 실제 유형
anycompatiblerangetype DEFAULT 'int4range' - 대응 가능 범위 유형을 테스트 할 때 사용할 실제 범위 유형
without_warnings DEFAULT false - 모든 경고를 비활성화합니다 (모든 xxxx_warning 매개 변수를 무시하고 빠른 재정의)
all_warnings DEFAULT false - 모든 경고를 활성화합니다 (다른 xxx_warning 매개 변수를 무시하고 빠른 긍정적)
newtable DEFAULT NULL , oldtable DEFAULT NULL 새 또는 오래된 전환 테이블의 이름. 전환 테이블이 트리거 함수에서 사용될 때 이러한 매개 변수가 필요합니다.
use_incomment_options DEFAULT true true 일 때, 인간 옵션이 활성화됩니다.
incomment_options_usage_warning DEFAULT 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_function을 호출하여 활성 모드에서만 수행됩니다. 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 Cursor와 Refcursor의 변수는 관련 포털의 고유 한 이름 (Cursor의 구현에 사용되는 Postgres의 내부 구조)을 갖는 향상된 문자열 변수입니다. PostgreSQL 16까지, 포털은 Cursor 변수 이름과 동일한 이름을 가졌습니다. Postgresql 16 이상의 변경 사항이 메커니즘과 기본적으로 관련 포털은 고유 한 이름으로 명명됩니다. 중첩 블록에서 커서의 일부 문제를 해결하거나 커서가 재귀로 사용되는 기능을 사용하는 경우를 해결합니다.
언급 된 변경 사항에 따라 Refcursor의 변수는 다른 Refcursor 변수 또는 일부 커서 변수 (커서가 열릴 때)에서 값을 가져와야합니다.
-- 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 플래그가 활성화되면 plpgsql_check REFCURSOR의 변수에 대한 수정 할당 또는 refcursor의 값 반환을 식별하려고합니다.
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는 실제로 정적 코드에서 거의 모든 오류를 찾아야합니다. 개발자가 Dynamic SQL 또는 Record Data Type과 같은 PLPGSQL의 동적 기능을 사용하면 잘못된 양성이 가능합니다. 이들은 잘 쓰여진 코드에서는 드물어야합니다. 그런 다음 영향을받는 함수를 재 설계하거나 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의 사용은 작은 오버 헤드 (수동 모드가 활성화 된 경우)를 추가하고 개발 또는 사전 생산 환경에서만 해당 설정을 사용해야합니다.
이 모듈은 런타임에 조립 된 쿼리를 확인하지 않습니다. Dynamic Queries의 결과를 식별 할 수는 없습니다. 따라서 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에 대해 동일합니다. Refcursor 유형을 사용하거나 이러한 함수에 대해 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 켜져있을 때 활성화됩니다. 프로파일 러는 공유 메모리가 필요하지 않지만 공유 메모리가 충분하지 않은 경우 프로파일 러는 활성 세션으로 제한됩니다. 프로파일 러는 함수 plpgsql_check_profiler(true) 호출하여 활성화 될 수 있고 false 인수 (또는 off on 와 동일한 함수를 호출하여 비활성화 할 수 있습니다.
PLPGSQL_CHECK는 PLPGSQL 기능이 실행되기 전에 초기화되어야합니다. 초기 초기화만이 프로파일 러 및 트레이서의 올바른 작업을 보장합니다. shared_preloaded_libraries 사용하지 않으면 대신 명령 load 'plpgsql_check' 사용할 수 있습니다.
plpgsql_check가 shared_preload_libraries 에 의해 초기화되면, 다른 GUC는 profiler에서 사용하는 공유 메모리의 양을 구성 할 수 있습니다 : plpgsql_check.profiler_max_shared_chunks . 이것은 공유 메모리에 저장할 수있는 최대 명령문 청크 수를 정의합니다. 각각의 PLPGSQL 함수 (또는 절차)에 대해 전체 내용은 30 문의 청크로 분할됩니다. 필요한 경우 여러 청크를 사용하여 단일 함수의 전체 내용을 저장할 수 있습니다. 단일 청크는 1704 바이트입니다. 이 GUC의 기본값은 15000이며, 이는 PLPGSQL에 수십만 개의 명세서를 포함하는 대형 프로젝트에 충분하며 약 24MB의 메모리를 소비 할 것입니다. 프로젝트에 해당 된 수의 청크가 필요하지 않은 경우 메모리 사용량을 줄이기 위해이 매개 변수를 더 작은 숫자로 설정할 수 있습니다. 최소값은 50 (약 83kb의 메모리를 소비해야 함)이고 최대 값은 10000 (약 163MB의 메모리를 소비해야 함)입니다. 이 매개 변수를 변경하려면 PostgreSQL 재시작이 필요합니다.
프로파일 러는 또한 표현식 또는 최적화 가능한 문을 포함하는 각 명령어의 쿼리 식별자를 검색합니다. 이를 위해서는 PG_STAT_STATEMENTS 또는 다른 유사한 타사 확장)을 설치해야합니다. 쿼리 식별자 검색에는 몇 가지 제한 사항이 있습니다.
주의 : 공유 프로파일의 업데이트는 더 높은로드 하에서 서버의 성능을 줄일 수 있습니다.
프로파일은 함수 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 및 DEFAULT VORBISIT) 및 시작 또는 끝 문 (Verbose Verbosity)에 대한 통지를 볼 수 있습니다. 기본 및 장점의 표현의 경우 기능 인수의 내용이 표시됩니다. 관련 변수의 내용은 구두가 장황 할 때 표시됩니다.
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 으로 설정하여 추적이 가능합니다. 주의 -이 동작을 가능하게하는 것은 프로파일 러와 달리 성능에 중대한 부정적인 영향을 미칩니다. Tracer plpgsql_check.tracer_errlevel 에서 사용하는 출력 레벨을 설정할 수 있습니다 (기본값은 notice ). 출력 내용은 plpgsql_check.tracer_variable_max_length configuration 변수로 지정된 길이로 제한됩니다. 트레이서는 함수 plpgsql_check_tracer(true) 호출하여 활성화 될 수 있으며 false 인수 (또는 off on 와 동일한 함수를 호출하여 비활성화 할 수 있습니다.
먼저, 추적자의 사용은 set plpgsql_check.enable_tracer to on; 또는 postgresql.conf 에서 plpgsql_check.enable_tracer to on . 이것은 보안 보호 장치입니다. 트레이서는 PLPGSQL 변수의 내용을 보여주고, 일부 보안 민감한 정보를 보안 정의 기능을 실행할 때 소외되지 않은 사용자에게 표시 될 수 있습니다. 둘째, 확장자 plpgsql_check 에로드해야합니다. 일부 plpgsql_check 함수를 실행하여 수행하거나 명시 적으로 load 'plpgsql_check'; . configuration의 옵션 shared_preload_libraries , local_preload_libraries 또는 session_preload_libraries 사용할 수 있습니다.
Terse Verbose 모드에서는 출력이 줄어 듭니다.
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)
Tracer의 특별한 특징은 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
트레이서는 서브 트랜잭션 버퍼 ID ( nxids )의 사용을 표시 할 수 있습니다. 표시된 tnl 번호는 트랜잭션 중첩 레벨 번호입니다 (PLPGSQL의 경우 예외의 핸들러가있는 블록의 깊은에 따라 다름).
PLPGSQL의 커서는 SQL 커서의 이름 일뿐입니다. SQL 커서의 수명주기는 관련 PLPGSQL의 커서 변수의 범위와 연결되지 않습니다. SQL 커서는 트랜잭션 종료시 자체적으로 클로로를 사용하지만 긴 트랜잭션과 너무 많은 커서의 경우 너무 늦을 수 있습니다. 커서가 필요하지 않을 때 Cursor를 명시 적으로 닫는 것이 좋습니다 (Close 진술서). 그것 없이는 중요한 메모리 문제가 가능합니다.
Open 문이 아직 닫히지 않은 커서를 사용하려고하면 경고가 제기됩니다. 이 기능은 plpgsql_check.cursors_leaks to off 설정하여 비활성화 할 수 있습니다. 이 점검은 활성화되지 않습니다.
함수가 완료되면 구제되지 않은 커서를 즉시 점검 할 수 있습니다. 이 점검은 기본적으로 비활성화되며 plpgsql_check.strict_cursors_leaks to on 의해 활성화되어야합니다.
부착되지 않은 커서는 한 번보고됩니다.
plpgsql_check 과 함께 plugin_debugger (plpgsql debugger)를 사용하는 경우 plpgsql_check plugin_debugger 후에 초기화해야합니다 ( plugin_debugger pl/pgsql의 디버그 API 공유를 지원하지 않기 때문에). 예를 들어 ( postgresql.conf ) :
shared_preload_libraries = 'plugin_debugger,plpgsql,plpgsql_check'
트레이서는 변수 또는 기능 인수의 내용을 인쇄합니다. 보안 정의기 기능의 경우이 컨텐츠는 보안 민감한 데이터를 보유 할 수 있습니다. 이 이유는 추적기가 기본적으로 비활성화되어 수퍼 사용자 권한 plpgsql_check.enable_tracer 에서만 활성화되어야합니다.
"Pragma"기능을 사용하여 확인 된 기능 내에서 PLPGSQL_CHECK 동작을 구성 할 수 있습니다. 이것은 Pragma 기능의 PL/SQL 또는 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 EXTENSION에 의해 정의됩니다. 대체 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:security_warnings
disable:check , disable:tracer , disable:other_warnings , disable:performance_warnings , disable:extra_warnings , disable:security_warnings 이를 사용하여 모든 요소 기능에서 반환 할 때 힌트를 비활성화 할 수 있습니다. 리턴 명세서에 프라그마를 넣으십시오.
type:varname typename 또는 type:varname (fieldname type, ...) - 레코드 유형의 변수로 설정
table: name (column_name type, ...) 또는 table: name (like tablename) - ephemeral temporary 테이블을 만듭니다 (스키마를 지정하려면 pg_temp 스키마 만 허용됩니다.
sequence: name - 임시 임시 시퀀스를 만듭니다
assert-schema: varname 체크 타임 주장 - 변수로 지정된 스키마가 유효한지 확인하십시오.
assert-table: [ varname_schema, ] , varname 변수별로 지정된 테이블 이름 (상수 추적)이 유효합니다.
assert-column: [varname_schema, ], varname_table , varname 변수에 의해 스파이 된 열이 유효한지 확인
Pragmas enable:tracer 및 disable:tracer Postgres 12 이상에 활성화됩니다.
PLPGSQL_CHECK (PLPGSQL_CHECK의 업데이트)를 지원하지 않습니다. 이 확장의 새 버전을 설치하기 전에 이것을 삭제해야합니다.
PostgreSQL 확장을위한 개발 환경이 필요합니다.
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/plpsqlcheck-is-available-for-microsoft.html, http://okbob.blogspot.com/2023/10-plpgpsqlcheck-254-254-254-254-254-254-254-254-254-254-1ppgps
또는 자기로 컴파일 :
plpgsql_check.dll PostgreSQL14lib 에 복사하십시오plpgsql_check.control 및 plpgsql_check--2.1.sql 을 PostgreSQL14shareextension 복사하십시오 meson setup buildcd buildninjasudo ninja installPostgresql 10에 대한 편집에는 libicu가 필요합니다!
저작권 (c) Pavel Stehule ([email protected])
이에 따라이 소프트웨어 및 관련 문서 파일 ( "소프트웨어")의 사본을 얻는 사람에게는 허가가 부여됩니다. 소프트웨어의 사용, 복사, 수정, 합병, 배포, 배포, 숭고 및/또는 소프트웨어의 사본을 판매 할 권한을 포함하여 제한없이 소프트웨어를 처리 할 수 있도록 소프트웨어를 제공 할 권한이 없습니다.
위의 저작권 통지 및이 권한 통지는 소프트웨어의 모든 사본 또는 실질적인 부분에 포함되어야합니다.
이 소프트웨어는 상업성, 특정 목적에 대한 적합성 및 비 침해에 대한 보증을 포함하여 명시 적 또는 묵시적 보증없이 "그대로"제공됩니다. 어떠한 경우에도 저자 또는 저작권 보유자는 계약, 불법 행위 또는 기타, 소프트웨어 또는 소프트웨어의 사용 또는 기타 거래에서 발생하는 계약, 불법 행위 또는 기타의 행동에 관계없이 청구, 손해 또는 기타 책임에 대해 책임을지지 않습니다.
당신이 그것을 좋아한다면, 엽서를 보내주십시오
Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
메일 주소 [email protected]에 질문, 의견, 버그 보고서, 패치를 초대합니다.