plpgsql_check. Средство контроля plpgsql
Версия: 2.5.
В исходном дистрибутиве установлено по умолчанию: нет.
Связанные компоненты: plpgsql.
Схема размещения:
ext
.
Расширение plpgsql_check
— это полноценный статический анализатор кода plpgsql
для PostgreSQL, предоставляющий набор функций для анализа и профилирования функций и процедур, написанных на языке PL/pgSQL.
Оно выполняет проверку встроенных SQL-запросов, выявляя ошибки, которые обычно не обнаруживаются при создании процедур с помощью команды CREATE FUNCTION/PROCEDURE
. Также можно настраивать уровни многих предупреждений и подсказок для различных проверок, а также использовать директиву PRAGMA
, чтобы управлять отображением сообщений — скрывать уже известные проблемы или оставлять напоминания для дальнейшего анализа.
Функциональные возможности расширения:
- проверка соответствия типов и полей таблиц, представлений и других объектов, используемых внутри SQL кода процедур/функций;
- проверка передачи параметров в функцию/процедуру в соответствии с их объявленными типами;
- идентификация неиспользуемых переменных и аргументов функций, а также возвращаемые значения, остающиеся неизменными;
- частичное обнаружение «мертвого кода» (код после команды
RETURN
) и пропущенных командRETURN
в функции (распространено после обработки исключений, сложной логики); - выявление нежелательных скрытых затрат, приводящих к проблемам с производительностью, например, неиспользуемых индексов;
- возможность собрать список объектов (таблицы, представления) и другие функции, к которым обращается функция, чтобы дать представление об ее зависимостях;
- возможность проверки инструкций
EXECUTE
на наличие уязвимости SQL-инъекции.
Помимо статического анализа расширение предоставляет возможность динамического анализа кода с отслеживанием выполнения функций (в том числе триггерных) и процедур на языке pl/pgsql
.
Доработка
Не производилась.
Ограничения
Для корректной работы plpgsql_check
необходимо наличие расширения plpgsql.
Расширение поддерживается СУБД Pangolin не ниже 5.X.X версии.
Использование расширения в пассивном режиме может влиять на производительность при сборе и хранении в статистике данных, переданных в параметрах функции, поэтому пассивный режим работы расширения не рекомендован к использованию в промышленной среде.
Также решение затрагивает аспекты безопасности хранения данных, так как позволяет перехватывать выполнение plpgsql
кода и получать доступ к полному стеку вызовов, включая значения переменных. Поэтому не рекомендуется к использованию в промышленной среде.
Установка
- SberLinux, РЕД ОС, CentOS
- Red Hat Enterprise Linux
- Astra Linux
- Альт СП
sudo dnf install pangolin-plpgsql-check-{version_component}-{OS}.x86_64.rpm
sudo yum install pangolin-plpgsql-check-{version_component}-{OS}.x86_64.rpm
sudo apt install pangolin-plpgsql-check-{version_component}_amd64.deb
sudo apt-get install pangolin-plpgsql-check-{version_component}-{OS}.x86_64.rpm
Пример заполненной команды:
cd distributive
sudo apt-get -y install pangolin-plpgsql-check-2.5-sberlinux8.x86_64.rpm
Для использования расширения на развернутой СУБД необходимо установить его в целевую БД:
CREATE EXTENSION plpgsql_check SCHEMA ext;
Настройка
Расширение может работать в нескольких режимах:
- активный (статический анализ кода) — расширение никак не настраивается и не работает в фоне. Вся информация о функциях получается путем ручного вызова функций;
- пассивный — анализ и проверка кода выполняются автоматически перед запуском отлаживаемой функции. Для этого необходимо добавить
plpgsql_check
вshared_preload_libraries
; - профилирования нагрузки — расширение собирает статистику о запусках и работе функций, а также выражений в теле функций. Статистика хранится в памяти сеанса, при добавлении библиотеки расширения в
shared_preload_libraries
используется общая разделяемая память и статистика хранится в общей памяти СУБД; - трассировки — каждый вызов любой
plpgsql
-функции сопровождается подробной информацией о выполняемой в текущий момент строке кода функции.
Расширение может быть добавлено в shared_preload_libraries
:
shared_preload_libraries = 'plpgsql_check'
В случае, если расширение используется совместно с расширением pldebugger, библиотека расширения должна быть указана после pldebugger
при перечислении в shared_preload_libraries
:
shared_preload_libraries = 'plugin_debugger,plpgsql_check'
Конфигурационные параметры расширения
Конфигурационные параметры plpgsql_check
Параметр | Значение по умолчанию | Описание |
plpgsql.check_asserts | on | Выполнение проверок, указанных в ASSERT-инструкциях |
plpgsql_check.compatibility_warnings | off | Вывод предупреждения о совместимости |
plpgsql_check.enable_tracer | off | Включение функциональности трассировщика |
plpgsql_check.fatal_errors | on | Остановка работы plpgsql при возникновении ошибки |
plpgsql_check.mode | by_function | Выбор режима расширенной проверки: - disabled для отключения; - by_function для ручного запуска функций проверки; - fresh_start для первого запуска проверяемой функции; - every_start для каждого запуска проверяемой функции |
plpgsql_check.profiler | off | Фоновое профилирование функции |
plpgsql_check.profiler_max_shared_chunks | 15000 | Максимальное количество выражений в общей памяти |
plpgsql_check.regress_test_mode | off | Изменение формата вывода для регрессионного тестирования |
plpgsql_check.show_nonperformance_extra_warnings | off | Вывод дополнительных предупреждений (кроме предупреждений о производительности) |
plpgsql_check.show_nonperformance_warnings | off | Вывод всех предупреждений (кроме предупреждений о производительности) |
plpgsql_check.show_performance_warnings | off | Вывод предупреждений о производительности |
plpgsql_check.trace_assert | off | Отслеживание ASSERT-оператора |
plpgsql_check.trace_assert_verbosity | default | Детализация ASSERT-инструкции (terse/default/verbose ) |
plpgsql_check.tracer | off | Включение функции трассировки |
plpgsql_check.tracer_errlevel | notice | Установка уровня сообщения трассировщика (log/notice/info/debug/debug1/debug2/debug3/debug4/debug5 ) |
plpgsql_check.tracer_verbosity | default | Детализация вывода трассировщика (terse/default/verbose ) |
Функции расширения
Функции расширения (с аргументами и возвращаемыми значениями можно ознакомиться по ссылке https://github.com/okbob/plpgsql_check):
Функции plpgsql_check
Имя функции | Описание |
__plpgsql_show_dependency_tb() | Обнаружение зависимостей для функции |
plpgsql_check_function() | Расширенная проверка (статический анализ кода) функции с выводом в виде форматированного текста |
plpgsql_check_function_tb() | Расширенная проверка (статический анализ кода) функции с выводом в виде таблицы с несколькими столбцами |
plpgsql_check_pragma() | Реализация функции pragma . Существует два разных варианта использования: для статического анализа с помощью plpgsql_check , где аргументы считываются из дерева синтаксического анализа и для управления трассировкой кода во время выполнения (аргументы обрабатываются аналогичным для variadic text образом) |
plpgsql_check_profiler() | Проверка/установка статуса профайлера |
plpgsql_check_tracer() | Проверка/установка статуса трассировщика |
plpgsql_coverage_branches() | Расчет показателя покрытия профилировщиком вариантов использования функции |
plpgsql_coverage_statements() | Расчет показателя покрытия профилировщиком строк с выражениями в функции |
plpgsql_profiler_function_statements_tb() | Отображение собранного профиля функции в виде таблицы |
plpgsql_profiler_function_tb() | Отображение собранного профиля функции в виде таблицы |
plpgsql_profiler_functions_all() | Отображение собранных профилей всех функций в виде таблицы |
plpgsql_profiler_install_fake_queryid_hook() | Отображение собранных профилей всех функций в виде таблицы |
plpgsql_profiler_remove_fake_queryid_hook() | Очистка сгенерированного функцией plpgsql_profiler_install_fake_queryid_hook() идентификатора запроса и возврат предыдущего значения |
plpgsql_profiler_reset() | Очистка статистики, собранной профайлером и относящейся к переданной в аргументе функции |
plpgsql_profiler_reset_all() | Очистка всей статистики, собранной профайлером |
plpgsql_show_dependency_tb() | Обнаружение зависимостей для функции |
Возможно установить уровни предупреждений через следующие аргументы функций:
Обязательные аргументы функций
Аргумент | значение | Описание |
Имя функции | funcoid имя функции определение функции | Различные функции в СУБД Pangolin могут быть заданы с помощью OID , по имени или по определению. Если известен OID или полное определение функции, возможно использовать тип regprocedure в качестве параметра следующим образом: fx()::regprocedure или 16799::regprocedure . Возможно альтернативное использование только имени, когда имя функции уникально - например fx . Если имя функции не уникально (функция перегружена с разным набором аргументов) или функции с таким именем не существует — это приведет к ошибке |
Аргументы функций
Дополнительные аргументы функций
Аргумент | Тип значения | значение по умолчанию | Описание |
relid | int | 0 | OID отношения, связанного с триггерной функцией. Если необходимо проверить какие-либо триггерные функции. Передается таблица, в которой работает триггер |
fatal_errors | boolean | true | остановка при первой ошибке (чтобы избежать разрастания отчета из-за обилия ошибок) |
other_warnings | boolean | true | отображает предупреждения, такие как разное количество атрибутов в присвоении, переменная перекрывает параметр функции, неиспользуемые переменные, нежелательное приведение и другое |
extra_warnings | boolean | true | показывает предупреждения о пропущенной команде RETURN , необъявленных переменных, неиспользуемом коде, неиспользуемых параметрах функции, неизменяемых переменных и другое |
performance_warnings | boolean | false | Предупреждения, связанные с производительностью, включая использование типов с модификаторами, явные или неявные преобразования типов, а также неявные приведения в условиях WHERE , которые могут помешать использованию индексов |
security_warnings | boolean | false | проверки безопасности, такие как обнаружение уязвимости SQL-инъекции |
compatibility_warnings | boolean | false | проверки, связанные с совместимостью, такие как устаревшая явная настройка внутренних имен курсоров в ссылочных курсорах или курсорных переменных |
anyelementtype | regtype | int | фактический тип, который будет использоваться при тестировании типа anyelement |
anyenumtype | regtype | - | фактический тип, который будет использоваться при тестировании типа anyenum |
anyrangetype | regtype | int4range | фактический тип, который будет использоваться при тестировании типа anyrange |
anycompatibletype | - | int | фактический тип, который будет использоваться при тестировании типа anycompatible |
anycompatiblerangetype | - | int4range | фактический тип, который будет использоваться при тестировании типа anycompatible range |
without_warnings | boolean | false | отключает все предупреждения (игнорируются все xxxx_warnings параметры, быстрое отключение) |
all_warnings | boolean | false | включает все предупреждения (игнорируются все остальные xxx_warnings параметры, быстрое включение) |
newtable | имя таблицы | NULL | имена новых переходных таблиц. Параметр необходим, когда переходные таблицы используются в триггерных функциях |
oldtable | имя таблицы | NULL | имена старых переходных таблиц. Параметр необходим, когда переходные таблицы используются в триггерных функциях |
use_incomment_options | boolean | true | в случае true , активируются in-comment опции |
incomment_options_usage_warning | boolean | false | в случае true предупреждение будет срабатывать когда используются in-comment опции |
Управление
Решение не оказывает влияния на кластерную архитектуру СУБД Pangolin, включая потоковую и логическую репликацию, процессы резервного копирования, интеграцию с СРК, а также сохраняет полную обратную совместимость с текущей версией СУБД Pangolin.
Также оно не затрагивает систему мониторинга: не требуется добавление новых метрик или их интеграция с системой WatchDog, кроме того не требуется внедрение новых точек трассировки в системные средства наблюдения. Влияние на другие модули и компоненты отсутствует.
Поскольку решение предоставляет возможность перехвата исполнения функций и доступа к полному стеку выполнения plpgsql
кода, необходимо обеспечить аудит вызовов функций из задействованных расширений.
Использование модуля
Настройка и демонстрация отладки и анализа функций PL/pgSQL с помощью расширения plpgsql_check
-
Добавьте в конфигурационный параметр
shared_preload_libraries
библиотеки расширений в следующей последовательности:shared_preload_libraries = 'plugin_debugger,plpgsql_check'
-
Для применения параметров из предыдущего действия перезагрузите СУБД.
-
В демонстрационной БД выполните установку расширений:
CREATE EXTENSION plpgsql_check SCHEMA ext;
CREATE EXTENSION pldbgapi SCHEMA ext; -
При отсутствии установите на клиент pgAdmin4.
-
Создайте в демонстрационной БД функции:
CREATE OR REPLACE FUNCTION test_raise (test_int integer) RETURNS int
AS $$
DECLARE
res int;
i int;
BEGIN
res := 1 / test_int;
for i in 1..4
LOOP
RAISE NOTICE 'test %', test_int;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION get_count (tabname text, div integer) RETURNS bigint
AS $$ DECLARE
cmd text;
test_result int;
retval bigint;
aux int := plpgsql_check_pragma('enable:extra_warnings');
BEGIN
cmd := 'SELECT COUNT(*) FROM '
|| quote_ident(tabname);
EXECUTE cmd INTO retval;
select test_raise(retval::int) into test_result;
select test_raise(div) into test_result;
RETURN retval;
END;
$$ LANGUAGE plpgsql STABLE;
Выполнение расширенной проверки функций в «активном» режиме
-
В
psql
выполните функцию:select * from plpgsql_check_function_tb('get_count(text, int)');
Результат:
functionid | lineno | statement | sqlstate | message | detail | hint | level | position | query | context
------------------+--------+-----------+----------+-----------------------------------+--------+------+---------------+----------+-------+---------
public.get_count | 3 | DECLARE | 00000 | never read variable "test_result" | | | warning extra | | |
(1 row) -
Измените функцию
get_count
, исправив в ней ошибку:CREATE OR REPLACE FUNCTION get_count (tabname text, div integer) RETURNS bigint
AS $$ DECLARE
cmd text;
test_result int;
retval bigint;
aux int := plpgsql_check_pragma('enable:extra_warnings');
BEGIN
cmd := 'SELECT COUNT(*) FROM '
|| quote_ident(tabname);
EXECUTE cmd INTO retval;
select test_raise(retval::int) into test_result;
RAISE NOTICE 'test_result is %', test_result;
select test_raise(div) into test_result;
RETURN retval;
END;
$$ LANGUAGE plpgsql STABLE; -
Выполните:
select * from plpgsql_check_function_tb('get_count(text, int)');
Результат:
functionid | lineno | statement | sqlstate | message | detail | hint | level | position | query | context
------------+--------+-----------+----------+---------+--------+------+-------+----------+-------+---------
(0 rows)
Выполнение расширенной проверки функций в «пассивном» режиме
-
Добавьте в
postgresql.conf
следующие параметры и выполнитеreload
:plpgsql_check.show_nonperformance_warnings = true
plpgsql_check.show_performance_warnings = true -
Добавьте в код функции директиву
PRAGMA
:CREATE OR REPLACE FUNCTION get_count (tabname text, div integer) RETURNS bigint
AS $$ DECLARE
cmd text;
test_result int;
retval bigint;
aux int := plpgsql_check_pragma('enable:extra_warnings');
BEGIN
PERFORM 'PRAGMA:echo:str @@id @@signature';
cmd := 'SELECT COUNT(*) FROM '
|| quote_ident(tabname);
EXECUTE cmd INTO retval;
select test_raise(retval::int) into test_result;
select test_raise(div) into test_result;
RETURN retval;
END;
$$ LANGUAGE plpgsql STABLE; -
Убедитесь, что код не анализируется в активном режиме:
select * from get_count('pg_class', 2);
Результат:
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test_result is 1
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
get_count
-----------
584
(1 row) -
Добавьте в
postgresql.conf
следующий параметр и выполнитеreload
:plpgsql_check.show_nonperformance_warnings = 'every_start'
-
Выполните функцию:
select * from get_count('pg_class', 2);
Результат:
WARNING: unused variable "i"
WARNING: routine is marked as VOLATILE, should be IMMUTABLE
HINT: When you fix this issue, please, recheck other functions that uses this function.
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test_result is 1
WARNING: unused variable "i"
WARNING: routine is marked as VOLATILE, should be IMMUTABLE
HINT: When you fix this issue, please, recheck other functions that uses this function.
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
get_count
-----------
584
(1 row) -
Верните параметр
plpgsql_check.show_nonperformance_warnings
в значениеby_function
и выполнитеreload
.
Сбор статистики использования функций
-
Выполните функцию:
select * from plpgsql_profiler_functions_all();
Результат: сохраненные профили отсутствуют:
funcoid | exec_count | exec_stmts_err | total_time | avg_time | stddev_time | min_time | max_time
--------+------------+----------------+------------+----------+-------------+----------+----------
(0 rows) -
Выполните функцию:
select * from plpgsql_profiler_function_tb('get_count(text)');
Результат: счетчики пустые:
lineno | stmt_lineno | queryids | cmds_on_row | exec_stmts | exec_stmts_err | total_time | avg_time | max_time | processed_rows | source
--------+-------------+----------+-------------+------------+----------------+------------+----------+----------+----------------+-----------------------------------------------------------------
1 | | | | | | | | | | DECLARE
2 | | | | | | | | | | cmd text;
3 | | | | | | | | | | test_result int;
4 | | | | | | | | | | retval bigint;
5 | | | | | | | | | | aux int := plpgsql_check_pragma('enable:extra_warnings');
6 | | | | | | | | | | BEGIN
7 | | | | | | | | | | cmd := 'SELECT COUNT(*) FROM '
8 | | | | | | | | | | || quote_ident(tabname);
9 | | | | | | | | | | EXECUTE cmd INTO retval;
10 | | | | | | | | | | select test_raise(retval::int) intotest_result;
11 | | | | | | | | | | RAISE NOTICE 'test_result is %', test_result;
12 | | | | | | | | | | select test_raise(div) into test_result;
13 | | | | | | | | | | RETURN retval;
14 | | | | | | | | | | END;
15 | | | | | | | | | |
(15 rows) -
Выполните функцию:
select * from plpgsql_profiler_function_statements_tb('get_count(text, int)');
Результат: счетчики пустые:
stmtid | parent_stmtid | parent_note | block_num | lineno | queryid | exec_stmts | exec_stmts_err | total_time | avg_time | max_time | processed_rows | stmtname
-------+---------------+-------------+-----------+--------+---------+------------+----------------+------------+----------+----------+----------------+-----------------
1 | | | 1 | 6 | | 0 | 0 | 0 | | 0 | 0 | statement block
2 | 1 | body | 1 | 7 | | 0 | 0 | 0 | | 0 | 0 | assignment
3 | 1 | body | 2 | 9 | | 0 | 0 | 0 | | 0 | 0 | EXECUTE
4 | 1 | body | 3 | 10 | | 0 | 0 | 0 | | 0 | 0 | SQL statement
5 | 1 | body | 4 | 11 | | 0 | 0 | 0 | | 0 | 0 | RAISE
6 | 1 | body | 5 | 12 | | 0 | 0 | 0 | | 0 | 0 | SQL statement
7 | 1 | body | 6 | 13 | | 0 | 0 | 0 | | 0 | 0 | RETURN
(7 rows) -
Включите профилирование выполнения функций:
select plpgsql_check_profiler(true);
Результат:
NOTICE: profiler is active
plpgsql_check_profiler
------------------------
t
(1 row) -
Выполните функцию:
select * from get_count('pg_class', 2);
Результат:
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test_result is 1
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
get_count
-----------
584
(1 row) -
Выполните функцию:
select * from plpgsql_profiler_functions_all();
Результат:
funcoid | exec_count | exec_stmts_err | total_time | avg_time | stddev_time | min_time | max_time
------------------------+------------+----------------+------------+----------+-------------+----------+----------
get_count(text,integer) | 1 | 0 | 2.171 | 2.171 | 0 | 2.171 | 2.171
test_raise(integer) | 2 | 0 | 0.366 | 0.183 | 0.13 | 0.053 | 0.313
(2 rows) -
Выполните функцию:
select * from plpgsql_profiler_function_tb('get_count(text, int)');
Результат:
lineno | stmt_lineno | queryids | cmds_on_row | exec_stmts | exec_stmts_err | total_time | avg_time | max_time | processed_rows | source
-------+-------------+----------+-------------+------------+----------------+------------+----------+----------+----------------+-----------------------------------------------------------------
1 | | | | | | | | | | DECLARE
2 | | | | | | | | | | cmd text;
3 | | | | | | | | | | test_result int;
4 | | | | | | | | | | retval bigint;
5 | | | | | | | | | | aux int := plpgsql_check_pragma('enable:extra_warnings');
6 | 6 | | 1 | 1 | 0 | 0.187 | 0.187 | {2.119} | {0} | BEGIN
7 | 7 | | 1 | 1 | 0 | 0.193 | 0.193 | {0.193} | {0} | cmd := 'SELECT COUNT(*) FROM '
8 | | | | | | | | | | || quote_ident(tabname);
9 | 9 | | 1 | 1 | 0 | 1.026 | 1.026 | {1.026} | {0} | EXECUTE cmd INTO retval;
10 | 10 | | 1 | 1 | 0 | 0.578 | 0.578 | {0.578} | {0} | select test_raise(retval::int) into test_result;
11 | 11 | | 1 | 1 | 0 | 0.032 | 0.032 | {0.032} | {0} | RAISE NOTICE 'test_result is %', test_result;
12 | 12 | | 1 | 1 | 0 | 0.103 | 0.103 | {0.103} | {0} | select test_raise(div) into test_result;
13 | 13 | | 1 | 1 | 0 | 0 | 0 | {0} | {0} | RETURN retval;
14 | | | | | | | | | | END;
15 | | | | | | | | | |
(15 rows) -
Выполните функцию:
select * from plpgsql_profiler_function_statements_tb('get_count(text, int)');
Результат:
stmtid | parent_stmtid | parent_note | block_num | lineno | queryid | exec_stmts | exec_stmts_err | total_time | avg_time | max_time | processed_rows | stmtname
-------+---------------+-------------+-----------+--------+---------+------------+----------------+------------+----------+----------+----------------+-----------------
1 | | | 1 | 6 | | 1 | 0 | 0.187 | 0.187 | 2.119 | 0 | statement block
2 | 1 | body | 1 | 7 | | 1 | 0 | 0.193 | 0.193 | 0.193 | 0 | assignment
3 | 1 | body | 2 | 9 | | 1 | 0 | 1.026 | 1.026 | 1.026 | 0 | EXECUTE
4 | 1 | body | 3 | 10 | | 1 | 0 | 0.578 | 0.578 | 0.578 | 0 | SQL statement
5 | 1 | body | 4 | 11 | | 1 | 0 | 0.032 | 0.032 | 0.032 | 0 | RAISE
6 | 1 | body | 5 | 12 | | 1 | 0 | 0.103 | 0.103 | 0.103 | 0 | SQL statement
7 | 1 | body | 6 | 13 | | 1 | 0 | 0 | 0 | 0 | 0 | RETURN
(7 rows) -
Проверьте покрытие собранной статистики:
select * from plpgsql_coverage_statements('get_count(text, int)');
Результат:
plpgsql_coverage_statements
-----------------------------
1
(1 row)Проверьте покрытие ветвлений в функции:
select * from plpgsql_coverage_branches('get_count(text, int)');
Результат:
plpgsql_coverage_branches
---------------------------
1
(1 row) -
Сбросьте статистику:
select plpgsql_profiler_reset('get_count(text, int)');
Результат:
plpgsql_profiler_reset
------------------------
(1 row)
- Сбросьте остальную статистику:
select plpgsql_profiler_reset_all();
Результат:
plpgsql_profiler_reset_all
----------------------------
(1 row)
- Выключите профилирование:
select plpgsql_check_profiler(false);
Результат:
NOTICE: profiler is not active
plpgsql_check_profiler
------------------------
f
(1 row)
Трассировка выполнения функций
-
Добавьте в
postgresql.conf
опциюenable_tracer
и перезагрузите СУБД:plpgsql_check.enable_tracer = on
-
Включите трассировку:
select plpgsql_check_tracer(true);
Результат:
NOTICE: tracer is active
NOTICE: tracer verbosity is default
plpgsql_check_tracer
----------------------
t
(1 row) -
Выполните функцию:
select * from get_count('pg_class', 2);
Результат:
NOTICE: #0 ->> start of function get_count(text,integer) (oid=44282)
NOTICE: #0 "tabname" => 'pg_class', "div" => '2'
NOTICE: #2 ->> start of function test_raise(integer) (oid=44281)
NOTICE: #2 context: SQL statement "select test_raise(retval::int)"
NOTICE: #2 "test_int" => '584'
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: #2 <<- end of function test_raise (elapsed time=0.141 ms)
NOTICE: test_result is 1
NOTICE: #2 ->> start of function test_raise(integer) (oid=44281)
NOTICE: #2 context: SQL statement "select test_raise(div)"
NOTICE: #2 "test_int" => '2'
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: #2 <<- end of function test_raise (elapsed time=0.099 ms)
NOTICE: #0 <<- end of function get_count (elapsed time=0.899 ms)
get_count
-----------
584
(1 row) -
Измените подробности трассировки:
set plpgsql_check.tracer_verbosity TO verbose;
-
Выполните функцию:
select * from get_count('pg_class', 2);
Результат:
Details
NOTICE: #0 ->> start of function get_count(text,integer) (oid=44282)
NOTICE: #0 "tabname" => 'pg_class', "div" => '2'
NOTICE: #0.1 6 --> start of statement block
NOTICE: #0.2 7 --> start of assignment 'SELECT COUNT(*) FROM '
..
NOTICE: #0.2 "tabname" => 'pg_class'
NOTICE: #0.1 <-- end of assignment (elapsed time=0.029 ms)
NOTICE: #0.2 "cmd" => 'SELECT COUNT(*) FROM pg_class'
NOTICE: #0.3 9 --> start of EXECUTE
NOTICE: #0.2 <-- end of EXECUTE (elapsed time=0.359 ms)
NOTICE: #0.4 10 --> start of SQL statement (query='select test_raise(retval::int)')
NOTICE: #0.4 "retval" => '584'
NOTICE: #2 ->> start of function test_raise(integer) (oid=44281)
NOTICE: #2 context: SQL statement "select test_raise(retval::int)"
NOTICE: #2 "test_int" => '584'
NOTICE: #2.1 5 --> start of statement block
NOTICE: #2.2 6 --> start of assignment 1 / test_int
NOTICE: #2.2 "test_int" => '584'
NOTICE: #2.1 <-- end of assignment (elapsed time=0.023 ms)
NOTICE: #2.2 "res" => '0'
NOTICE: #2.3 7 --> start of FOR with integer loop variable
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 584
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.021 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 584
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.017 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 584
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.016 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 584
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.017 ms)
NOTICE: #2.2 <-- end of FOR with integer loop variable (elapsed time=0.122 ms)
NOTICE: #2.5 11 --> start of RETURN (expr='1')
NOTICE: #2.4 <-- end of RETURN (elapsed time=0.011 ms)
NOTICE: #2.5 <-- end of statement block (elapsed time=0.208 ms)
NOTICE: #2 <<- end of function test_raise (elapsed time=0.261 ms)
NOTICE: #0.3 <-- end of SQL statement (elapsed time=0.335 ms)
NOTICE: #0.5 11 --> start of RAISE
NOTICE: test_result is 1
NOTICE: #0.4 <-- end of RAISE (elapsed time=0.015 ms)
NOTICE: #0.6 12 --> start of SQL statement (query='select test_raise(div)')
NOTICE: #0.6 "div" => '2'
NOTICE: #2 ->> start of function test_raise(integer) (oid=44281)
NOTICE: #2 context: SQL statement "select test_raise(div)"
NOTICE: #2 "test_int" => '2'
NOTICE: #2.1 5 --> start of statement block
NOTICE: #2.2 6 --> start of assignment 1 / test_int
NOTICE: #2.2 "test_int" => '2'
NOTICE: #2.1 <-- end of assignment (elapsed time=0.018 ms)
NOTICE: #2.2 "res" => '0'
NOTICE: #2.3 7 --> start of FOR with integer loop variable
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 2
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.020 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 2
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.015 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 2
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.019 ms)
NOTICE: #2.4 9 --> start of RAISE
NOTICE: test 2
NOTICE: #2.3 <-- end of RAISE (elapsed time=0.024 ms)
NOTICE: #2.2 <-- end of FOR with integer loop variable (elapsed time=0.131 ms)
NOTICE: #2.5 11 --> start of RETURN (expr='1')
NOTICE: #2.4 <-- end of RETURN (elapsed time=0.014 ms)
NOTICE: #2.5 <-- end of statement block (elapsed time=0.240 ms)
NOTICE: #2 <<- end of function test_raise (elapsed time=0.290 ms)
NOTICE: #0.5 <-- end of SQL statement (elapsed time=0.348 ms)
NOTICE: #0.7 13 --> start of RETURN
NOTICE: #0.7 "retval" => '584'
NOTICE: #0.6 <-- end of RETURN (elapsed time=0.016 ms)
NOTICE: #0.7 <-- end of statement block (elapsed time=1.224 ms)
NOTICE: #0 <<- end of function get_count (elapsed time=1.324 ms)
get_count
-----------
584
(1 row) -
Выключите трассировку:
select plpgsql_check_tracer(false);
Результат:
NOTICE: tracer is not active
NOTICE: tracer verbosity is verbose
plpgsql_check_tracer
----------------------
f
(1 row) -
Выполните функцию:
select * from get_count('pg_class', 2);
Результат:
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test 584
NOTICE: test_result is 1
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
NOTICE: test 2
get_count
-----------
584
(1 row)
Отключение функциональности
Для отключения данной функциональности необходимо:
- Удалить расширения командой:
DROP EXTENSION plpgsql_check;
- Убрать библиотеку расширения
plpgsql_check
изshared_preload_libraries
. - Перезагрузить сервер СУБД:
pg_ctl restart
Ссылки на документацию разработчика
plpgsql_check: https://github.com/okbob/plpgsql_check.