<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Plpgsql_debugger</id>
	<title>Plpgsql debugger - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Plpgsql_debugger"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Plpgsql_debugger&amp;action=history"/>
	<updated>2026-04-29T20:46:46Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Plpgsql_debugger&amp;diff=388&amp;oldid=prev</id>
		<title>imported&gt;WikiSysop v 16. 3. 2008, 09:25</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Plpgsql_debugger&amp;diff=388&amp;oldid=prev"/>
		<updated>2008-03-16T09:25:14Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==Profiler a debugger pro PostgreSQL==&lt;br /&gt;
[[Category:Články]]&lt;br /&gt;
Bez debuggeru a profileru se neobejde žádný zkušený programátor, tím spíše &lt;br /&gt;
začátečník. Bez zkušeností nedokáže přesně odhadnout kritická místa a často&lt;br /&gt;
zbytečně umisťuje ladící výpisy. I já jako relativně zkušený programátor se při&lt;br /&gt;
hackingu bez debuggeru neobejdu. Zvlášť nedocenitelná je postmorten analýza &lt;br /&gt;
core souboru a to natolik, že jsem se naučil používat gdb a zásadně &lt;br /&gt;
používám debug verzi PostgreSQL&amp;lt;ref&amp;gt;Tuto verzi získáte vlastním překladem zdrojových kódů a nastavením přepínačů&lt;br /&gt;
--enable-debug a --enable-cassert. S tímto nastavením aktivujete vnitřní kontroly,&lt;br /&gt;
jejichž provedení má negativní vliv na výkon (cca 50%), takže pokud zrovna nepíšete&lt;br /&gt;
SPI doplňky nebo neřešíte nějaký problém, tak tuto verzi nepoužívejte.&amp;lt;/ref&amp;gt;. Programování uložených procedur se v tomto&lt;br /&gt;
ohledu neliší od klasického programování. Debugger a profiler přijde vhod. Až do&lt;br /&gt;
loňského roku (2007) tyto nástroje pro PL/pgSQL chyběly. Proto se iniciativity&lt;br /&gt;
ujala firma EnterpriseDB a její programátor Korry Douglas vytvořil nezbytné moduly&lt;br /&gt;
pro PostgreSQL a GUI debuggeru pro klienta EnterpriseDB. O možnost krokovat a&lt;br /&gt;
ladit PL/pgSQL funkce byla rozšířena administrační konzole pgAdminIII. [[image: pgAdminIII.png|thumb]]&lt;br /&gt;
==Profiler==&lt;br /&gt;
Začnu tím jednodušším a, dovolím si tvrdit, užitečnějším nástrojem - profilerem.&lt;br /&gt;
Výsledkem profileru je tabulka zobrazující kolikrát byla vykonána určitá část&lt;br /&gt;
programu a kolik času pro to bylo potřeba. Jelikož ani profiler a ani debugger&lt;br /&gt;
není součástí distribuce PostgreSQL nejprve musíme doinstalovat chybějící moduly&lt;br /&gt;
na server&amp;lt;ref&amp;gt;Nejedná se o komplikovanou záležitost, kterou bych také ovšem neoznačil&lt;br /&gt;
jako triviální.&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# zdrojové kódy PostgreSQL jsou v adreáři src/pgsql&lt;br /&gt;
cd src/pgsql/contrib&lt;br /&gt;
cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/edb-debugger login&lt;br /&gt;
cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/edb-debugger checkout server&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Na výzvu o zadání hesla zadejte libovolný (neprázdný) řetězec, například mezeru.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cd server&lt;br /&gt;
make all&lt;br /&gt;
su -&lt;br /&gt;
make install&lt;br /&gt;
&amp;lt;/pre?&lt;br /&gt;
Dále musíme zavést modul profileru:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[pavel@localhost ~]$ psql postgres&lt;br /&gt;
Welcome to psql 8.3beta4, the PostgreSQL interactive terminal.&lt;br /&gt;
&lt;br /&gt;
Type:  \copyright for distribution terms&lt;br /&gt;
       \h for help with SQL commands&lt;br /&gt;
       \? for help with psql commands&lt;br /&gt;
       \g or terminate with semicolon to execute query&lt;br /&gt;
       \q to quit&lt;br /&gt;
