此擴展是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: 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]