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 кода и получать доступ к полному стеку вызовов, включая значения переменных. Поэтому не рекомендуется к использованию в промышленной среде.
Установка#
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'
Конфигурационные параметры расширения#
Параметр |
Значение по умолчанию |
Описание |
|---|---|---|
|
|
Выполнение проверок, указанных в ASSERT-инструкциях |
|
|
Вывод предупреждения о совместимости |
|
|
Включение функциональности трассировщика |
|
|
Остановка работы |
|
|
Выбор режима расширенной проверки:
|
|
|
Фоновое профилирование функции |
|
15000 |
Максимальное количество выражений в общей памяти |
|
|
Изменение формата вывода для регрессионного тестирования |
|
|
Вывод дополнительных предупреждений (кроме предупреждений о производительности) |
|
|
Вывод всех предупреждений (кроме предупреждений о производительности) |
|
|
Вывод предупреждений о производительности |
|
|
Отслеживание ASSERT-оператора |
|
|
Детализация ASSERT-инструкции ( |
|
|
Включение функции трассировки |
|
|
Установка уровня сообщения трассировщика ( |
|
|
Детализация вывода трассировщика ( |
Функции расширения#
Функции расширения (с аргументами и возвращаемыми значениями можно ознакомиться по ссылке https://github.com/okbob/plpgsql_check):
Имя функции |
Описание |
|---|---|
|
Обнаружение зависимостей для функции |
|
Расширенная проверка (статический анализ кода) функции с выводом в виде форматированного текста |
|
Расширенная проверка (статический анализ кода) функции с выводом в виде таблицы с несколькими столбцами |
|
Реализация функции |
|
Проверка/установка статуса профайлера |
|
Проверка/установка статуса трассировщика |
|
Расчет показателя покрытия профилировщиком вариантов использования функции |
|
Расчет показателя покрытия профилировщиком строк с выражениями в функции |
|
Отображение собранного профиля функции в виде таблицы |
|
Отображение собранного профиля функции в виде таблицы |
|
Отображение собранных профилей всех функций в виде таблицы |
|
Отображение собранных профилей всех функций в виде таблицы |
|
Очистка сгенерированного функцией |
|
Очистка статистики, собранной профайлером и относящейся к переданной в аргументе функции |
|
Очистка всей статистики, собранной профайлером |
|
Обнаружение зависимостей для функции |
Возможно установить уровни предупреждений через следующие аргументы функций:
Аргумент |
значение |
Описание |
|---|---|---|
Имя функции |
|
Различные функции в СУБД Pangolin могут быть заданы с помощью Если известен Возможно альтернативное использование только имени, когда имя функции уникально - например |
Аргументы функций#
Аргумент |
Тип значения |
значение по умолчанию |
Описание |
|---|---|---|---|
|
|
0 |
OID отношения, связанного с триггерной функцией. Если необходимо проверить какие-либо триггерные функции. Передается таблица, в которой работает триггер |
|
boolean |
|
остановка при первой ошибке (чтобы избежать разрастания отчета из-за обилия ошибок) |
|
|
|
отображает предупреждения, такие как разное количество атрибутов в присвоении, переменная перекрывает параметр функции, неиспользуемые переменные, нежелательное приведение и другое |
|
|
|
показывает предупреждения о пропущенной команде |
|
|
|
Предупреждения, связанные с производительностью, включая использование типов с модификаторами, явные или неявные преобразования типов, а также неявные приведения в условиях |
|
|
|
проверки безопасности, такие как обнаружение уязвимости SQL-инъекции |
|
|
|
проверки, связанные с совместимостью, такие как устаревшая явная настройка внутренних имен курсоров в ссылочных курсорах или курсорных переменных |
|
|
|
фактический тип, который будет использоваться при тестировании типа |
|
|
|
фактический тип, который будет использоваться при тестировании типа |
|
|
|
фактический тип, который будет использоваться при тестировании типа |
|
- |
|
фактический тип, который будет использоваться при тестировании типа |
|
- |
|
фактический тип, который будет использоваться при тестировании типа |
|
|
|
отключает все предупреждения (игнорируются все |
|
|
|
включает все предупреждения (игнорируются все остальные |
|
имя таблицы |
|
имена новых переходных таблиц. Параметр необходим, когда переходные таблицы используются в триггерных функциях |
|
имя таблицы |
|
имена старых переходных таблиц. Параметр необходим, когда переходные таблицы используются в триггерных функциях |
|
|
|
в случае |
|
|
|
в случае |
Управление#
Решение не оказывает влияния на кластерную архитектуру СУБД 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);Результат:
Примечание
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.