&lt;br /&gt;
postgres=# LOAD &amp;#039;$libdir/plugins/plugin_profiler&amp;#039;;&lt;br /&gt;
LOAD&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Po zavedení modulu se vytvoří nová systémová proměnná plpgsql.profiler_tablename&lt;br /&gt;
obsahující název tabulky do které se má ukládat profilační log. Implicitně tato&lt;br /&gt;
proměnná obsahuje prázdný řetězec, což znamená, že je profiler neaktivní. &lt;br /&gt;
&lt;br /&gt;
Potřebnou tabulku modul profileru vytvoří automaticky, pokud ještě neexistuje.&lt;br /&gt;
Profiler tedy aktivujeme nastavením proměnné plpgsql.profiler_tablename.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SET plpgsql.profiler_tablename = &amp;#039;profil1&amp;#039;;&lt;br /&gt;
SET&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Budu profilovat funkci:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo()&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE a integer;  &lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR i IN 1..1000 LOOP&lt;br /&gt;
    SELECT INTO a pk &lt;br /&gt;
       FROM footab&lt;br /&gt;
      WHERE pk = i;&lt;br /&gt;
    -- do some&lt;br /&gt;
  END LOOP;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
funkci spustíme příkazem SELECT foo() a po jejím dokončení se můžeme dotázat do &lt;br /&gt;
tabulky profil1. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT line_number AS ln, sourcecode, &lt;br /&gt;
                  exec_count AS count, time_total AS total, &lt;br /&gt;
                  time_longest AS longest &lt;br /&gt;
              FROM profil1 ORDER BY func_oid, line_number;&lt;br /&gt;
 ln |       sourcecode        | count |  total   | longest  &lt;br /&gt;
----+-------------------------+-------+----------+----------&lt;br /&gt;
  0 |                         |     1 |    2e-06 |    2e-06&lt;br /&gt;
  1 | DECLARE a integer;      |     0 |        0 |        0&lt;br /&gt;
  2 | BEGIN                   |     0 |        0 |        0&lt;br /&gt;
  3 |   FOR i IN 1..1000 LOOP |     1 | 0.039662 | 0.039662&lt;br /&gt;
  4 |     SELECT INTO a pk    |  1000 | 0.037785 |  0.00082&lt;br /&gt;
  5 |        FROM footab      |     0 |        0 |        0&lt;br /&gt;
  6 |       WHERE pk = i;     |     0 |        0 |        0&lt;br /&gt;
  7 |     -- do some          |     0 |        0 |        0&lt;br /&gt;
  8 |   END LOOP;             |     0 |        0 |        0&lt;br /&gt;
  9 | END;                    |     0 |        0 |        0&lt;br /&gt;
