debugging your pl/pgsql code

33
PL/pgSQL Debugging

Upload: jim-mlodgenski

Post on 20-Feb-2017

1.028 views

Category:

Technology


0 download

TRANSCRIPT

PL/pgSQL Debugging

Who Am I?

● Jim Mlodgenski– [email protected]

– @jim_mlodgenski

● Director– United States PostgreSQL (www.postgresql.us)

● Co-organizer of– Philly PUG (www.phlpug.org)

– NYC PUG (www.nycpug.org)

● CTO, OpenSCG– www.openscg.com

Fantasy Football

● Fantasy football is a statistical game in which players compete against each other by managing groups of real players or position units selected from American football teams.

Business Rules

● Passing– Touchdown (4 points)

– Every 25 yards (1 point)

– Interception (-1 point)

● Rushing– Touchdown (6 points)

– Every 10 yards (1 point)

● Receiving– Touchdown (6 points)

– Every 10 yards (1 point)

Sample Schema

Choosing a Language

ProceduresCREATE OR REPLACE FUNCTION rushing_score(p_player_key VARCHAR, p_year int, p_week int) RETURNS INT AS$$DECLARE score INT;BEGIN -- 1 point for every 10 yards rushing SELECT r.yards/10 INTO score FROM rushing r, games g WHERE r.game_id = g.game_id AND g.year = p_year AND g.week = p_week AND r.player_key = p_player_key;

IF score IS NULL THEN RETURN 0; END IF; RETURN score;END;$$ LANGUAGE plpgsql;

ProceduresCREATE OR REPLACE FUNCTION player_game_score(p_player_key VARCHAR, p_year int, p_week int)...BEGIN score := 0;

-- Get the position of the player SELECT position INTO l_position FROM player WHERE player_key = p_player_key;

IF l_position = 'qb' THEN score := score + passing_score(p_player_key, p_year, p_week); score := score + rushing_score(p_player_key, p_year, p_week); score := score + td_score(p_player_key, p_year, p_week); ELSIF l_position = 'rb' THEN score := score + rushing_score(p_player_key, p_year, p_week); score := score + td_score(p_player_key, p_year, p_week); ELSIF l_position = 'wr' THEN score := score + receiving_score(p_player_key, p_year, p_week); score := score + td_score(p_player_key, p_year, p_week); ELSIF l_position = 'te' THEN score := score + receiving_score(p_player_key, p_year, p_week); score := score + td_score(p_player_key, p_year, p_week); ELSE return 0; END IF; return score;END;$$ LANGUAGE plpgsql;

ProceduresCREATE OR REPLACE FUNCTION avg_yearly_score(p_player_key VARCHAR, p_year int) RETURNS REAL AS$$DECLARE score INT; i INT;BEGIN score := 0;

FOR i IN 1..17 LOOP score := score + player_game_score(p_player_key, p_year, i); END LOOP;

RETURN score/16.0;END;$$ LANGUAGE plpgsql;

Debugging: RAISECREATE OR REPLACE FUNCTION passing_score(p_player_key VARCHAR, p_year int, p_week int) RETURNS INT AS$$...BEGIN score := 0; -- 1 point for every 25 yards passing SELECT p.pass_yards/25 INTO yardage_score FROM passing p, games g WHERE p.game_id = g.game_id AND g.year = p_year AND g.week = p_week AND p.player_key = p_player_key;

IF yardage_score IS NULL THEN yardage_score := 0; END IF;

RAISE NOTICE 'Passing Yards Score: %', yardage_score;

Debugging: RAISE

nfl=# select passing_score('BreeDr00', 2005, 5);NOTICE: Passing Yards Score: 8NOTICE: Passing TD Score: 4NOTICE: Interception Score: -2 passing_score --------------- 10(1 row)

Debugger

git://git.postgresql.org/git/pldebugger.gitmake USE_PGXS=1make install USE_PGXS=1

- OR -

http://community.openscg.com/se/postgresql/packages.jsp

shared_preload_libraries = '$libdir/plugin_debugger'

CREATE EXTENSION pldbgapi;

Demo

Debugger

Debugger

Debugger

Debugging Triggers

Debugging Triggers

Debugging: RAISE

nfl=# SELECT p.first_name, p.last_name, p.position, avg_yearly_score(p.player_key, 2006) AS score FROM player p WHERE p.player_key IN (SELECT * FROM yearly_player(2005)) AND avg_yearly_score(p.player_key, 2006) > 10 ORDER BY 4 DESC;

