Diese Erweiterung ist ein vollständiger Linter für PLPGSQL für PostgreSQL. Es nutzt nur den internen PostgreSQL -Parser/Evaluator, sodass Sie genau sehen, dass die Fehler zur Laufzeit auftreten würden. Darüber hinaus analysiert es den SQL in Ihren Routinen und findet Fehler, die normalerweise während des Befehls "Prozedur/Funktion erstellen" nicht gefunden werden. Sie können die Ebenen vieler Warnungen und Hinweise kontrollieren. Schließlich können Sie Pragama -Markierungen zum Ausschalten/in vielen Aspekten hinzufügen, sodass Sie Nachrichten ausblenden können, von denen Sie bereits wissen, oder Sie daran erinnern, später zur tieferen Reinigung zurückzukehren.
Ich habe dieses Projekt gegründet, weil ich den Code veröffentlichen wollte, den ich für die zwei Jahre geschrieben habe, als ich versuchte, erweiterte Überprüfung nach Postgresql -stromaufwärts zu schreiben. Es war nicht vollständig erfolgreich - die Integration in Upstream erfordert einige größere PLPGSQL -Refactoring. Der Code ist jedoch voll funktionsfähig und kann in der Produktion verwendet werden (und er wird in der Produktion verwendet). Daher habe ich diese Erweiterung erstellt, um für alle PLPGSQL -Entwickler verfügbar zu sein.
Wenn Sie sich unserer Gruppe anschließen möchten, um die Weiterentwicklung dieser Erweiterung zu unterstützen, registrieren Sie sich bei dieser PostgreSQL -Erweiterung, die Google Group hackt.
Ich lade alle Ideen, Patches, Bugreports ein.
PostgreSQL PostgreSQL 12 - 17 werden unterstützt.
Die SQL -Anweisungen in PL/PGSQL -Funktionen werden vom Validator auf semantische Fehler überprüft. Diese Fehler finden Sie durch Aufrufen des 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$
Funktion PLPGSQL_Check_function () hat drei mögliche Ausgabeformate: Text, JSON oder 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)
Sie können Warnungen über die Parameter der Funktion festlegen:
funcoid oid - Funktionsname oder Funktionssignatur - Funktionen erfordern eine Funktionsspezifikation. Jede Funktion in PostgreSQL kann durch OID oder mit Namen oder Signatur angegeben werden. Wenn Sie die Signatur der OID oder der vollständigen Funktion kennen, können Sie einen Parameter von RegProcedure -Typ wie 'fx()'::regprocedure oder 16799::regprocedure verwenden. Mögliche Alternative verwendet nur einen Namen, wenn der Name der Funktion eindeutig ist - wie 'fx' . Wenn der Name nicht eindeutig ist oder die Funktion nicht existiert, wird ein Fehler aufgenommen. relid DEFAULT 0 - OID der mit Triggerfunktion zugewiesenen Beziehung abgeben. Es ist notwendig, jede Triggerfunktion zu überprüfen. Sie senden die Tabelle darin, dass der Abzug arbeitet.
fatal_errors boolean DEFAULT true - Stop beim ersten Fehler (verhindert massive Fehlerberichte)
other_warnings boolean DEFAULT true - WARNUNGEN wie verschiedene Attribute -Nummer in AstromingMenet auf der linken und rechten Seite, dem Parameter der variablen Überlappungen, nicht verwendeten Variablen, unerwünschten Gießen usw.
extra_warnings boolean DEFAULT true - WARNEN wie fehlende RETURN , Schattenvariablen, toter Code, Niemals (nicht verwendete) Funktionsparameter, unmodifizierte Variablen, geänderte automatische Variablen usw.
performance_warnings boolean DEFAULT false - leistungsbezogene Warnungen wie deklarierter Typ mit Typmodifikator, Casting, implizite Abgüsse in der Klausel (kann der Grund sein, warum ein Index nicht verwendet wird) usw.
security_warnings boolean DEFAULT false - Sicherheitsbezogene Überprüfungen wie die Erkennung von Schwachstellen bei SQL Injection
compatibility_warnings boolean DEFAULT false - Kompatibilitätsbezogene Überprüfungen wie veraltete explizite Einstellung interner Cursornamen in den Variablen von RefCursor oder Cursor.
anyelementtype regtype DEFAULT 'int' - Ein tatsächlicher Typ, der beim Testen des AnyLement -Typs verwendet werden soll
anyenumtype regtype DEFAULT '-' - Ein tatsächlicher Typ, der beim Testen des Typs allerum verwendet werden soll
anyrangetype regtype DEFAULT 'int4range' - Ein tatsächlicher Typ, der beim Testen des AnyRange -Typs verwendet werden soll
anycompatibletype DEFAULT 'int' - Ein tatsächlicher Typ, der beim Testen des Anycompatible -Typs verwendet werden soll
anycompatiblerangetype DEFAULT 'int4range' - Ein tatsächlicher Bereich, der beim Testen des Typs Anycompatible -Bereich verwendet wird, der verwendet wird
without_warnings DEFAULT false - Deaktivieren Sie alle Warnungen (ignorieren Sie alle XXXX_WARNING -Parameter, eine schnelle Überschrift).
all_warnings DEFAULT false - Aktivieren Sie alle Warnungen (Ignoriert andere XXX_WARNING -Parameter, ein schnelles Positiv).
newtable DEFAULT NULL , oldtable DEFAULT NULL - Die Namen neuer oder alter Übergangstabellen. Diese Parameter sind erforderlich, wenn Übergangstabellen in Triggerfunktionen verwendet werden.
use_incomment_options DEFAULT true - Wenn es wahr ist, sind die Optionen für die Inklusive aktiv
incomment_options_usage_warning DEFAULT false - Wenn es wahr ist, wird die Warnung angehoben, wenn die Option für die Möglichkeit verwendet wird.
constant_tracing boolean DEFAULT true - Wenn es wahr ist, kann die Variable, die einen konstanten Inhalt enthält, wie konstant verwendet werden (es funktioniert nur in einigen einfachen Fällen, und der Inhalt der Variablen sollte nicht mehrdeutig sein).
Wenn Sie einen Trigger überprüfen möchten, müssen Sie eine Beziehung eingeben, die zusammen mit der Triggerfunktion verwendet wird
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$
Fehlende Beziehungspezifikation
postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR: missing trigger relation
HINT: Trigger relation oid must be valid
Richtige Auslöserprüfung (mit angegebener Beziehung)
postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
Für Trigger mit Transitivtabellen können Sie die oldtable und newtable Parameter festlegen:
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 ermöglicht eine anhaltende Einstellung in Kommentaren. Diese Optionen werden vor dem Überprüfen aus dem Quellcode der Funktion entnommen. Die Syntax ist:
@plpgsql_check_option: optioname [=] value [, optname [=] value ...]
Die Einstellungen von Kommentaroptionen haben oberste Priorität, kann jedoch false Allgemeinen durch Option use_incomment_options werden.
Beispiel:
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;
Sie können die PLPGSQL_Check_Function zur Massenüberprüfung von Funktionen/Verfahren und Massenüberprüfung von Triggern verwenden. Bitte testen Sie die folgenden Fragen:
-- 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;
oder
-- 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';
oder
-- 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;
Funktionen können bei der Ausführung überprüft werden - PLPGSQL_Check -Modul muss geladen werden (über 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
Der Standardmodus ist By_function , dh die erweiterte Überprüfung erfolgt nur im aktiven Modus - indem die PLPGSQL_Check_Function aufgerufen wird. fresh_start bedeutet Kaltstart (zuerst wird die Funktion aufgerufen).
Sie können den passiven Modus von aktivieren
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 Cursors und RefCursors Variablen sind verbesserte Zeichenfolgenvariablen, die einen eindeutigen Namen des verwandten Portals enthält (interne Struktur von Postgres, die für die Implementierung von Cursor verwendet werden). Bis nach Postgresql 16 hatte das Portal den gleichen Namen wie den Namen der Cursorvariablen. PostgreSQL 16 und höher ändern diesen Mechanismus und standardmäßig verwandtes Portal wird mit einem einzigartigen Namen benannt. Es löst einige Probleme mit Cursoren in verschachtelten Blöcken oder wenn Cursor in rekursiver als Funktion bezeichnet wird.
Bei der genannten Änderung sollte die Variable des RefCursors einen Wert aus einer anderen RefCursor -Variablen oder aus einer Cursorvariablen (wenn Cursor geöffnet wird) annehmen.
-- 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;
...
Wenn das Flag compatibility_warnings aktiv ist, versuchen plpgsql_check , einige fischige, die der Variablen von RefCursor zuweisen oder die Werte von RefCursor zurückgeben:
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 sollte fast alle Fehler auf wirklich statischen Code finden. Wenn Entwickler die dynamischen Funktionen von PLPGSQL wie Dynamic SQL oder Datensatzstyp verwenden, sind falsch positive Ergebnisse möglich. Diese sollten selten sein - in gut geschriebenem Code - und dann sollte die betroffene Funktion neu gestaltet oder PLPGSQL_Check für diese Funktion deaktiviert werden.
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;
Eine Verwendung von PLPGSQL_Check fügt einen kleinen Overhead hinzu (wenn der passive Modus aktiviert ist) und Sie sollten diese Einstellung nur in Entwicklungs- oder Vorproduktionsumgebungen verwenden.
Dieses Modul überprüft keine Abfragen, die zur Laufzeit zusammengestellt werden. Es ist nicht möglich, die Ergebnisse dynamischer Abfragen zu identifizieren - daher kann PLPGSQL_Check nicht korrekte Typen für die Aufzeichnung von Variablen festlegen und können nicht abhängig SQLs und Ausdrücke überprüfen.
Wenn die Art der Datensatzvariable nicht bekannt ist, können Sie sie explizit dem Pragma type zuweisen:
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 kann nicht verwendet werden, um die Struktur referenzierter Cursoren zu erkennen. Eine Referenz zum Cursor in PLPGSQL wird als Name des globalen Cursors implementiert. In der Schauere ist der Name nicht bekannt (nicht in allen Möglichkeiten), und es gibt keinen globalen Cursor. Es ist ein wesentliches Problem für jede statische Analyse. PLPGSQL kann nicht wissen, wie der richtige Typ für die Datensatzvariablen festgelegt werden soll, und kann die abhängigen SQL -Anweisungen und -ausdrücke nicht überprüfen. Eine Lösung ist für dynamische SQL gleich. Verwenden Sie keine Datensatzvariable als Ziel, wenn Sie den RefCursor -Typ verwenden oder PLPGSQL_Check für diese Funktionen deaktivieren.
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
In diesem Fall sollte nicht ein Datensatztyp verwendet werden (verwenden Sie stattdessen den bekannten 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 können keine Abfragen über temporäre Tabellen überprüfen, die in der Funktionsruntime von PLPGSQL erstellt werden. Für diesen Anwendungsfall ist es erforderlich, eine gefälschte Temperaturtabelle zu erstellen oder PLPGSQL_Check für diese Funktion zu deaktivieren.
In der Realität werden TEMP -Tabellen im eigenen (pro Benutzer-) Schema mit höherer Priorität als persistierende Tabellen gespeichert. Sie können dies tun (mit dem folgenden Trick sicher):
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
Dieser Trick emuliert teilweise globale Temperaturtabellen und ermöglicht eine statische Validierung. Eine weitere Möglichkeit besteht
Sie können die Pragma table verwenden und eine kurzlebige Tabelle erstellen:
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)');
...
Eine Funktion PLPGSQL_Show_Dependency_tb zeigt alle Funktionen, Operatoren und Beziehungen in der in der verarbeiteten Funktion verwendet:
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)
Optionale Argumente von PLPGSQL_Show_Dependency_tb sind relid , anyelementtype , enumtype , anyrangetype , anycompatibletype und anycompatiblerangetype .
Das PLPGSQL_Check enthält einen einfachen Profiler von PLPGSQL -Funktionen und -Verfahren. Es kann mit/ohne Zugriff auf gemeinsam genutzten Speicher funktionieren. Es hängt von der Konfiguration shared_preload_libraries ab. Wenn PLPGSQL_Check von shared_preload_libraries initialisiert wird, kann es den gemeinsam genutzten Speicher zuweisen und die Funktionsprofile werden dort gespeichert. Wenn PLPGSQL_Check nicht zur Zuordnung des gemeinsamen Speicheres nicht zugewiesen wird, wird das Profil im Sitzungsspeicher gespeichert.
Aufgrund von Abhängigkeiten sollten shared_preload_libraries zuerst plpgsql enthalten
postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
│ shared_preload_libraries │
╞══════════════════════════╡
│ plpgsql,plpgsql_check │
└──────────────────────────┘
(1 row)
Der Profiler ist aktiv, wenn GUC plpgsql_check.profiler eingeschaltet ist. Der Profiler benötigt keinen gemeinsamen Speicher, aber wenn nicht genügend gemeinsamer Speicher vorhanden ist, ist der Profiler nur auf aktive Sitzung begrenzt. Der Profiler kann aktiviert werden, indem die Funktion plpgsql_check_profiler(true) aufgerufen und durch Aufruf derselben Funktion mit false Argument (oder mit Literalen on ) deaktiviert off .
Das PLPGSQL_Check sollte initialisiert werden, bevor eine PLPGSQL -Funktion ausgeführt wird. Nur die frühe Initialisierung sorgt für die korrekte Arbeit von Profiler und Tracer. Wenn Sie shared_preloaded_libraries nicht verwenden, können Sie stattdessen den Befehl load 'plpgsql_check' verwenden.
Wenn PLPGSQL_Check von shared_preload_libraries initialisiert wird, ist ein weiterer GUC verfügbar, um die vom Profiler verwendete gemeinsame Speicherspeicher zu konfigurieren: plpgsql_check.profiler_max_shared_chunks . Dies definiert die maximale Anzahl von Anweisungen, die im gemeinsamen Speicher gespeichert werden können. Für jede PLPGSQL -Funktion (oder Prozedur) wird der gesamte Inhalt in Stücke von 30 Anweisungen aufgeteilt. Bei Bedarf können mehrere Teile verwendet werden, um den gesamten Inhalt einer einzelnen Funktion zu speichern. Ein einzelner Stück ist 1704 Bytes. Der Standardwert für diesen GUC beträgt 15000, was für große Projekte ausreichen sollte, die Hunderttausende von Aussagen in PLPGSQL enthalten und etwa 24 MB Speicher verbrauchen. Wenn Ihr Projekt nicht so viele Stücke benötigt, können Sie diesen Parameter auf eine kleinere Zahl einstellen, um die Speicherverwendung zu verringern. Der Mindestwert beträgt 50 (was etwa 83 KB Speicher verbrauchen sollte) und der Maximalwert 100000 (was etwa 163 MB Speicher verbrauchen sollte). Das Ändern dieses Parameters erfordert einen PostgreSQL -Neustart.
Der Profiler wird auch die Abfragekennung für jede Anweisung abrufen, die einen Ausdruck oder eine optimierbare Anweisung enthält. Beachten Sie, dass dies erforderlich ist, dass pg_stat_statements oder eine andere ähnliche Erweiterung von Drittanbietern installiert werden muss. Es gibt einige Einschränkungen bei der Abnahme des Abfragebedingers:
Achtung: Ein Update gemeinsamer Profile kann die Leistung auf Servern unter höherer Belastung verringern.
Das Profil kann nach Funktion plpgsql_profiler_function_tb angezeigt werden:
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)
Die Zeiten im Ergebnis sind Milisekunden.
Das Profil pro Anweisungen (nicht pro Zeile) kann durch Funktion PLPGSQL_PROFILER_FUNCTION_STATEMENTS_TB angezeigt werden:
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)
Alle gespeicherten Profile können angezeigt werden, indem die Funktion plpgsql_profiler_functions_all aufruft:
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)
Es gibt zwei Funktionen für die Reinigung gespeicherter Profile: plpgsql_profiler_reset_all() und plpgsql_profiler_reset(regprocedure) .
PLPGSQL_Check bietet zwei Funktionen:
plpgsql_coverage_statements(name)plpgsql_coverage_branches(name)Es gibt einen weiteren sehr guten PLPGSQL -Profiler - https://github.com/glynastill/plprofiler
Meine Erweiterung ist so konzipiert, dass sie einfach für die Verwendung und praktisch ist. Nichts mehr oder weniger.
PLPROFILER ist komplexer. Es erstellt Call -Diagramme und kann aus diesem Diagramm Flammengraphen der Ausführungszeiten erstellen.
Beide Erweiterungen können zusammen mit der Funktion des integrierten Postgresql - Tracking -Funktionen - verwendet werden.
set track_functions to 'pl';
...
select * from pg_stat_user_functions;
PLPGSQL_Check bietet eine Tracing -Möglichkeit - in diesem Modus können Sie Hinweise auf Start- oder Endfunktionen (knackige und Standardausläufe) und Start- oder Endaussagen (ausführliche Ausführlichkeit) sehen. Für Standard- und ausführliche Ausführlichkeit wird der Inhalt der Funktionsargumente angezeigt. Der Inhalt verwandter Variablen wird angezeigt, wenn Ausführlichkeit ausführlich ist.
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)
Die Zahl nach # ist ein Ausführungsrahmenzähler (diese Nummer bezieht sich auf die Tiefe des Fehlerkontextstapels). Es ermöglicht das Kombinieren von Start und Ende der Funktion.
Die Verfolgung erfolgt durch Einstellen plpgsql_check.tracer auf on . Achtung - Das Ermöglichen dieses Verhaltens hat erhebliche negative Auswirkungen auf die Leistung (im Gegensatz zum Profiler). Sie können einen von Tracer plpgsql_check.tracer_errlevel verwendeten Ausgangspegel festlegen (Standard ist notice ). Der Ausgangsinhalt wird durch Länge begrenzt, die durch plpgsql_check.tracer_variable_max_length -Konfigurationsvariable angegeben ist. Der Tracer kann aktiviert werden, indem die Funktion plpgsql_check_tracer(true) aufgerufen und durch Aufrufen derselben Funktion mit false Argument (oder mit Literalen on ) deaktiviert off .
Zunächst sollte die Verwendung von Tracer von Superuser explizit aktiviert werden, indem set plpgsql_check.enable_tracer to on; oder plpgsql_check.enable_tracer to on in postgresql.conf . Dies ist ein Sicherheitsschutz. Der Tracer zeigt den Inhalt der Variablen von PLPGSQL an, und dann können einige sicherheitssensitive Informationen einem nicht privilegierten Benutzer angezeigt werden (wenn er Sicherheitsdefinitionsfunktion ausführt). Zweitens sollte die Erweiterung plpgsql_check geladen werden. Dies kann durch Ausführung einer plpgsql_check -Funktion oder explizit durch den Befehl load 'plpgsql_check'; . Sie können die Option der Konfiguration shared_preload_libraries , local_preload_libraries oder session_preload_libraries verwenden.
Im prese ausführlichen Modus wird der Ausgang reduziert:
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
Im ausführlichen Modus wird die Ausgabe über Anweisungsdetails erweitert:
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)
Eine spezielle Merkmale von Tracer ist die Verfolgung der ASSERT -Anweisung, wenn plpgsql_check.trace_assert on ist. Wenn plpgsql_check.trace_assert_verbosity DEFAULT ist, werden alle Variablen aller Funktionen oder Prozeduren angezeigt, wenn der Ausdruck falsch ist. Wenn diese Konfiguration VERBOSE ist, werden alle Variablen aller PLPGSQL -Rahmen angezeigt. Dieses Verhalten ist unabhängig beim Wert plpgsql.check_asserts . Es kann verwendet werden, obwohl die Behauptungen in der PLPGSQL -Laufzeit deaktiviert sind.
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 kann die Nutzung der Subtransaktion -Puffer -ID ( nxids ) zeigen. Die angezeigte tnl -Nummer ist die Transaktionsnistnistenniveau (für PLPGSQL hängt von der Tiefenblöcke mit den Handlern der Ausnahme ab).
PLPGSQLs Cursor sind nur Namen von SQL Cursors. Der Lebenszyklus von SQL Cursors ist nicht mit dem Umfang der verwandten PLPGSQL -Cursorvariablen verbunden. SQL Cursors werden am Transaktionende von selbst gepackt, aber für lange Transaktionen und zu viel geöffnete Cursor kann es zu spät sein. Es ist besser, den Cursor explizit zu schließen, wenn Cursor nicht erforderlich ist (durch genaue Aussage). Ohne sie sind die erheblichen Gedächtnisprobleme möglich.
Wenn eine offene Anweisung versucht, Cursor noch nicht geschlossen zu können, wird die Warnung angehoben. Diese Funktion kann deaktiviert werden, indem plpgsql_check.cursors_leaks to off eingestellt wird. Diese Überprüfung ist nicht aktiv, wenn Routine wiederholt aufgerufen wird
Die nicht abgestellten Cursor können sofort überprüft werden, wenn die Funktion beendet ist. Diese Überprüfung ist standardmäßig deaktiviert und sollte von plpgsql_check.strict_cursors_leaks to on aktiviert werden.
Jeder nicht abgeschlossene Cursor wird einmal gemeldet.
Wenn Sie plugin_debugger (PLPGSQL -Debugger) zusammen mit plpgsql_check verwenden, sollte plpgsql_check nach plugin_debugger initialisiert werden (weil plugin_debugger die Freigabe von PL/PGSQL -Debug -API nicht unterstützt). Zum Beispiel ( postgresql.conf ):
shared_preload_libraries = 'plugin_debugger,plpgsql,plpgsql_check'
Tracer druckt Inhalt von Variablen oder Funktionsargumenten. Für die Sicherheitsdefinitionsfunktion kann dieser Inhalt Sicherheitssensitive Daten enthalten. Dies ist Grund, warum Tracer standardmäßig deaktiviert ist und nur mit Super -Benutzerrechts plpgsql_check.enable_tracer aktiviert werden sollte.
Sie können PLPGSQL_Check -Verhalten in einer geprüften Funktion mit "Pragma" -Funktion konfigurieren. Dies ist eine Analogie der PL/SQL- oder ADA -Sprache der Pragma -Funktion. PLPGSQL unterstützt Pragma nicht, aber PLPGSQL_Check erkennt die Funktion mit dem Namen plpgsql_check_pragma und nimmt Optionen aus den Parametern dieser Funktion auf. Diese PLPGSQL_Check -Optionen sind bis zum Ende dieser Gruppe von Aussagen gültig.
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;
Die Funktion plpgsql_check_pragma ist eine unveränderliche Funktion, die eine zurückgibt. Es wird durch plpgsql_check -Erweiterung definiert. Sie können alternative plpgsql_check_pragma -Funktion deklarieren wie:
CREATE OR REPLACE FUNCTION plpgsql_check_pragma(VARIADIC args[])
RETURNS int AS $$
SELECT 1
$$ LANGUAGE sql IMMUTABLE;
Verwenden Sie die Pragma -Funktion in der Deklaration, Teil des Top -Block -Setzungsoptionen auch auf Funktionsebene.
CREATE OR REPLACE FUNCTION test()
RETURNS void AS $$
DECLARE
aux int := plpgsql_check_pragma('disable:extra_warnings');
...
Eine kürzere Syntax für Pragma wird ebenfalls unterstützt:
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 - Druckzeichenfolge (zum Testen). In der String können "Variablen" verwendet werden: @@ id, @@ name, @@ Signature
status:check , status:tracer , status:other_warnings , status:performance_warnings , status:extra_warnings , status:security_warnings Dies gibt den aktuellen Wert aus (z. B. andere_Warnings aktiviert)
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 Mit Dies kann der Hinweis bei der Rückkehr von einer beliebigen Funktion deaktiviert werden. Setzen Sie einfach die Pragma vor die Rückgabeerklärung.
type:varname typename oder type:varname (fieldname type, ...) - Typtyp auf Variable des Datensatztyps setzen
table: name (column_name type, ...) oder table: name (like tablename) - Erstellen Sie eine kurzlebige temporäre Tabelle (wenn Sie Schema angeben möchten, ist nur pg_temp -Schema zulässig.
sequence: name - Erstellen Sie eine kurzlebige temporäre Sequenz
assert-schema: varname - Überprüfung der Zeitbehandlung - Stellen Sie sicher, dass das durch Variablen angegebene Schema gültig ist
assert-table: [ varname_schema, ] , varname - Stellen Sie sicher, dass der durch Variablen angegebene Tabellenname (durch konstante Verfolgung) gültig ist
assert-column: [varname_schema, ], varname_table , varname - Stellen Sie sicher
Pragmas enable:tracer und disable:tracer sind für Postgres 12 und höher aktiv
PLPGSQL_Check unterstützt das Update (von PLPGSQL_Check) nicht. Sie sollten dies vor der Installation einer neuen Version dieser Erweiterung fallen lassen.
Sie benötigen eine Entwicklungsumgebung für Postgresql -Erweiterungen:
make clean
make install
Ergebnis:
[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.
=====================
Manchmal erfordern eine erfolgreiche Kompilierung ein Libicu -dev -Paket (PostgreSQL 10 und höher - wenn PG mit der Intensivstation zusammengestellt wurde)
sudo apt install libicu-dev
You can check precompiled dll libraries http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-available-for-microsoft.html, http://okbob.blogspot.com/2023/10/compiled-dll-of-plpgsqlcheck-254-and.html
oder durch sich selbst kompilieren:
plpgsql_check.dll PostgreSQL14libplpgsql_check.control plpgsql_check--2.1.sql PostgreSQL14shareextension meson setup buildcd buildninjasudo ninja installDie Kompilierung gegen PostgreSQL 10 erfordert Libicu!
Copyright (c) Pavel Stehule ([email protected])
Die Erlaubnis wird hiermit einer Person, die eine Kopie dieser Software und zugehörigen Dokumentationsdateien (der "Software") erhält, kostenlos erteilt, um die Software ohne Einschränkung zu behandeln, einschließlich ohne Einschränkung der Rechte, zu verwenden, zu kopieren, zu modifizieren, zusammenzufassen, zu veröffentlichen, zu veröffentlichen, zu verteilen, zu verteilt, und/oder Kopien der Software zu ermöglichen, um Personen zu beanstanden, an denen die Software zugänglich ist, um die folgenden Bedingungen zu beantragen.
Die oben genannte Copyright -Mitteilung und diese Erlaubnisbekanntmachung müssen in alle Kopien oder wesentlichen Teile der Software enthalten sein.
Die Software wird "wie es ist" ohne Garantie jeglicher Art, ausdrücklich oder stillschweigend bereitgestellt, einschließlich, aber nicht beschränkt auf die Gewährleistung der Handelsfähigkeit, die Eignung für einen bestimmten Zweck und die Nichtverletzung. In keinem Fall sind die Autoren oder Urheberrechtsinhaber für Ansprüche, Schäden oder andere Haftungen haftbar, sei es in einer Vertragsklage, unerbittlich oder auf andere Weise, die sich aus oder im Zusammenhang mit der Software oder anderen Geschäften in der Software ergeben.
Wenn Sie es mögen, senden Sie eine Postkarte an die Adresse
Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
Ich lade alle Fragen, Kommentare, Fehlerberichte, Patches auf Mail -Adresse [email protected] ein