(10 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Překvapivě se zdá, že nejpomalejší je příkaz FOR (řádek 3). To ale není pravda,&lt;br /&gt;
je to určité zkreslení, které vychází z interní struktury interpretu plpgsql, které&lt;br /&gt;
poskytuje časy nikoliv na řádek, ale na příkaz. Tedy 0.045 sec zabralo vykonávání&lt;br /&gt;
nikoliv řádku č. 3, ale příkazu FOR (a tedy hlavně jeho těla), v tomto případě&lt;br /&gt;
1000 provedení příkazu SELECT. Samotný řádek 3 zabral pouze 0.001ms = 0.045 - 0.044.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo()&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE a integer;  &lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR a IN&lt;br /&gt;
     SELECT pk &lt;br /&gt;
     FROM footab &lt;br /&gt;
     WHERE pk BETWEEN 1 &lt;br /&gt;
              AND 1000 &lt;br /&gt;
  LOOP&lt;br /&gt;
    -- do some&lt;br /&gt;
  END LOOP;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
DROP TABLE profil1;&lt;br /&gt;
SELECT foo();&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT line_number AS ln, sourcecode, &lt;br /&gt;
                  exec_count AS count, time_total AS total, &lt;br /&gt;
                  time_longest AS longest &lt;br /&gt;
              FROM profil1 ORDER BY func_oid, line_number;&lt;br /&gt;
 ln |        sourcecode        | count |  total   | longest  &lt;br /&gt;
----+--------------------------+-------+----------+---------&lt;br /&gt;
  0 |                          |     1 |    3e-06 |    3e-06&lt;br /&gt;
  1 | DECLARE a integer;       |     0 |        0 |        0&lt;br /&gt;
  2 | BEGIN                    |     0 |        0 |        0&lt;br /&gt;
  3 |   FOR a IN               |     1 | 0.003407 | 0.003407&lt;br /&gt;
  4 |      SELECT pk           |     0 |        0 |        0&lt;br /&gt;
  5 |      FROM footab         |     0 |        0 |        0&lt;br /&gt;
  6 |      WHERE pk BETWEEN 1  |     0 |        0 |        0&lt;br /&gt;
  7 |               AND 1000   |     0 |        0 |        0&lt;br /&gt;
  8 |   LOOP                   |     0 |        0 |        0&lt;br /&gt;
  9 |     -- do some           |     0 |        0 |        0&lt;br /&gt;
 10 |   END LOOP;              |     0 |        0 |        0&lt;br /&gt;
 11 | END;                     |     0 |        0 |        0&lt;br /&gt;
(12 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nyní cyklus zabere méně než desetinu předešlé varianty. Že rozdíl není 1000 násobný,s &lt;br /&gt;
je zapříčiněno rozdílným prováděním příkazu SELECT v těchto dvou případech. Nicméně&lt;br /&gt;
desetinásobné zrychlení rozhodně nepostrádá na smyslu (na zatížené databázi by &lt;br /&gt;
zrychlení bylo znatelnější). Tento poměr je např. 10h a 1h nebo 10min a 1min nebo&lt;br /&gt;
10sec a 1sec. Profiler sám o sobě za nás žádnou práci neudělá, ale pomůže nám &lt;br /&gt;
identifikovat místa, která má smysl měnit a snažit se optimalizovat (platí pravidlo&lt;br /&gt;
80/20, tj. 20% kódu je odpovědno za 80% potřeby času).&lt;br /&gt;
==Debugger==&lt;br /&gt;
Stejně jako profiler, tak i debugger má svůj modul, který běží na serveru. Pokud&lt;br /&gt;
chceme používat globální breakpointy (body přerušení), tak tento modul musí být&lt;br /&gt;
aktivován automaticky po startu serveru. To zařídíme nastavením konfiguračního &lt;br /&gt;
parametru v postgresql.conf shared_preload_libraries (Nezapomeňte restartovat&lt;br /&gt;
server).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
shared_preload_libraries = &lt;br /&gt;
  &amp;#039;$libdir/plugins/plugin_debugger.so&amp;#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
dalším krokem je registrace funkcí debuggeru pro určitou databázi. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
psql postgres&lt;br /&gt;
postgres=# \i /usr/local/pgsql/share/contrib/pldbgapi.sql &lt;br /&gt;
CREATE TYPE&lt;br /&gt;
CREATE TYPE&lt;br /&gt;
CREATE TYPE&lt;br /&gt;
CREATE TYPE&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pokud nemáme nainstalovanou nejnovější verzi programu pgAdminIII můžeme ladit&lt;br /&gt;
plpgsql z druhé konzole. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# \x&lt;br /&gt;
Expanded display is on.&lt;br /&gt;
postgres=# select * from pldbg_get_target_info(&amp;#039;foo&amp;#039;,&amp;#039;f&amp;#039;);&lt;br /&gt;
-[ RECORD 1 ]----------&lt;br /&gt;
target     | 41259&lt;br /&gt;
schema     | 2200&lt;br /&gt;
nargs      | 0&lt;br /&gt;
argtypes   | &lt;br /&gt;
targetname | foo&lt;br /&gt;
argmodes   | &lt;br /&gt;
argnames   | &lt;br /&gt;
targetlang | 24578&lt;br /&gt;
fqname     | public.foo&lt;br /&gt;
returnsset | f&lt;br /&gt;
returntype | 2278&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Výše uvedeným dotazem získáme popis funkce foo.&lt;br /&gt;
&lt;br /&gt;
V dalším kroku vytvoříme tzv. listener. Vrácený handle použijeme v další funkci,&lt;br /&gt;
kterou nastavujeme globální breakpoint:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT * &lt;br /&gt;
   FROM pldbg_set_global_breakpoint(1, 41259, NULL, NULL);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Druhý parametr je oid laděné funkce. Třetí volitelný parametr je číslo řádku. &lt;br /&gt;
Poslední volitelný parametr určuje, u kterého procesu se má breakpoint aplikovat.&lt;br /&gt;
Pokud není zadán breakpoint bude platit pro všechny procesy, které spustí laděnou&lt;br /&gt;
funkci (Pozor na používání debuggeru na provozních serverech!).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 SELECT * FROM pldbg_wait_for_target(1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Volání funkce pldbg_wait_for_target způsobí čekání na dosažení breakpointu. Z druhé&lt;br /&gt;
konzoli spustím funkci foo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT foo(); -- konzole B&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
poté se mi uvolní konzole A, odkud mohu ladit funkci foo:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * FROM pldbg_wait_for_target(1);&lt;br /&gt;
-[ RECORD 1 ]---------+------&lt;br /&gt;
pldbg_wait_for_target | 20123&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Dotazem na listener zjistíme, který breakpoint aktivoval přerušení.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * FROM pldbg_wait_for_breakpoint(1);&lt;br /&gt;
-[ RECORD 1 ]-----&lt;br /&gt;
func       | 41259&lt;br /&gt;
linenumber | 4&lt;br /&gt;
targetname | foo&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Další dotazy slouží k získání popisu laděné funkce:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * FROM pldbg_get_source(1, 41259);&lt;br /&gt;
-[ RECORD 1 ]----+-----------------------------------------&lt;br /&gt;
pldbg_get_source | &lt;br /&gt;
                 : DECLARE a integer;  &lt;br /&gt;
                 : BEGIN&lt;br /&gt;
                 :   FOR a IN &lt;br /&gt;
                 :           SELECT pk &lt;br /&gt;
                 :              FROM footab &lt;br /&gt;
                 :             WHERE pk BETWEEN 1 AND 1000 &lt;br /&gt;
                 :   LOOP&lt;br /&gt;
                 :     -- do some&lt;br /&gt;
                 :   END LOOP;&lt;br /&gt;
                 : END;&lt;br /&gt;
                 : &lt;br /&gt;
&lt;br /&gt;
postgres=# \x&lt;br /&gt;
Expanded display is off.&lt;br /&gt;
postgres=# SELECT name, value from pldbg_get_variables(1);&lt;br /&gt;
 name | value &lt;br /&gt;
------+-------&lt;br /&gt;
 a    | NULL&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Voláním funkcí pldbg_step_into(handler), call pldbg_step_over(handler) nebo &lt;br /&gt;
pldbg_continue(handler) můžeme krokovat laděnou funkci. Posloupnost volání funkcí&lt;br /&gt;
získáme dotazem:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# select * from pldbg_get_stack(1);&lt;br /&gt;
 level | targetname | func  | linenumber | args &lt;br /&gt;
-------+------------+-------+------------+------&lt;br /&gt;
     0 | foo        | 41259 |          3 | &lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ladění můžeme ukončit funkcí pldbg_abort_target(handler);&lt;br /&gt;
&lt;br /&gt;
Ladění funkce bez GUI není příliš pohodlné, proto se o tuto funkci rozšířil&lt;br /&gt;
pgAdminIII. Nainstalujte si nejnovější verzi, protože pouze ta obsahuje debugger.&lt;br /&gt;
&lt;br /&gt;
Spustíme pgAdminIII a připojíme se k naší databázi. Potom v levém stromu objektů&lt;br /&gt;
vybereme funkci foo. Pokud je vše v pořádku&amp;lt;ref&amp;gt;Pokud nemáte povolenou volbu Debugging, tak a) nemáte nainstalován modul&lt;br /&gt;
debuggeru na serveru, b) nemáte zaregistrovány funkce debuggeru v databázi, ke &lt;br /&gt;
které jste se připojili.&amp;lt;/ref&amp;gt;, tak v kontextovém menu (na pravém&lt;br /&gt;
tlačítku myši) máme volbu Debugging. Toto políčko menu má submenu Debug a Set&lt;br /&gt;
breakpoint. Pokud vybereme Debug, tak se nám otevře okno debuggeru a my můžeme&lt;br /&gt;
interaktivně ladit funkci foo. Pokud vybereme Set breakpoint, pak se zobrazí&lt;br /&gt;
modální dialogové okno, které nás informuje, že pgAdminIII čeká na vykonání funkce. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Otevřeme si konzolu a z ní spustíme funkci foo. Poté pgAdminIII ukončí čekání &lt;br /&gt;
a aktivuje GUI debuggeru.&lt;br /&gt;
Jak debugger, tak profiler jsou jednoduché nástroje, které nabízí pouze základní&lt;br /&gt;
funkce a je znát určitá nevyzrálost těchto nástrojů. Nicméně tyto nástroje tady&lt;br /&gt;
jsou a je možné je používat.&lt;br /&gt;
[[image:debugger.png|thumb]] [[image:Wait for breakpoint.png|thumb]]&lt;br /&gt;
==Poznámky==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;/div&gt;</summary>
		<author><name>imported&gt;WikiSysop</name></author>
	</entry>
</feed>