Debugging: RAISECONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing TD Score: 8CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Interception Score: -2CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing Yards Score: 9CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing TD Score: 4CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Interception Score: 0CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing Yards Score: 10CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing TD Score: 8CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Interception Score: -4CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing Yards Score: 7CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Passing TD Score: 4CONTEXT: PL/pgSQL function player_game_score(character varying,integer,integer) line 15 at assignmentPL/pgSQL function avg_yearly_score(character varying,integer) line 9 at assignmentNOTICE: Interception Score: -2...

Track Functionsset track_functions = 'PL';

nfl=# SELECT * FROM pg_stat_user_functions; funcid | schemaname | funcname | calls | total_time | self_time --------+------------+-------------------+-------+------------+----------- 20564 | public | avg_yearly_score | 547 | 7011.25 | 13.33 20565 | public | passing_score | 1666 | 1551.862 | 1551.862 20566 | public | player_game_score | 9299 | 6997.92 | 188.718 20567 | public | receiving_score | 4811 | 2465.982 | 2465.982 20568 | public | rushing_score | 4488 | 2303.934 | 2303.934 20569 | public | td_score | 9299 | 487.424 | 487.424 20570 | public | yearly_player | 1 | 14.139 | 14.139(7 rows)

Profiler

https://bitbucket.org/openscg/plprofiler.gitmake USE_PGXS=1make install USE_PGXS=1

- OR -

http://community.openscg.com/se/postgresql/packages.jsp

shared_preload_libraries = '$libdir/plprofiler.so'

CREATE EXTENSION plprofiler;

Demo

Profiler

nfl=# SELECT * FROM pl_profiler;ERROR: plprofiler must be loaded and enabledSTATEMENT: SELECT * FROM pl_profiler;

nfl=# SELECT pl_profiler_enable(true); pl_profiler_enable -------------------- t(1 row)

Profilernfl=# SELECT p.first_name, p.last_name, p.position, nfl-# avg_yearly_score(p.player_key, 2006) AS scorenfl-# FROM player pnfl-# WHERE p.player_key IN (SELECT * FROM yearly_player(2005))nfl-# AND avg_yearly_score(p.player_key, 2006) > 10nfl-# ORDER BY 4 DESC; first_name | last_name | position | score ------------+----------------+----------+--------- LaDainian | Tomlinson | rb | 22.5 Peyton | Manning | qb | 18.875 Larry | Johnson | rb | 17.875 Michael | Vick | qb | 15.875 Drew | Brees | qb | 15.75 Marc | Bulger | qb | 15.5625 Steven | Jackson | rb | 15.1875 Carson | Palmer | qb | 15.125 Willie | Parker | rb | 14.9375

Profilernfl=# SELECT * FROM pl_profiler; func_oid | line_number | line | exec_count | total_time | longest_time ----------+-------------+----------------------------------------------------------------------+------------+------------+-------------- 99155 | 1 | | 0 | 0 | 0 99155 | 2 | DECLARE | 0 | 0 | 0 99155 | 3 | l_position VARCHAR; | 0 | 0 | 0 99155 | 4 | score INT; | 0 | 0 | 0 99155 | 5 | BEGIN | 0 | 0 | 0 99155 | 6 | score := 0; | 9299 | 5519 | 18 99155 | 7 | | 0 | 0 | 0 99155 | 8 | -- Get the position of the player | 0 | 0 | 0 99155 | 9 | SELECT position | 9299 | 114556 | 136 99155 | 10 | INTO l_position | 0 | 0 | 0 99155 | 11 | FROM player | 0 | 0 | 0 99155 | 12 | WHERE player_key = p_player_key; | 0 | 0 | 0 99155 | 13 | | 0 | 0 | 0 99155 | 14 | IF l_position = 'qb' THEN | 9299 | 7011451 | 2854 99155 | 15 | score := score + passing_score(p_player_key, p_year, p_week); | 1666 | 1591910 | 2101 99155 | 16 | score := score + rushing_score(p_player_key, p_year, p_week); | 1666 | 873194 | 744 99155 | 17 | score := score + td_score(p_player_key, p_year, p_week); | 1666 | 138214 | 320 99155 | 18 | ELSIF l_position = 'rb' THEN | 0 | 0 | 0 99155 | 19 | score := score + rushing_score(p_player_key, p_year, p_week); | 2822 | 1470068 | 961 99155 | 20 | score := score + td_score(p_player_key, p_year, p_week); | 2822 | 140201 | 802 99155 | 21 | ELSIF l_position = 'wr' THEN | 0 | 0 | 0 99155 | 22 | score := score + receiving_score(p_player_key, p_year, p_week); | 3111 | 1649303 | 1028 99155 | 23 | score := score + td_score(p_player_key, p_year, p_week); | 3111 | 158259 | 571 99155 | 24 | ELSIF l_position = 'te' THEN | 0 | 0 | 0 99155 | 25 | score := score + receiving_score(p_player_key, p_year, p_week); | 1700 | 902277 | 870 99155 | 26 | score := score + td_score(p_player_key, p_year, p_week); | 1700 | 67249 | 151 99155 | 27 | ELSE | 0 | 0 | 0 99155 | 28 | return 0; | 0 | 0 | 0 99155 | 29 | END IF; | 0 | 0 | 0

