هذا الامتداد عبارة عن linter كامل لـ PLPGSQL لـ postgresql. إنه يعزز فقط محلل/المقيِّم الداخلي PostgreSQL بحيث ترى بالضبط أن الأخطاء ستحدث في وقت التشغيل. علاوة على ذلك ، فإنه يوسع SQL داخل روتينك ويجد أخطاء غير موجودة عادةً أثناء أمر "إنشاء/وظيفة". يمكنك التحكم في مستويات العديد من التحذيرات والتلميحات. أخيرًا ، يمكنك إضافة علامات نوع Pragama لإيقاف/على العديد من الجوانب مما يتيح لك إخفاء الرسائل التي تعرفها بالفعل ، أو تذكيرك بالعودة للتنظيف الأعمق لاحقًا.
لقد أسست هذا المشروع ، لأنني أردت نشر الرمز الذي كتبته خلال العامين ، عندما حاولت كتابة فحص محسّن لـ PostgreSQL. لم يكن ناجحًا تمامًا - يتطلب التكامل في المنبع بعض إعادة إعادة تصميم PLPGSQL الأكبر. لكن الرمز يعمل بكامل طاقته ويمكن استخدامه في الإنتاج (ويتم استخدامه في الإنتاج). لذلك ، قمت بإنشاء هذا الامتداد ليكون متاحًا لجميع مطوري PLPGSQL.
إذا كنت ترغب في الانضمام إلى مجموعتنا للمساعدة في تطوير هذا الامتداد ، فقم بتسجيل نفسك على هذا الامتداد postgresql اختراق مجموعة Google.
أدعو أي أفكار ، بقع ، bugreports.
يتم دعم PostgreSQL PostgreSQL 12 - 17.
يتم فحص عبارات SQL داخل وظائف PL/PGSQL بواسطة التحقق من الأخطاء الدلالية. يمكن العثور على هذه الأخطاء عن طريق استدعاء 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 أو إكمال الوظيفة ، يمكنك استخدام معلمة نوع Regprocedure مثل 'fx()'::regprocedure أو 16799::regprocedure . البديل المحتمل هو استخدام اسم فقط ، عندما يكون اسم الوظيفة فريدًا - مثل 'fx' . عندما يكون الاسم ليس فريدًا أو لا توجد الوظيفة ، فإنها ترفع خطأ. relid DEFAULT 0 - OID من العلاقة المعينة مع وظيفة الزناد. من الضروري التحقق من أي وظيفة الزناد. أنت ترسل الجدول من حيث أن الزناد يعمل عليه.
fatal_errors boolean DEFAULT true - توقف عن الخطأ الأول (يمنع تقارير الأخطاء الهائلة)
other_warnings boolean DEFAULT true - إظهار تحذيرات مثل رقم السمات المختلفة في envisionmenet على الجانب الأيسر واليسرى ، معلمة دالة التداخل المتغيرة ، المتغيرات غير المستخدمة ، الصب غير المرغوب فيها ، إلخ.
extra_warnings boolean DEFAULT true - إظهار تحذيرات مثل RETURN المفقود ، والمتغيرات المظللة ، والرمز الميت ، وعدم قراءة المعلمة (غير المستخدمة) ، والمتغيرات غير المعدلة ، والمتغيرات التلقائية المعدلة ، إلخ.
performance_warnings boolean DEFAULT false - تحذيرات ذات صلة بالأداء مثل النوع المعلن مع المعدل من النوع ، الصب ، الممثلات الضمنية في مكان البند (يمكن أن يكون السبب وراء عدم استخدام الفهرس) ، إلخ.
security_warnings boolean DEFAULT false - الشيكات المتعلقة بالأمان مثل اكتشاف قابلية حقن SQL
compatibility_warnings boolean DEFAULT false - التوافقات المتعلقة بالتوافق مثل الأسماء الصريحة العجلة الأسماء الداخلية في متغيرات refcursor أو المؤشر.
anyelementtype regtype DEFAULT 'int' - نوع فعلي لاستخدامه عند اختبار نوع anyelement
anyenumtype regtype DEFAULT '-' - نوع فعلي لاستخدامه عند اختبار نوع anyenum
anyrangetype regtype DEFAULT 'int4range' - نوع فعلي لاستخدامه عند اختبار نوع أي نوع
anycompatibletype DEFAULT 'int' - نوع فعلي لاستخدامه عند اختبار نوع أي شيء
anycompatiblerangetype DEFAULT 'int4range' - نوع النطاق الفعلي لاستخدامه عند اختبار نوع النطاق غير القابل للتطبيق
without_warnings DEFAULT false - تعطيل جميع التحذيرات (يتجاهل جميع معلمات xxxx_warning ، تجاوز سريع)
all_warnings DEFAULT false - تمكين جميع التحذيرات (يتجاهل معلمات XXX_WARNING أخرى ، إيجابية سريعة)
newtable DEFAULT NULL ، oldtable DEFAULT NULL - أسماء جداول الانتقال الجديدة أو القديمة. هذه المعلمات مطلوبة عند استخدام جداول الانتقال في وظائف الزناد.
use_incomment_options DEFAULT true - عندما يكون هذا صحيح
incomment_options_usage_warning DEFAULT false - عندما يكون هذا صحيحًا ، يتم رفع التحذير عند استخدام خيار الخفية.
constant_tracing boolean DEFAULT true - عندما يكون صحيحًا ، يمكن استخدام المتغير الذي يحمل بعض المحتوى الثابت ، مثل ثابت (إنه يعمل فقط في بعض الحالات البسيطة ، ويجب ألا يكون محتوى المتغير غامضًا).
عندما تريد التحقق من أي مشغل ، يجب عليك إدخال علاقة سيتم استخدامها مع وظيفة الزناد
CREATE TABLE bar(a int, b int);
postgres=# sf+ foo_trg
CREATE OR REPLACE FUNCTION public.foo_trg()
RETURNS trigger
LANGUAGE plpgsql
1 AS $function$
2 BEGIN
3 NEW.c := NEW.a + NEW.b;
4 RETURN NEW;
5 END;
6 $function$
مواصفات العلاقة المفقودة
postgres=# select * from plpgsql_check_function('foo_trg()');
ERROR: missing trigger relation
HINT: Trigger relation oid must be valid
فحص المشغل الصحيح (مع علاقة محددة)
postgres=# select * from plpgsql_check_function('foo_trg()', 'bar');
plpgsql_check_function
--------------------------------------------------------
error:42703:3:assignment:record "new" has no field "c"
(1 row)
بالنسبة للمشغلات مع الجداول المتعدية ، يمكنك تعيين المعلمات oldtable newtable :
create or replace function footab_trig_func()
returns trigger as $$
declare x int;
begin
if false then
-- should be ok;
select count(*) from newtab into x;
-- should fail;
select count(*) from newtab where d = 10 into x;
end if;
return null;
end;
$$ language plpgsql;
select * from plpgsql_check_function('footab_trig_func','footab', newtable := 'newtab');
يسمح PLPGSQL_Check بإعداد مستمر مكتوب في التعليقات. يتم أخذ هذه الخيارات من رمز مصدر الوظيفة قبل التحقق. بناء الجملة هو:
@plpgsql_check_option: optioname [=] value [, optname [=] value ...]
تعد الإعدادات من خيارات التعليقات ذات أولوية عالية ، ولكن يمكن تعطيلها بشكل عام عن طريق use_incomment_options إلى false .
مثال:
create or replace function fx(anyelement)
returns text as $$
begin
/*
* rewrite default polymorphic type to text
* @plpgsql_check_options: anyelementtype = text
*/
return $1;
end;
$$ language plpgsql;
يمكنك استخدام PLPGSQL_CHECK_FUNCTION للتحقق من الوظائف/الإجراءات والتحقق من الكتلة للمشغلات. من فضلك ، اختبار الاستعلامات التالية:
-- check all nontrigger plpgsql functions
SELECT p.oid, p.proname, plpgsql_check_function(p.oid)
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON pronamespace = n.oid
JOIN pg_catalog.pg_language l ON p.prolang = l.oid
WHERE l.lanname = 'plpgsql' AND p.prorettype <> 2279;
أو
-- check all trigger plpgsql functions
SELECT p.proname, tgrelid::regclass, cf.*
FROM pg_proc p
JOIN pg_trigger t ON t.tgfoid = p.oid
JOIN pg_language l ON p.prolang = l.oid
JOIN pg_namespace n ON p.pronamespace = n.oid,
LATERAL plpgsql_check_function(p.oid, t.tgrelid) cf
WHERE n.nspname = 'public' and l.lanname = 'plpgsql';
أو
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0)) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
يمكن فحص الوظائف عند التنفيذ - يجب تحميل وحدة PLPGSQL_Check (عبر postgreSQL.Conf).
plpgsql_check.mode = [ disabled | by_function | fresh_start | every_start ]
plpgsql_check.fatal_errors = [ yes | no ]
plpgsql_check.show_nonperformance_warnings = false
plpgsql_check.show_performance_warnings = false
الوضع الافتراضي هو by_function ، وهذا يعني أن التحقق المحسن يتم فقط في الوضع النشط - عن طريق استدعاء PLPGSQL_CHECK_FUNCTION . fresh_start يعني البدء البارد (أولاً تسمى الوظيفة).
يمكنك تمكين الوضع السلبي
load 'plpgsql'; -- 1.1 and higher doesn't need it
load 'plpgsql_check';
set plpgsql_check.mode = 'every_start'; -- This scans all code before it is executed
SELECT fx(10); -- run functions - function is checked before runtime starts it
متغيرات مؤشر PostgreSQL و REFCURSOR هي متغيرات السلسلة المعززة التي تحمل اسمًا فريدًا للبوابة ذات الصلة (البنية الداخلية للبنية التي يتم استخدامها لتنفيذ المؤشر). حتى PostgreSql 16 ، كان للبوابة نفس اسم مثل متغير المؤشر. سيتم تسمية PostgreSQL 16 و Thight Change هذه الآلية والبوابة ذات الصلة الافتراضية ببعض الأسماء الفريدة. إنه يحل بعض المشكلات مع المؤشرات في الكتل المتداخلة أو عند استخدام المؤشر في الوظيفة المتكررة.
مع التغيير المذكور ، يجب أن يأخذ متغير refcursor القيمة من متغير refcursor آخر أو من بعض متغير المؤشر (عند فتح المؤشر).
-- obsolete pattern
DECLARE
cur CURSOR FOR SELECT 1;
rcur refcursor;
BEGIN
rcur := 'cur';
OPEN cur;
...
-- new pattern
DECLARE
cur CURSOR FOR SELECT 1;
rcur refcursor;
BEGIN
OPEN cur;
rcur := cur;
...
عندما تكون علامة compatibility_warnings نشطة ، فحاول plpgsql_check تحديد بعض التعيينات السمكية لمتغير أو إرجاع قيم Refcursor:
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 تعيين النوع الصحيح لتسجيل المتغيرات ولا يمكن التحقق من SQLs والتعبيرات التابعة.
عندما لا يعرف نوع متغير السجل ، يمكنك تعيينه بشكل صريح type البراغما:
DECLARE r record;
BEGIN
EXECUTE format('SELECT * FROM %I', _tablename) INTO r;
PERFORM plpgsql_check_pragma('type: r (id int, processed bool)');
IF NOT r.processed THEN
...
لا يمكن استخدام PLPGSQL_Check للكشف عن بنية المؤشرات المرجعية. يتم تنفيذ مرجع على المؤشر في PLPGSQL كاسم للمؤشر العالمي. في وقت التحقق ، لا يُعرف الاسم (ليس في جميع الاحتمالات) ، ولا يوجد المؤشر العالمي. إنها مشكلة كبيرة لأي تحليل ثابت. لا يمكن لـ PLPGSQL معرفة كيفية تعيين النوع الصحيح لمتغيرات السجل ولا يمكنه التحقق من عبارات وتعبيرات SQL التابعة. الحل هو نفسه بالنسبة إلى SQL الديناميكي. لا تستخدم متغير السجل كهدف عند استخدام نوع refcursor أو تعطيل PLPGSQL_Check لهذه الوظائف.
CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS $$
DECLARE
rec_var record;
BEGIN
FETCH refcur_var INTO rec_var; -- this is STOP for plpgsql_check
RAISE NOTICE '%', rec_var; -- record rec_var is not assigned yet error
في هذه الحالة ، لا ينبغي استخدام نوع السجل (استخدم RowType المعروف بدلاً من ذلك):
CREATE OR REPLACE FUNCTION foo(refcur_var refcursor)
RETURNS void AS $$
DECLARE
rec_var some_rowtype;
BEGIN
FETCH refcur_var INTO rec_var;
RAISE NOTICE '%', rec_var;
لا يمكن لـ PLPGSQL_Check التحقق من الاستعلامات عبر الجداول المؤقتة التي يتم إنشاؤها في وقت تشغيل وظيفة PLPGSQL. لحالة الاستخدام هذه ، من الضروري إنشاء جدول مؤقت مزيف أو تعطيل PLPGSQL_Check لهذه الوظيفة.
في الواقع ، يتم تخزين جداول مؤقتة في مخطط (لكل مستخدم) ذات أولوية أعلى من الجداول المستمرة. لذلك يمكنك القيام (مع متابعة خدعة سرية):
CREATE OR REPLACE FUNCTION public.disable_dml()
RETURNS trigger
LANGUAGE plpgsql AS $function$
BEGIN
RAISE EXCEPTION SQLSTATE '42P01'
USING message = format('this instance of %I table doesn''t allow any DML operation', TG_TABLE_NAME),
hint = format('you should use "CREATE TEMP TABLE %1$I(LIKE %1$I INCLUDING ALL);" statement',
TG_TABLE_NAME);
RETURN NULL;
END;
$function$;
CREATE TABLE foo(a int, b int); -- doesn't hold data, ever
CREATE TRIGGER foo_disable_dml
BEFORE INSERT OR UPDATE OR DELETE ON foo
EXECUTE PROCEDURE disable_dml();
postgres=# INSERT INTO foo VALUES(10,20);
ERROR: this instance of foo table doesn't allow any DML operation
HINT: you should to run "CREATE TEMP TABLE foo(LIKE foo INCLUDING ALL);" statement
postgres=#
CREATE TABLE
postgres=# INSERT INTO foo VALUES(10,20);
INSERT 0 1
تحاكي هذه الخدعة جداول مؤقتة عالمية جزئيًا وتتيح التحقق من الصحة. الاحتمال الآخر هو استخدام [قالب غلاف البيانات الأجنبي] (https://github.com/okbob/template_fdw)
يمكنك استخدام table Pragma وإنشاء جدول سريع الزوال:
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 على profiler بسيطة لوظائف وإجراءات PLPGSQL. يمكن أن تعمل مع/بدون الوصول إلى الذاكرة المشتركة. يعتمد ذلك على shared_preload_libraries . عند تهيئة PLPGSQL_Check بواسطة shared_preload_libraries ، يمكنه تخصيص ذاكرة مشتركة ، ويتم تخزين ملفات تعريف الوظيفة هناك. عندما لا يمكن لـ PLPGSQL_Check تخصيص الذاكرة المشتركة ، يتم تخزين ملف التعريف في ذاكرة الجلسة.
بسبب التبعيات ، يجب أن تحتوي على plpgsql shared_preload_libraries أولاً
postgres=# show shared_preload_libraries ;
┌──────────────────────────┐
│ shared_preload_libraries │
╞══════════════════════════╡
│ plpgsql,plpgsql_check │
└──────────────────────────┘
(1 row)
ينشط profiler عندما يكون GUC plpgsql_check.profiler قيد التشغيل. لا يتطلب Profiler ذاكرة مشتركة ، ولكن إذا لم يكن هناك ما يكفي من الذاكرة المشتركة ، فإن Profiler محدود فقط في الجلسة النشطة. يمكن تنشيط profiler عن طريق استدعاء وظيفة plpgsql_check_profiler(true) وتعطيلها عن طريق استدعاء نفس الوظيفة مع وسيطة false (أو مع on ، off ).
يجب تهيئة PLPGSQL_Check قبل تنفيذ أي وظيفة PLPGSQL. فقط التهيئة المبكرة تضمن العمل الصحيح من البروفيلر والتتبع. عندما لا تستخدم shared_preloaded_libraries ، يمكنك استخدام load 'plpgsql_check' بدلاً من ذلك.
عند تهيئة PLPGSQL_Check بواسطة shared_preload_libraries ، يتوفر GUC آخر لتكوين كمية الذاكرة المشتركة المستخدمة من قبل profiler: plpgsql_check.profiler_max_shared_chunks . يحدد هذا الحد الأقصى لعدد العبارات التي يمكن تخزينها في الذاكرة المشتركة. لكل وظيفة PLPGSQL (أو الإجراء) ، يتم تقسيم المحتوى بأكمله إلى أجزاء من 30 بيانات. إذا لزم الأمر ، يمكن استخدام أجزاء متعددة لتخزين المحتوى الكامل لوظيفة واحدة. قطعة واحدة هي 1704 بايت. القيمة الافتراضية لهذا GUC هي 15000 ، والتي يجب أن تكون كافية للمشاريع الكبيرة التي تحتوي على مئات الآلاف من العبارات في PLPGSQL ، وسوف تستهلك حوالي 24 ميجابايت من الذاكرة. إذا لم يكن مشروعك يتطلب عددًا كبيرًا من القطع ، فيمكنك تعيين هذه المعلمة على رقم أصغر من أجل تقليل استخدام الذاكرة. الحد الأدنى للقيمة هو 50 (والتي يجب أن تستهلك حوالي 83 كيلو بايت من الذاكرة) ، والحد الأقصى القيمة هي 100000 (والتي يجب أن تستهلك حوالي 163 ميجابايت من الذاكرة). يتطلب تغيير هذه المعلمة إعادة تشغيل postgreSQL.
سيقوم Profiler أيضًا باسترداد معرف الاستعلام لكل تعليمات تحتوي على تعبير أو عبارة قابلة للتحسين. لاحظ أن هذا يتطلب PG_STAT_STATEMENTS ، أو امتداد آخر من الطرف الثالث) ، ليتم تثبيته. هناك بعض القيود على استرجاع معرف الاستعلام:
الانتباه: يمكن أن يؤدي تحديث الملفات الشخصية المشتركة إلى تقليل الأداء على الخوادم تحت الحمل الأعلى.
يمكن عرض ملف التعريف عن طريق الدالة plpgsql_profiler_function_tb :
postgres=# select lineno, avg_time, source from plpgsql_profiler_function_tb('fx(int)');
┌────────┬──────────┬───────────────────────────────────────────────────────────────────┐
│ lineno │ avg_time │ source │
╞════════╪══════════╪═══════════════════════════════════════════════════════════════════╡
│ 1 │ │ │
│ 2 │ │ declare result int = 0; │
│ 3 │ 0.075 │ begin │
│ 4 │ 0.202 │ for i in 1..$1 loop │
│ 5 │ 0.005 │ select result + i into result; select result + i into result; │
│ 6 │ │ end loop; │
│ 7 │ 0 │ return result; │
│ 8 │ │ end; │
└────────┴──────────┴───────────────────────────────────────────────────────────────────┘
(9 rows)
الأوقات في النتيجة هي في العليا.
يمكن عرض ملف التعريف لكل عبارات (وليس لكل سطر) عن طريق الدالة PLPGSQL_PROFILER_FUNCTION_STATEMENTS_TB:
CREATE OR REPLACE FUNCTION public.fx1(a integer)
RETURNS integer
LANGUAGE plpgsql
1 AS $function$
2 begin
3 if a > 10 then
4 raise notice 'ahoj';
5 return -1;
6 else
7 raise notice 'nazdar';
8 return 1;
9 end if;
10 end;
11 $function$
postgres=# select stmtid, parent_stmtid, parent_note, lineno, exec_stmts, stmtname
from plpgsql_profiler_function_statements_tb('fx1');
┌────────┬───────────────┬─────────────┬────────┬────────────┬─────────────────┐
│ stmtid │ parent_stmtid │ parent_note │ lineno │ exec_stmts │ stmtname │
╞════════╪═══════════════╪═════════════╪════════╪════════════╪═════════════════╡
│ 0 │ ∅ │ ∅ │ 2 │ 0 │ statement block │
│ 1 │ 0 │ body │ 3 │ 0 │ IF │
│ 2 │ 1 │ then body │ 4 │ 0 │ RAISE │
│ 3 │ 1 │ then body │ 5 │ 0 │ RETURN │
│ 4 │ 1 │ else body │ 7 │ 0 │ RAISE │
│ 5 │ 1 │ else body │ 8 │ 0 │ RETURN │
└────────┴───────────────┴─────────────┴────────┴────────────┴─────────────────┘
(6 rows)
يمكن عرض جميع ملفات التعريف المخزنة عن طريق استدعاء وظيفة plpgsql_profiler_functions_all :
postgres=# select * from plpgsql_profiler_functions_all();
┌───────────────────────┬────────────┬────────────┬──────────┬─────────────┬──────────┬──────────┐
│ funcoid │ exec_count │ total_time │ avg_time │ stddev_time │ min_time │ max_time │
╞═══════════════════════╪════════════╪════════════╪══════════╪═════════════╪══════════╪══════════╡
│ fxx(double precision) │ 1 │ 0.01 │ 0.01 │ 0.00 │ 0.01 │ 0.01 │
└───────────────────────┴────────────┴────────────┴──────────┴─────────────┴──────────┴──────────┘
(1 row)
هناك وظيفتان لتنظيف الملفات الشخصية المخزنة: plpgsql_profiler_reset_all() و plpgsql_profiler_reset(regprocedure) .
يوفر PLPGSQL_Check وظيفتين:
plpgsql_coverage_statements(name)plpgsql_coverage_branches(name)هناك PLPGSQL Profiler أخرى جيدة جدًا - https://github.com/glynastill/plprofiler
تم تصميم امتدادي ليكون بسيطًا للاستخدام والعملي. لا شيء أكثر أو أقل.
Plprofiler أكثر تعقيدًا. يقوم بإنشاء الرسوم البيانية للمكالمات ومن هذا الرسم البياني ، يمكنه إنشاء رسم بياني لهب لأوقات التنفيذ.
يمكن استخدام كلا الامتدادات مع ميزة PostgreSQL المدمجة - وظائف التتبع.
set track_functions to 'pl';
...
select * from pg_stat_user_functions;
يوفر PLPGSQL_Check إمكانية تتبع - في هذا الوضع ، يمكنك رؤية إشعارات في وظائف البدء أو النهاية (terse والشفرة الافتراضية) والبيانات البدء أو النهائي (Verbose Verbosity). من أجل الافتراضي والشفرة ، يتم عرض محتوى وسيطات الوظائف. يتم عرض محتوى المتغيرات ذات الصلة عندما يكون الارتفاع مطوّلة.
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 ->> start of inline_code_block (Oid=0)
NOTICE: #2 ->> start of function fx(integer,integer,date,text) (Oid=16405)
NOTICE: #2 call by inline_code_block line 1 at PERFORM
NOTICE: #2 "a" => '10', "b" => null, "c" => '2020-08-03', "d" => 'stěhule'
NOTICE: #4 ->> start of function fx(integer) (Oid=16404)
NOTICE: #4 call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: #4 "a" => '10'
NOTICE: #4 <<- end of function fx (elapsed time=0.098 ms)
NOTICE: #2 <<- end of function fx (elapsed time=0.399 ms)
NOTICE: #0 <<- end of block (elapsed time=0.754 ms)
الرقم بعد # هو عداد إطار التنفيذ (يرتبط هذا الرقم بعمق مكدس سياق الخطأ). يسمح للابتعاد عن الوظيفة ونهاية الوظيفة.
يتم تمكين التتبع عن طريق تعيين plpgsql_check.tracer إلى on . الانتباه - تمكين هذا السلوك له تأثير سلبي كبير على الأداء (على عكس البروفيلر). يمكنك تعيين مستوى للإخراج المستخدم بواسطة Tracer plpgsql_check.tracer_errlevel (افتراضي هو notice ). يقتصر محتوى الإخراج على الطول المحدد بواسطة plpgsql_check.tracer_variable_max_length CONFIGURITION. يمكن تنشيط التتبع عن طريق استدعاء وظيفة plpgsql_check_tracer(true) وتعطيله عن طريق off نفس الوظيفة مع وسيطة false (أو مع الحرفيين ، on ).
أولاً ، يجب تمكين استخدام Tracer بشكل صريح بواسطة SuperUser عن طريق تعيين set plpgsql_check.enable_tracer to on; أو plpgsql_check.enable_tracer to on في postgresql.conf . هذه حماية أمنية. يعرض التتبع محتوى متغيرات PLPGSQL ، ثم يمكن عرض بعض المعلومات الحساسة للأمن على مستخدم غير محظوظ (عندما يقوم بتشغيل وظيفة Security Definer). ثانياً ، يجب تحميل تمديد plpgsql_check . يمكن القيام بذلك عن طريق تنفيذ بعض وظيفة plpgsql_check أو بشكل صريح عن طريق load 'plpgsql_check'; . يمكنك استخدام خيار التكوين shared_preload_libraries أو local_preload_libraries أو session_preload_libraries .
في وضع المطوّل terse ، يتم تقليل الإخراج:
postgres=# set plpgsql_check.tracer_verbosity TO terse;
SET
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 start of inline code block (oid=0)
NOTICE: #2 start of fx (oid=16405)
NOTICE: #4 start of fx (oid=16404)
NOTICE: #4 end of fx
NOTICE: #2 end of fx
NOTICE: #0 end of inline code block
في وضع المطالبة ، يمتد الإخراج حول تفاصيل العبارة:
postgres=# do $$ begin perform fx(10,null, 'now', e'stěhule'); end; $$;
NOTICE: #0 ->> start of block inline_code_block (oid=0)
NOTICE: #0.1 1 --> start of PERFORM
NOTICE: #2 ->> start of function fx(integer,integer,date,text) (oid=16405)
NOTICE: #2 call by inline_code_block line 1 at PERFORM
NOTICE: #2 "a" => '10', "b" => null, "c" => '2020-08-04', "d" => 'stěhule'
NOTICE: #2.1 1 --> start of PERFORM
NOTICE: #2.1 "a" => '10'
NOTICE: #4 ->> start of function fx(integer) (oid=16404)
NOTICE: #4 call by fx(integer,integer,date,text) line 1 at PERFORM
NOTICE: #4 "a" => '10'
NOTICE: #4.1 6 --> start of assignment
NOTICE: #4.1 "a" => '10', "b" => '20'
NOTICE: #4.1 <-- end of assignment (elapsed time=0.076 ms)
NOTICE: #4.1 "res" => '130'
NOTICE: #4.2 7 --> start of RETURN
NOTICE: #4.2 "res" => '130'
NOTICE: #4.2 <-- end of RETURN (elapsed time=0.054 ms)
NOTICE: #4 <<- end of function fx (elapsed time=0.373 ms)
NOTICE: #2.1 <-- end of PERFORM (elapsed time=0.589 ms)
NOTICE: #2 <<- end of function fx (elapsed time=0.727 ms)
NOTICE: #0.1 <-- end of PERFORM (elapsed time=1.147 ms)
NOTICE: #0 <<- end of block (elapsed time=1.286 ms)
ميزة خاصة من Tracer هي تتبع بيان ASSERT عند on plpgsql_check.trace_assert . عندما يكون plpgsql_check.trace_assert_verbosity DEFAULT ، يتم عرض جميع متغيرات الوظيفة أو الإجراء عندما يكون التعبير التأكيد غير صحيح. عندما يكون هذا التكوين 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
يمكن أن تظهر التتبع استخدام معرف المخزن المؤقت Subtransaction ( nxids ). رقم tnl المعروض هو رقم مستوى تعشيش المعاملات (بالنسبة إلى PLPGSQL ، يعتمد على أعماق الكتل مع معالجات الاستثناء).
مؤشرات PLPGSQL هي مجرد أسماء من المؤشرات SQL. لا يتم ربط دورة حياة مؤشرات SQL مع نطاق متغير مؤشر PLPGSQL ذي الصلة. إن مؤشرات SQL مرتبطة بالذات في نهاية المعاملة ، ولكن بالنسبة للمعاملات الطويلة والكثير من المؤشرات المفتوحة ، قد يكون الوقت قد فات. من الأفضل إغلاق المؤشر بشكل صريح عندما لا يكون المؤشر ضروريًا (ببيان إغلاق). بدونها ، تكون مشكلات الذاكرة المهمة ممكنة.
عندما حاول البيان المفتوح استخدام المؤشر الذي لم يتم إغلاقه بعد ، يتم رفع التحذير. يمكن تعطيل هذه الميزة عن طريق تعيين plpgsql_check.cursors_leaks to off . هذا الشيك غير نشط ، عندما يتم استدعاء الروتين بشكل متكرر
يمكن فحص المؤشرات غير المغلقة على الفور عند الانتهاء من الوظيفة. يتم تعطيل هذا الشيك افتراضيًا ، ويجب تمكينه بواسطة plpgsql_check.strict_cursors_leaks to on .
تم الإبلاغ عن أي مؤشر غير مغرس مرة واحدة.
إذا كنت تستخدم plugin_debugger (plpgsql debugger) مع plpgsql_check ، فيجب أن يتم تهيئة plpgsql_check بعد plugin_debugger (لأن plugin_debugger لا يدعم مشاركة API من PL/PGSQL). على سبيل المثال ( postgresql.conf ):
shared_preload_libraries = 'plugin_debugger,plpgsql,plpgsql_check'
يطبع التتبع محتوى المتغيرات أو وسيطات الوظائف. بالنسبة لوظيفة الأمن المقررة ، يمكن لهذا المحتوى أن يحتفظ ببيانات حساسة للأمان. هذا هو السبب في تعطيل التتبع افتراضيًا ويجب تمكينه فقط مع حقوق المستخدم Super plpgsql_check.enable_tracer .
يمكنك تكوين سلوك PLPGSQL_CHECK داخل وظيفة محددة مع وظيفة "pragma". هذا هو تشبيه لغة PL/SQL أو ADA لميزة Pragma. لا يدعم 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 في جزء من Top Block Sets على مستوى الوظيفة أيضًا.
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 هذا يخرج القيمة الحالية (على سبيل المثال ، ممكّن other_warnings)
enable:check ، enable:tracer ، enable:other_warnings ، enable:performance_warnings ، enable:extra_warnings ، enable:security_warnings
disable:check ، disable:tracer ، disable:other_warnings ، disable:performance_warnings ، disable:extra_warnings ، disable:security_warnings يمكن استخدام هذا لتعطيل تلميح في العودة من دالة أي شيء. فقط ضع البراغما قبل بيان العودة.
type:varname typename أو type:varname (fieldname type, ...) - تعيين نوع إلى متغير نوع السجل
table: name (column_name type, ...) أو table: name (like tablename) - إنشاء جدول مؤقت سريع الزوال (إذا كنت تريد تحديد المخطط ، فإن مخطط pg_temp فقط مسموح به.
sequence: name - إنشاء تسلسل مؤقت سريع الزوال
assert-schema: varname - تأكيد وقت الشيك - تأكد من أن المخطط المحدد حسب المتغير صالح
assert-table: [ varname_schema, ] , varname - تأكد من أن اسم الجدول المحدد بواسطة المتغيرات (عن طريق تتبع ثابت) صالح
assert-column: [varname_schema, ], varname_table , varname - تأكد من أن العمود spified بواسطة المتغيرات صالح
Pragmas enable:tracer disable:tracer النشطين ل postgres 12 والأعلى
لا يدعم PLPGSQL_Check التحديث (من PLPGSQL_CHECK). يجب عليك إسقاط هذا قبل تثبيت إصدار جديد من هذا الامتداد.
تحتاج إلى بيئة تطوير لتمديدات postgresql:
make clean
make install
نتيجة:
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 clean
rm -f plpgsql_check.so libplpgsql_check.a libplpgsql_check.pc
rm -f plpgsql_check.o
rm -rf results/ regression.diffs regression.out tmp_check/ log/
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 all
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -I. -I./ -I/usr/local/pgsql/include/server -I/usr/local/pgsql/include/internal -D_GNU_SOURCE -c -o plpgsql_check.o plpgsql_check.c
clang -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/pl/plpgsql/src -shared -o plpgsql_check.so plpgsql_check.o -L/usr/local/pgsql/lib -Wl,--as-needed -Wl,-rpath,'/usr/local/pgsql/lib',--enable-new-dtags
[pavel@localhost plpgsql_check]$ su root
Password: *******
[root@localhost plpgsql_check]# make USE_PGXS=1 install
/usr/bin/mkdir -p '/usr/local/pgsql/lib'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/mkdir -p '/usr/local/pgsql/share/extension'
/usr/bin/install -c -m 755 plpgsql_check.so '/usr/local/pgsql/lib/plpgsql_check.so'
/usr/bin/install -c -m 644 plpgsql_check.control '/usr/local/pgsql/share/extension/'
/usr/bin/install -c -m 644 plpgsql_check--0.9.sql '/usr/local/pgsql/share/extension/'
[root@localhost plpgsql_check]# exit
[pavel@localhost plpgsql_check]$ make USE_PGXS=1 installcheck
/usr/local/pgsql/lib/pgxs/src/makefiles/../../src/test/regress/pg_regress --inputdir=./ --psqldir='/usr/local/pgsql/bin' --dbname=pl_regression --load-language=plpgsql --dbname=contrib_regression plpgsql_check_passive plpgsql_check_active plpgsql_check_active-9.5
(using postmaster on Unix socket, default port)
============== dropping database "contrib_regression" ==============
DROP DATABASE
============== creating database "contrib_regression" ==============
CREATE DATABASE
ALTER DATABASE
============== installing plpgsql ==============
CREATE LANGUAGE
============== running regression test queries ==============
test plpgsql_check_passive ... ok
test plpgsql_check_active ... ok
test plpgsql_check_active-9.5 ... ok
=====================
All 3 tests passed.
=====================
في بعض الأحيان ، قد يتطلب التجميع الناجح حزمة libicu -dev (postgresql 10 والأعلى - عندما تم تجميع PG بدعم وحدة العناية المركزة)
sudo apt install libicu-dev
يمكنك التحقق من مكتبات DLL المسبقة http://okbob.blogspot.cz/2015/02/plpgsqlcheck-is-available-for-microsoft.html ، http://okbob.blogspot.com/2023/10/compiled-plpgsqlpgsqlcl.
أو تجميعها بالذات:
plpgsql_check.dll إلى PostgreSQL14libplpgsql_check.control و plpgsql_check--2.1.sql PostgreSQL14shareextension meson setup buildcd buildninjasudo ninja installالتجميع ضد postgresql 10 يتطلب libicu!
حقوق الطبع والنشر (ج) بافيل ستيهول ([email protected])
يتم منح الإذن بموجب هذا ، مجانًا ، لأي شخص يحصل على نسخة من هذا البرنامج وملفات الوثائق المرتبطة به ("البرنامج") ، للتعامل في البرنامج دون تقييد ، بما في ذلك على سبيل المثال لا الحصر حقوق استخدام الأشخاص ونسخها ودمجها ودمجها وتوزيعها وتوزيعها على ما يلي:
يجب إدراج إشعار حقوق الطبع والنشر أعلاه وإشعار الإذن هذا في جميع النسخ أو الأجزاء الكبيرة من البرنامج.
يتم توفير البرنامج "كما هو" ، دون أي ضمان من أي نوع ، صريح أو ضمني ، بما في ذلك على سبيل المثال لا الحصر ضمانات القابلية للتسويق واللياقة لغرض معين وعدم الانفجار. لا يجوز بأي حال من الأحوال أن يكون المؤلفون أو حاملي حقوق الطبع والنشر مسؤولاً عن أي مطالبة أو أضرار أو مسؤولية أخرى ، سواء في إجراء عقد أو ضرر أو غير ذلك ، ناشئة عن أو خارج البرنامج أو الاستخدام أو غيرها من المعاملات في البرنامج.
إذا أعجبك ذلك ، فأرسل بطاقة بريدية للعنوان
Pavel Stehule
Skalice 12
256 01 Benesov u Prahy
Czech Republic
أدعو أي أسئلة ، تعليقات ، تقارير الأخطاء ، تصحيحات على عنوان البريد [email protected]