* Starting PostgreSQL 9.6 database server ...done. ALTER SYSTEM * Restarting PostgreSQL 9.6 database server ...done. PLDEBUGGER API ~~~~~~~~~~~~~~ Установим отладчик: 1| => show shared_preload_libraries; 1| shared_preload_libraries 1| -------------------------- 1| plugin_debugger 1| (1 row) 1| 1| => create extension pldbgapi; 1| CREATE EXTENSION ....................................................................... Создадим функцию для подсчета количества строк в таблице, имя которой передается параметром. При помощи отладчика мы хотим убедиться, что текст команды SELECT формируется корректно: 1| => CREATE FUNCTION get_count (tabname text) RETURNS bigint 1| AS $$DECLARE 1| cmd text; 1| retval bigint; 1| BEGIN 1| cmd := 'SELECT COUNT(*) FROM ' 1| || quote_ident(tabname); 1| 1| EXECUTE cmd INTO retval; 1| RETURN retval; 1| END; 1| $$ LANGUAGE plpgsql STABLE; 1| CREATE FUNCTION ....................................................................... Получим идентификатор сеанса отладки и OID функции: 1| => select pldbg_create_listener() as sessionid, 1| 'get_count'::regproc::oid as funcoid \gset 1| => \echo :sessionid :funcoid 1| 1 34401 ....................................................................... Установим точку останова в начале функции (3-й параметр: -1) и ждем вызова функции из любого (4-й параметр: NULL) другого сеанса: 1| => select * from pldbg_set_global_breakpoint(:sessionid, :funcoid, -1, NULL); 1| pldbg_set_global_breakpoint 1| ----------------------------- 1| t 1| (1 row) 1| 1| => select * from pldbg_wait_for_target(:sessionid); Отладочный сеанс перешел в режим ожидания... ....................................................................... Откроем второй сеанс и вызываем функцию: 2| => select get_count('pg_class'); Выполнение доходит до точки останова и теперь второй сеанс переходит в режим ожидания команд отладчика. Первый сеанс просыпается и переходит в режим управления отладкой. 1| pldbg_wait_for_target 1| ----------------------- 1| 10681 1| (1 row) 1| ....................................................................... В этом режиме можно: * проверить стек вызова (pldbg_get_stack), * перемещаться по функциям в стеке (pldbg_select_frame), * смотреть значения параметров и локальных переменных текущей функции в стеке (pldbg_get_variables), * изменить значения переменных (pldbg_deposit_value), * установить/удалить точки останова (pldbg_set_breakpoint/pldbg_drop_breakpoint), * продолжить выполнение до следующей точки (pldbg_continue), * выполнять пошаговую отладку с заходом внутрь функций (pldbg_step_into), * или выполняя строку целиком (pldbg_step_over), * в конечном итоге, прекратить сеанс отладки (pldbg_abort_target). ....................................................................... Посмотрим, например, значения переменных: 1| => select name, varClass, value, pg_catalog.format_type(dtype, NULL) as dtype 1| from pldbg_get_variables(:sessionid) 1| order by varClass; 1| name | varclass | value | dtype 1| ---------+----------+----------+-------- 1| tabname | A | pg_class | text 1| cmd | L | NULL | text 1| retval | L | NULL | bigint 1| (3 rows) 1| Видим, что параметр функции tabname = 'pg_class', а значения переменных cmd и retval еще не установлены. ....................................................................... Добавим еще одну точку останова на команде EXECUTE (8 строка функции): 1| => select * from pldbg_set_breakpoint(:sessionid, :funcoid, 8); 1| pldbg_set_breakpoint 1| ---------------------- 1| t 1| (1 row) 1| ....................................................................... Продолжим выполнение до новой точки останова: 1| => select p.targetName, 1| (select string_agg(t.n::text|| 1| case when t.n=p.linenumber-1 then '*' else '' end, 1| chr(10)) from generate_series(1, 11) as t(n)) as line, 1| pldbg_get_source(:sessionid, p.func) AS src 1| from pldbg_continue(:sessionid) p; 1| targetname | line | src 1| -----------------+------+------------------------------------- 1| get_count(text) | 1 +| DECLARE + 1| | 2 +| cmd text; + 1| | 3 +| retval bigint; + 1| | 4 +| BEGIN + 1| | 5 +| cmd := 'SELECT COUNT(*) FROM ' + 1| | 6 +| || quote_ident(tabname);+ 1| | 7 +| + 1| | 8* +| EXECUTE cmd INTO retval; + 1| | 9 +| RETURN retval; + 1| | 10 +| END; + 1| | 11 | 1| (1 row) 1| pldbg_get_source - гарантирует, что мы получим исходный код функции ровно таким, каким его видит 2-й сеанс. Если после начала отладки кто-то изменил get_count, то чтение pg_proc в 1-м сеансе будет некорректным. ....................................................................... Еще раз посмотрим значения локальных переменных: 1| => select name, varClass, value, pg_catalog.format_type(dtype, NULL) as dtype 1| from pldbg_get_variables(:sessionid) 1| order by varClass; 1| name | varclass | value | dtype 1| ---------+----------+-------------------------------+-------- 1| tabname | A | pg_class | text 1| cmd | L | SELECT COUNT(*) FROM pg_class | text 1| retval | L | NULL | bigint 1| (3 rows) 1| Видим, что переменной cmd присвоен текст команды SELECT. ....................................................................... Выполним следующую строку функции: 1| => select s.targetname, s.linenumber-1 as line 1| from pldbg_step_over(:sessionid) s; 1| targetname | line 1| -----------------+------ 1| get_count(text) | 9 1| (1 row) 1| ....................................................................... И еще раз посмотрим значения локальных переменных: 1| => select name, varClass, value, pg_catalog.format_type(dtype, NULL) as dtype 1| from pldbg_get_variables(:sessionid) 1| order by varClass; 1| name | varclass | value | dtype 1| ---------+----------+-------------------------------+-------- 1| tabname | A | pg_class | text 1| cmd | L | SELECT COUNT(*) FROM pg_class | text 1| retval | L | 321 | bigint 1| (3 rows) 1| Команда SELECT выполнена, в переменную retval записан результат. ....................................................................... Прекратим сеанс отладки: 1| => select * from pldbg_abort_target(:sessionid); 1| pldbg_abort_target 1| -------------------- 1| t 1| (1 row) 1| 2| ERROR: canceling statement due to user request 2| CONTEXT: PL/pgSQL function get_count(text) line 9 at RETURN Выполнение функции во втором сеансе завершается ошибкой. .......................................................................