Profilernfl=# SELECT func_oid::regprocedure, line_number, substr(line, 0, 30), nfl-# (total_time::numeric)/(exec_count::numeric), exec_count nfl-# FROM pl_profiler nfl-# WHERE exec_count > 0 nfl-# ORDER BY 4 DESC; func_oid | line_number | substr | ?column? | exec_count ------------------------------------------------------+-------------+-------------------------------+------------------------+------------ yearly_player(integer) | 3 | RETURN QUERY SELECT DISTINC | 27455.000000000000 | 1 avg_yearly_score(character varying,integer) | 8 | FOR i IN 1..17 LOOP | 12840.711151736746 | 547 player_game_score(character varying,integer,integer) | 15 | score := score + passing | 926.8511404561824730 | 1666 avg_yearly_score(character varying,integer) | 9 | score := score + player_g | 755.1645338208409506 | 9299 player_game_score(character varying,integer,integer) | 14 | IF l_position = 'qb' THEN | 735.3543391762555113 | 9299 player_game_score(character varying,integer,integer) | 25 | score := score + receivi | 518.6929411764705882 | 1700 player_game_score(character varying,integer,integer) | 22 | score := score + receivi | 512.6878817100610736 | 3111 receiving_score(character varying,integer,integer) | 6 | SELECT r.yards/10 | 507.6599459571814592 | 4811 player_game_score(character varying,integer,integer) | 19 | score := score + rushing | 503.4840538625088590 | 2822 player_game_score(character varying,integer,integer) | 16 | score := score + rushing | 503.2094837935174070 | 1666 rushing_score(character varying,integer,integer) | 6 | SELECT r.yards/10 | 495.7384135472370766 | 4488 passing_score(character varying,integer,integer) | 11 | SELECT p.pass_yards/25 | 417.8895558223289316 | 1666 passing_score(character varying,integer,integer) | 37 | SELECT p.interceptions * -2 | 409.1992797118847539 | 1666 player_game_score(character varying,integer,integer) | 17 | score := score + td_scor | 83.6602641056422569 | 1666 passing_score(character varying,integer,integer) | 24 | SELECT p.passing * 4 | 83.1236494597839136 | 1666 player_game_score(character varying,integer,integer) | 23 | score := score + td_scor | 54.0118932819029251 | 3111 player_game_score(character varying,integer,integer) | 20 | score := score + td_scor | 53.8954642097802977 | 2822 td_score(character varying,integer,integer) | 6 | SELECT t.other * 6 | 50.4611248521346381 | 9299 player_game_score(character varying,integer,integer) | 26 | score := score + td_scor | 44.9488235294117647 | 1700 player_game_score(character varying,integer,integer) | 9 | SELECT position | 13.0637702978814926 | 9299 passing_score(character varying,integer,integer) | 19 | IF yardage_score IS NULL TH | 2.2893157262905162 | 1666 passing_score(character varying,integer,integer) | 45 | IF int_score IS NULL THEN | 2.1248499399759904 | 1666 rushing_score(character varying,integer,integer) | 14 | IF score IS NULL THEN | 2.1033868092691622 | 4488 receiving_score(character varying,integer,integer) | 14 | IF score IS NULL THEN | 2.0700478071087092 | 4811 td_score(character varying,integer,integer) | 14 | IF score IS NULL THEN | 1.9494569308527799 | 9299

Summary

● Be careful running these tools on production systems– They do have some performance impact

● Building the extensions are simple, but still require a development environment

● The debugger and the profiler use the same hooks so at the moment they can not be used at the same time

Questions?

[email protected]@jim_mlodgenski