Ekstensi ini adalah linter penuh untuk PLPGSQL untuk PostgreSQL. Ini hanya memanfaatkan Parser/Evaluator PostgreSQL Internal sehingga Anda melihat dengan tepat kesalahan akan terjadi saat runtime. Selain itu, ini mem -parsing SQL di dalam rutinitas Anda dan menemukan kesalahan yang biasanya tidak ditemukan selama perintah "Buat Prosedur/Fungsi". Anda dapat mengontrol tingkat banyak peringatan dan petunjuk. Akhirnya, Anda dapat menambahkan penanda jenis pragama untuk mematikan/pada banyak aspek yang memungkinkan Anda menyembunyikan pesan yang sudah Anda ketahui, atau untuk mengingatkan Anda untuk kembali untuk pembersihan yang lebih dalam nanti.
Saya mendirikan proyek ini, karena saya ingin mempublikasikan kode yang saya tulis selama dua tahun, ketika saya mencoba menulis pemeriksaan yang lebih baik untuk postgresql hulu. Itu tidak sepenuhnya berhasil - integrasi ke hulu membutuhkan beberapa refactoring PLPGSQL yang lebih besar. Tetapi kode tersebut berfungsi penuh dan dapat digunakan dalam produksi (dan digunakan dalam produksi). Jadi, saya membuat ekstensi ini tersedia untuk semua pengembang PLPGSQL.
Jika jika Anda ingin bergabung dengan grup kami untuk membantu pengembangan lebih lanjut dari ekstensi ini, daftarkan diri Anda di PostgreSQL Extension Hacking Google Group.
Saya mengundang ide, tambalan, laporan bug.
PostgreSQL PostgreSQL 12 - 17 didukung.
Pernyataan SQL di dalam fungsi PL/PGSQL diperiksa oleh validator untuk kesalahan semantik. Kesalahan ini dapat ditemukan dengan memanggil 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$
Fungsi plpgsql_check_function () memiliki tiga kemungkinan format output: Teks, JSON atau 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)
Anda dapat mengatur level peringatan melalui parameter fungsi:
funcoid oid - Nama fungsi atau tanda tangan fungsi - Fungsi memerlukan spesifikasi fungsi. Fungsi apa pun di PostgreSQL dapat ditentukan oleh OID atau dengan nama atau dengan tanda tangan. Ketika Anda mengetahui tanda tangan fungsi OID atau lengkap, Anda dapat menggunakan parameter tipe RegProcedure seperti 'fx()'::regprocedure atau 16799::regprocedure . Alternatif yang mungkin adalah menggunakan nama saja, ketika nama fungsi unik - seperti 'fx' . Ketika namanya tidak unik atau fungsinya tidak ada, ia menimbulkan kesalahan. relid DEFAULT 0 - OID Relasi Ditugaskan Dengan Fungsi Pemicu. Perlu untuk memeriksa fungsi pemicu. Anda mengirim tabel di mana pemicu beroperasi.
fatal_errors boolean DEFAULT true - hentikan kesalahan pertama (mencegah laporan kesalahan besar)
other_warnings boolean DEFAULT true - Tampilkan peringatan seperti nomor atribut yang berbeda di AssignMeNet di sisi kiri dan kanan, parameter fungsi tumpang tindih variabel, variabel yang tidak digunakan, casting yang tidak diinginkan, dll.
extra_warnings boolean DEFAULT true - Tampilkan peringatan seperti yang RETURN , variabel bayangan, kode mati, tidak pernah membaca (tidak digunakan) parameter fungsi, variabel yang tidak dimodifikasi, variabel otomatis yang dimodifikasi, dll.
performance_warnings boolean DEFAULT false - Peringatan terkait kinerja seperti tipe yang dinyatakan dengan pengubah tipe, casting, gips implisit di mana klausa (dapat menjadi alasan mengapa indeks tidak digunakan), dll.
security_warnings boolean DEFAULT false - Pemeriksaan Terkait Keamanan Seperti Deteksi Kerentanan Injeksi SQL
compatibility_warnings boolean DEFAULT false - Cek terkait kompatibilitas seperti Obsolete Explicit mengatur nama kursor internal dalam variabel refcursor atau kursor.
anyelementtype regtype DEFAULT 'int' - Jenis aktual yang akan digunakan saat menguji jenis apa pun
anyenumtype regtype DEFAULT '-' - Jenis aktual yang akan digunakan saat menguji jenis anyenum
anyrangetype regtype DEFAULT 'int4range' - tipe aktual yang akan digunakan saat menguji jenis anyrange
anycompatibletype DEFAULT 'int' - tipe aktual yang akan digunakan saat menguji jenis apa pun
anycompatiblerangetype DEFAULT 'int4range' - jenis rentang aktual yang akan digunakan saat menguji jenis rentang apa pun
without_warnings DEFAULT false - Nonaktifkan semua peringatan (mengabaikan semua parameter xxxx_warning, override cepat)
all_warnings DEFAULT false - aktifkan semua peringatan (mengabaikan parameter xxx_warning lainnya, positif cepat)
newtable DEFAULT NULL , oldtable DEFAULT NULL - nama tabel transisi baru atau lama. Parameter ini diperlukan ketika tabel transisi digunakan dalam fungsi pemicu.
use_incomment_options DEFAULT true - ketika itu benar, maka opsi dalam -datang aktif
incomment_options_usage_warning DEFAULT false - Ketika itu benar, maka peringatan tersebut dinaikkan ketika opsi dalam -komentum digunakan.
constant_tracing boolean DEFAULT true - Ketika itu benar, maka variabel yang memiliki beberapa konten konstan, dapat digunakan seperti konstan (hanya berfungsi dalam beberapa kasus sederhana, dan konten variabel tidak boleh ambigonu).
Saat Anda ingin memeriksa pemicu apa pun, Anda harus memasukkan relasi yang akan digunakan bersama dengan fungsi pemicu
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$
Spesifikasi hubungan yang hilang
postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR: missing trigger relation
HINT: Trigger relation oid must be valid
Pemeriksaan pemicu yang benar (dengan relasi yang ditentukan)
postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
Untuk pemicu dengan tabel transitif, Anda dapat mengatur parameter oldtable dan 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 memungkinkan pengaturan yang terus -menerus ditulis dalam komentar. Opsi ini diambil dari kode sumber fungsi sebelum memeriksa. Sintaksnya adalah:
@plpgsql_check_option: optioname [=] value [, optname [=] value ...]
Pengaturan dari opsi komentar memiliki prioritas tinggi, tetapi umumnya dapat dinonaktifkan dengan opsi use_incomment_options ke false .
Contoh:
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;
Anda dapat menggunakan PLPGSQL_CHECK_FUNCTION untuk pemeriksaan massa fungsi/prosedur dan pemeriksaan massal pemicu. Tolong, uji pertanyaan berikut:
-- 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;
atau
-- 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';
atau
-- 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;
Fungsi dapat diperiksa setelah eksekusi - modul PLPGSQL_CHECK harus dimuat (melalui 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
Mode default adalah by_function , itu berarti bahwa pemeriksaan yang disempurnakan hanya dilakukan dalam mode aktif - dengan memanggil plpgsql_check_function . fresh_start berarti Cold Start (pertama fungsinya disebut).
Anda dapat mengaktifkan mode pasif dengan
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
Variabel kursor dan refcursor postgresql adalah variabel string yang ditingkatkan yang memiliki nama unik dari portal terkait (struktur internal postgres yang digunakan untuk implementasi kursor). Sampai PostgreSQL 16, portal memiliki nama yang sama seperti nama variabel kursor. PostgreSQL 16 dan perubahan yang lebih tinggi mekanisme ini dan secara default portal terkait akan dinamai oleh beberapa nama unik. Ini memecahkan beberapa masalah dengan kursor di blok bersarang atau ketika kursor digunakan dalam fungsi yang disebut rekursif.
Dengan perubahan yang disebutkan, variabel refcursor harus mengambil nilai dari variabel refcursor lain atau dari beberapa variabel kursor (ketika kursor dibuka).
-- 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;
...
Ketika bendera compatibility_warnings aktif, kemudian plpgsql_check Cobalah untuk mengidentifikasi beberapa penugasan mencurigakan ke variabel refcursor atau pengembalian nilai -nilai 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 harus menemukan hampir semua kesalahan pada kode yang sangat statis. Ketika pengembang menggunakan fitur dinamis PLPGSQL seperti SQL dinamis atau tipe data catatan, maka positif palsu dimungkinkan. Ini harus jarang - dalam kode yang ditulis dengan baik - dan kemudian fungsi yang terpengaruh harus dirancang ulang atau PLPGSQL_CHECK harus dinonaktifkan untuk fungsi ini.
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;
Penggunaan PLPGSQL_CHECK menambahkan overhead kecil (ketika mode pasif diaktifkan) dan Anda harus menggunakan pengaturan itu hanya dalam lingkungan pengembangan atau praproduksi.
Modul ini tidak memeriksa pertanyaan yang dirakit dalam runtime. Tidak mungkin untuk mengidentifikasi hasil kueri dinamis - jadi PLPGSQL_CHECK tidak dapat mengatur jenis yang benar untuk merekam variabel dan tidak dapat memeriksa SQL dan ekspresi dependen.
Ketika jenis variabel catatan tidak diketahui, Anda dapat menetapkannya secara eksplisit dengan type pragma:
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 tidak dapat digunakan untuk mendeteksi struktur kursor yang direferensikan. Referensi tentang kursor di PLPGSQL diimplementasikan sebagai nama kursor global. Dalam waktu cek, namanya tidak diketahui (tidak ada dalam semua kemungkinan), dan kursor global tidak ada. Ini adalah masalah yang signifikan untuk setiap analisis statis. PLPGSQL tidak dapat mengetahui cara mengatur jenis yang benar untuk variabel catatan dan tidak dapat memeriksa pernyataan dan ekspresi SQL yang bergantung. Solusi adalah sama untuk SQL dinamis. Jangan gunakan variabel rekaman sebagai target saat Anda menggunakan jenis refcursor atau nonaktifkan PLPGSQL_CHECK untuk fungsi -fungsi ini.
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
Dalam hal ini jenis rekaman tidak boleh digunakan (gunakan RowType yang dikenal sebagai gantinya):
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 tidak dapat memverifikasi kueri atas tabel sementara yang dibuat dalam runtime fungsi PLPGSQL. Untuk kasus penggunaan ini, perlu membuat tabel temp palsu atau menonaktifkan PLPGSQL_CHECK untuk fungsi ini.
Pada kenyataannya tabel suhu disimpan dalam skema sendiri (per pengguna) dengan prioritas yang lebih tinggi daripada tabel persisten. Jadi Anda dapat melakukannya (dengan trik berikut dengan aman):
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
Trik ini meniru tabel temp global sebagian dan memungkinkan validasi statis. Kemungkinan lain adalah menggunakan [Templat Pembungkus Data Asing] (https://github.com/okbob/template_fdw)
Anda dapat menggunakan table pragma dan membuat tabel fana:
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)');
...
Fungsi Plpgsql_show_dependency_tb akan menunjukkan semua fungsi, operator, dan hubungan yang digunakan di dalam fungsi olahan:
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)
Argumen opsional dari plpgsql_show_dependency_tb relid , anyelementtype , enumtype , anyrangetype , anycompatibletype dan anycompatiblerangetype .
PLPGSQL_CHECK berisi profiler sederhana fungsi dan prosedur PLPGSQL. Ini dapat bekerja dengan/tanpa akses ke memori bersama. Itu tergantung pada konfigurasi shared_preload_libraries . Ketika PLPGSQL_CHECK diinisialisasi oleh shared_preload_libraries , maka dapat mengalokasikan memori bersama, dan profil fungsi disimpan di sana. Ketika PLPGSQL_CHECK tidak dapat mengalokasikan memori bersama, profil disimpan dalam memori sesi.
Karena dependensi, shared_preload_libraries harus berisi plpgsql terlebih dahulu
postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
│ shared_preload_libraries │
╞══════════════════════════╡
│ plpgsql,plpgsql_check │
└──────────────────────────┘
(1 row)
Profiler aktif ketika GUC plpgsql_check.profiler aktif. Profiler tidak memerlukan memori bersama, tetapi jika tidak ada memori bersama yang cukup, maka profiler terbatas hanya untuk sesi aktif. Profiler dapat diaktifkan dengan memanggil fungsi plpgsql_check_profiler(true) dan dinonaktifkan dengan memanggil fungsi yang sama dengan argumen false (atau dengan literal on , off ).
PLPGSQL_CHECK harus diinisialisasi sebelum fungsi PLPGSQL dijalankan. Hanya inisialisasi awal memastikan pekerjaan yang benar dari profiler dan pelacak. Ketika Anda tidak menggunakan shared_preloaded_libraries , Anda dapat menggunakan command load 'plpgsql_check' sebagai gantinya.
Ketika plpgsql_check diinisialisasi oleh shared_preload_libraries , GUC lain tersedia untuk mengonfigurasi jumlah memori bersama yang digunakan oleh profiler: plpgsql_check.profiler_max_shared_chunks . Ini mendefinisikan jumlah maksimum potongan pernyataan yang dapat disimpan dalam memori bersama. Untuk setiap fungsi PLPGSQL (atau prosedur), seluruh konten dibagi menjadi potongan 30 pernyataan. Jika diperlukan, beberapa potongan dapat digunakan untuk menyimpan seluruh konten dari satu fungsi. Satu potongan adalah 1704 byte. Nilai default untuk GUC ini adalah 15000, yang seharusnya cukup untuk proyek -proyek besar yang berisi ratusan ribu pernyataan di PLPGSQL, dan akan mengkonsumsi sekitar 24MB memori. Jika proyek Anda tidak memerlukan jumlah potongan sebanyak itu, Anda dapat mengatur parameter ini ke angka yang lebih kecil untuk mengurangi penggunaan memori. Nilai minimum adalah 50 (yang harus mengkonsumsi sekitar 83kb memori), dan nilai maksimum adalah 100000 (yang harus mengkonsumsi sekitar 163MB memori). Mengubah parameter ini membutuhkan restart PostgreSQL.
Profiler juga akan mengambil pengidentifikasi kueri untuk setiap instruksi yang berisi ekspresi atau pernyataan yang dapat dioptimalkan. Perhatikan bahwa ini membutuhkan PG_STAT_STATEMENTS, atau ekstensi pihak ketiga yang serupa), untuk diinstal. Ada beberapa keterbatasan pengenal pengidentifikasi kueri:
Perhatian: Pembaruan profil bersama dapat mengurangi kinerja pada server di bawah beban yang lebih tinggi.
Profil dapat ditampilkan dengan fungsi 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)
Waktu dalam hasilnya adalah milidetik.
Profil per pernyataan (bukan per baris) dapat ditampilkan dengan fungsi 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)
Semua profil yang disimpan dapat ditampilkan dengan memanggil fungsi 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)
Ada dua fungsi untuk pembersihan profil tersimpan: plpgsql_profiler_reset_all() dan plpgsql_profiler_reset(regprocedure) .
PLPGSQL_CHECK menyediakan dua fungsi:
plpgsql_coverage_statements(name)plpgsql_coverage_branches(name)Ada profiler PLPGSQL lainnya yang sangat bagus - https://github.com/glynastill/plprofiler
Ekstensi saya dirancang agar sederhana untuk digunakan dan praktis. Tidak lebih atau kurang.
PlProfiler lebih kompleks. Ini membangun grafik panggilan dan dari grafik ini dapat membuat grafik nyala waktu eksekusi.
Kedua ekstensi dapat digunakan bersama dengan fitur Builtin PostgreSQL - fungsi pelacakan.
set track_functions to 'pl';
...
select * from pg_stat_user_functions;
PLPGSQL_CHECK Memberikan kemungkinan penelusuran - Dalam mode ini Anda dapat melihat pemberitahuan pada fungsi awal atau akhir (verbositas singkat dan default) dan pernyataan mulai atau akhir (verbositas verbose). Untuk verbositas default dan verbose, konten argumen fungsi ditampilkan. Konten variabel terkait ditampilkan saat verbositas verbose.
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)
Nomor setelah # adalah penghitung bingkai eksekusi (nomor ini terkait dengan kedalaman tumpukan konteks kesalahan). Ini memungkinkan untuk memasangkan start dan akhir fungsi.
Penelusuran diaktifkan dengan mengatur plpgsql_check.tracer on . Perhatian - Mengaktifkan perilaku ini memiliki dampak negatif yang signifikan pada kinerja (tidak seperti profiler). Anda dapat mengatur level untuk output yang digunakan oleh Tracer plpgsql_check.tracer_errlevel (Default adalah notice ). Konten output dibatasi oleh panjang yang ditentukan oleh plpgsql_check.tracer_variable_max_length Variabel konfigurasi. Pelacak dapat diaktifkan dengan memanggil fungsi plpgsql_check_tracer(true) dan dinonaktifkan dengan memanggil fungsi yang sama dengan argumen false (atau dengan literal on , off ).
Pertama, penggunaan pelacak harus diaktifkan secara eksplisit oleh SuperUser dengan mengatur set plpgsql_check.enable_tracer to on; atau plpgsql_check.enable_tracer to on di postgresql.conf . Ini adalah perlindungan keamanan. Pelacak menunjukkan konten variabel PLPGSQL, dan kemudian beberapa informasi sensitif keamanan dapat ditampilkan kepada pengguna yang tidak beruntung (ketika ia menjalankan fungsi definisi keamanan). Kedua, ekstensi plpgsql_check harus dimuat. Ini dapat dilakukan dengan eksekusi beberapa fungsi plpgsql_check atau secara eksplisit dengan load 'plpgsql_check'; . Anda dapat menggunakan opsi konfigurasi shared_preload_libraries , local_preload_libraries atau session_preload_libraries .
Dalam mode verbose singkat output berkurang:
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
Dalam mode verbose output diperluas tentang detail pernyataan:
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)
Fitur khusus dari Tracer adalah melacak pernyataan ASSERT ketika plpgsql_check.trace_assert on . Saat plpgsql_check.trace_assert_verbosity DEFAULT , maka semua fungsi atau variabel prosedur ditampilkan saat menegaskan ekspresi salah. Ketika konfigurasi ini VERBOSE maka semua variabel dari semua frame PLPGSQL ditampilkan. Perilaku ini independen pada nilai plpgsql.check_asserts . Ini dapat digunakan, meskipun pernyataan dinonaktifkan dalam runtime 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
Tracer dapat menunjukkan penggunaan ID buffer subtransaksi ( nxids ). Nomor tnl yang ditampilkan adalah nomor level bersarang transaksi (untuk PLPGSQL tergantung pada mendalam blok dengan penangan Exception).
Kursor PLPGSQL hanyalah nama kursor SQL. Siklus hidup kursor SQL tidak bergabung dengan ruang lingkup variabel kursor PLPGSQL terkait. Kursor SQL tercipta oleh diri pada akhir transaksi, tetapi untuk transaksi yang lama dan terlalu banyak kursor terbuka itu bisa terlambat. Lebih baik menutup kursor secara eksplisit ketika kursor tidak diperlukan (dengan pernyataan dekat). Tanpa itu masalah memori yang signifikan dimungkinkan.
Ketika pernyataan terbuka, cobalah untuk menggunakan kursor yang belum ditutup, peringatan tersebut dinaikkan. Fitur ini dapat dinonaktifkan dengan mengatur plpgsql_check.cursors_leaks to off . Pemeriksaan ini tidak aktif, saat rutinitas disebut Ristivel
Kursor yang tidak tertutup dapat diperiksa segera setelah fungsi selesai. Cek ini dinonaktifkan secara default, dan harus diaktifkan oleh plpgsql_check.strict_cursors_leaks to on .
Kursor yang tidak tertutup dilaporkan sekali.
Jika Anda menggunakan plugin_debugger (debugger plpgsql) bersama -sama dengan plpgsql_check , maka plpgsql_check harus diinisialisasi setelah plugin_debugger (karena plugin_debugger tidak mendukung berbagi API debug PL/PGSQL). Misalnya ( postgresql.conf ):
shared_preload_libraries = 'plugin_debugger,plpgsql,plpgsql_check'
Tracer mencetak konten variabel atau argumen fungsi. Untuk fungsi definisi keamanan, konten ini dapat menyimpan data yang sensitif terhadap keamanan. Inilah alasan mengapa Tracer dinonaktifkan secara default dan harus diaktifkan hanya dengan hak pengguna super plpgsql_check.enable_tracer .
Anda dapat mengonfigurasi perilaku PLPGSQL_CHECK di dalam fungsi yang diperiksa dengan fungsi "Pragma". Ini adalah analogi bahasa PL/SQL atau ADA dari fitur pragma. PLPGSQL tidak mendukung pragma, tetapi PLPGSQL_CHECK mendeteksi fungsi bernama plpgsql_check_pragma dan mengambil opsi dari parameter fungsi ini. Opsi PLPGSQL_CHECK ini berlaku sampai akhir grup pernyataan ini.
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;
Fungsi plpgsql_check_pragma adalah fungsi abadi yang mengembalikan satu. Ini didefinisikan oleh ekstensi plpgsql_check . Anda dapat mendeklarasikan fungsi alternatif plpgsql_check_pragma seperti:
CREATE OR REPLACE FUNCTION plpgsql_check_pragma(VARIADIC args[])
RETURNS int AS $$
SELECT 1
$$ LANGUAGE sql IMMUTABLE;
Menggunakan fungsi pragma di bagian deklarasi dari opsi set blok teratas pada level fungsi juga.
CREATE OR REPLACE FUNCTION test()
RETURNS void AS $$
DECLARE
aux int := plpgsql_check_pragma('disable:extra_warnings');
...
Sintaks yang lebih pendek untuk pragma juga didukung:
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 - untuk pengujian). Inside String, ada yang bisa digunakan "Variabel": @@ id, @@ name, @@ Signature
status:check , status:tracer , status:other_warnings , status:performance_warnings , status:extra_warnings , status:security_warnings Ini menghasilkan nilai saat ini (misalnya Other_warnings diaktifkan)
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 Ini dapat digunakan untuk menonaktifkan petunjuk dalam pengembalian dari fungsi elemen apa pun. Letakkan saja pragma sebelum pernyataan pengembalian.
type:varname typename atau type:varname (fieldname type, ...) - Setel Type ke Variabel Jenis Rekaman
table: name (column_name type, ...) atau table: name (like tablename) - Buat tabel sementara fana (jika Anda ingin menentukan skema, maka hanya skema pg_temp yang diizinkan.
sequence: name - Buat Urutan Sementara Ephemeral
assert-schema: varname - Periksa Assertasi - Pastikan Skema yang Ditentukan oleh Variabel Valid
assert-table: [ varname_schema, ] , varname - pastikan nama tabel yang ditentukan oleh variabel (dengan penelusuran konstan) valid
assert-column: [varname_schema, ], varname_table , varname - pastikan kolom yang diperpefikasikan oleh variabel valid
Pragmas enable:tracer dan disable:tracer aktif untuk postgres 12 dan lebih tinggi
PLPGSQL_CHECK tidak mendukung pembaruan (dari PLPGSQL_CHECK). Anda harus menjatuhkan ini sebelum menginstal versi baru dari ekstensi ini.
Anda memerlukan lingkungan pengembangan untuk ekstensi PostgreSQL:
make clean
make install
hasil:
[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.
=====================
Terkadang kompilasi yang berhasil dapat membutuhkan paket libicu -dev (PostgreSQL 10 dan lebih tinggi - ketika PG dikompilasi dengan dukungan ICU)
sudo apt install libicu-dev
Anda dapat memeriksa perpustakaan DLL yang sudah dikompilasi http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-available-for-microsoft.html, http://okbob.blogspot.com/2023/10/compiled-dlllllllllen
atau dikompilasi dengan diri sendiri:
plpgsql_check.dll ke PostgreSQL14libplpgsql_check.control dan plpgsql_check--2.1.sql ke PostgreSQL14shareextension meson setup buildcd buildninjasudo ninja installKompilasi terhadap PostgreSQL 10 membutuhkan libicu!
Hak Cipta (C) Pavel Stehule ([email protected])
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Pemberitahuan hak cipta di atas dan pemberitahuan izin ini harus dimasukkan dalam semua salinan atau bagian substansial dari perangkat lunak.
Perangkat lunak ini disediakan "sebagaimana adanya", tanpa jaminan apa pun, tersurat maupun tersirat, termasuk tetapi tidak terbatas pada jaminan dapat diperjualbelikan, kebugaran untuk tujuan tertentu dan nonpringement. Dalam hal apa pun penulis atau pemegang hak cipta tidak akan bertanggung jawab atas klaim, kerusakan atau tanggung jawab lainnya, baik dalam tindakan kontrak, gugatan atau sebaliknya, timbul dari, di luar atau sehubungan dengan perangkat lunak atau penggunaan atau transaksi lain dalam perangkat lunak.
Jika Anda menyukainya, kirim kartu pos ke alamat
Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
Saya mengundang pertanyaan, komentar, laporan bug, tambalan di alamat surat [email protected]