blob: 5404eb52d3c618271cf6b3a87a63cd3883a2edc7 [file] [log] [blame]
#!/usr/bin/env python
# coding: UTF-8
# pt-snap-statements
#
# Copyright(c) 2015 Uptime Technologies, LLC.
import sys, os
libpath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../lib")
sys.path.append(libpath)
import getopt
import PsqlWrapper
import log
class SnapStatements:
def build_where_clause(self, where_clause, cond):
if where_clause is None:
where_clause = " where "
else:
where_clause = where_clause + " and "
where_clause = where_clause + " " + cond
return where_clause
def __init__(self, psql, interval, n_top, debug=False):
self.debug = debug
self.psql = psql
# Max queries to be listed.
if n_top is None:
n_top = 10000
log.debug("version: " + str(self.psql.get_version()))
if self.psql.get_version() < 9.4:
queryid1 = ''
queryid2 = ''
else:
queryid1 = 'queryid, '
queryid2 = 'to_hex(s1.queryid) AS "QUERYID", '
if self.psql.get_version() < 9.2:
dirtied1 = ''
dirtied2 = ''
blkreadtime1 = ''
blkreadtime2 = ''
blkwritetime1 = ''
blkwritetime2 = ''
else:
dirtied1 = 'sum(shared_blks_dirtied) + sum(local_blks_dirtied) AS blks_dirtied,'
dirtied2 = '( s1.blks_dirtied - coalesce(s2.blks_dirtied,0) ) AS "B_DIRT", '
blkreadtime1 = ', sum(blk_read_time) AS blk_read_time'
blkreadtime2 = ', round( (s1.blk_read_time - coalesce(s2.blk_read_time,0))::numeric, 1) AS "R_TIME"'
blkwritetime1 = ', sum(blk_write_time) AS blk_write_time'
blkwritetime2 = ', round( (s1.blk_write_time - coalesce(s2.blk_write_time,0))::numeric, 1) AS "W_TIME" '
self.query = ' \
/*SNAP*/ CREATE TEMP TABLE snap_pg_stat_statements \
AS SELECT userid, \
dbid, \
%s \
query, \
sum(calls) AS calls, \
sum(total_time) AS total_time, \
sum(rows) AS rows, \
sum(shared_blks_hit) + sum(local_blks_hit) AS blks_hit, \
sum(shared_blks_read) + sum(local_blks_read) + sum(temp_blks_read) AS blks_read, \
%s /* blks_dirtied */ \
sum(shared_blks_written) + sum(local_blks_written) + sum(temp_blks_written) AS blks_written \
%s /* blk_read_time */ \
%s /* blk_write_time */ \
FROM pg_stat_statements \
GROUP BY userid,dbid, %s query; \
\
/*SNAP*/ SELECT pg_sleep(%d); \
\
/*SNAP*/ CREATE TEMP TABLE snap_pg_stat_statements2 \
AS SELECT userid, \
dbid, \
%s /* queryid */ \
query, \
sum(calls) AS calls, \
sum(total_time) AS total_time, \
sum(rows) AS rows, \
sum(shared_blks_hit) + sum(local_blks_hit) AS blks_hit, \
sum(shared_blks_read) + sum(local_blks_read) + sum(temp_blks_read) AS blks_read, \
%s /* blks_dirtied */ \
sum(shared_blks_written) + sum(local_blks_written) + sum(temp_blks_written) AS blks_written \
%s /* blk_read_time */ \
%s /* blk_write_time */ \
FROM pg_stat_statements \
GROUP BY userid,dbid, %s query; \
\
SELECT u.usename AS "USER", \
d.datname AS "DBNAME", \
%s \
substring(s1.query, 1, 30) AS "QUERY", \
( s1.calls - coalesce(s2.calls,0) ) AS "CALLS", \
( s1.total_time - coalesce(s2.total_time,0) )::integer AS "T_TIME", \
( s1.rows - coalesce(s2.rows,0) ) AS "ROWS", \
( s1.blks_hit - coalesce(s2.blks_hit,0) ) AS "B_HIT", \
( s1.blks_read - coalesce(s2.blks_read,0) ) AS "B_READ", \
%s /* blks_dirtied */ \
( s1.blks_written - coalesce(s2.blks_written,0) ) AS "B_WRTN" \
%s /* blk_read_time */ \
%s /* blk_write_time */ \
FROM snap_pg_stat_statements2 AS s1 \
LEFT OUTER JOIN snap_pg_stat_statements s2 ON s1.userid = s2.userid \
AND s1.dbid = s2.dbid \
AND s1.query = s2.query \
LEFT OUTER JOIN pg_database d ON s1.dbid = d.oid \
LEFT OUTER JOIN pg_user u ON s1.userid = u.usesysid \
WHERE ( s1.calls - coalesce(s2.calls,0) ) > 0 \
AND s1.query NOT LIKE \'--%%\' \
AND s1.query NOT LIKE \'/*SNAP*/ %%\' \
ORDER BY 6 DESC \
LIMIT %d; \
' % (queryid1, dirtied1, blkreadtime1, blkwritetime1, queryid1,
interval,
queryid1, dirtied1, blkreadtime1, blkwritetime1, queryid1,
queryid2, dirtied2, blkreadtime2, blkwritetime2, n_top)
def check(self):
query = ' \
select count(*) as "pg_stat_statements" \
from pg_class c left outer join pg_namespace n \
on c.relnamespace = n.oid \
where n.nspname=\'public\' \
and c.relname=\'pg_stat_statements\''
rs = self.psql.execute_query(query)
log.debug("check: " + str(rs))
if int(rs[1][0]) != 1:
log.error("pg_stat_statements view not found.")
return False
query = ' \
select count(*) as "track_io_timing"\
from pg_settings \
where name = \'track_io_timing\' \
and setting = \'on\''
rs = self.psql.execute_query(query)
log.debug("check: " + str(rs))
if self.psql.get_version() >= 9.2 and int(rs[1][0]) != 1:
log.warning("track_io_timing is diabled.")
return True
def reset(self):
query = 'SELECT pg_stat_statements_reset();'
rs = self.psql.execute_query(query)
log.debug("reset: " + str(rs))
if len(rs) == 0 or rs[0][0] != 'pg_stat_statements_reset':
log.error("Cannot reset.")
log.error("Check your privilege and database.")
return True
def get(self):
if self.check() is False:
return False
log.debug("get: " + self.query)
rs = self.psql.execute_query(self.query)
avail = False
rs2 = []
for r in rs:
if r[0] == 'USER':
avail = True
if avail is True:
rs2.append(r)
log.debug("get: " + str(rs2))
self.psql.print_result(rs2)
return True
def usage():
print ""
print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [interval]"
print ""
print "Options:"
print " -h, --host=HOSTNAME Host name of the postgres server"
print " -p, --port=PORT Port number of the postgres server"
print " -U, --username=USERNAME User name to connect"
print " -d, --dbname=DBNAME Database name to connect"
print ""
print " -t, --top=NUMBER Number of queries to be listed"
print " -R, --reset Reset statistics"
print ""
print " --help Print this help."
print ""
if __name__ == "__main__":
try:
opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:Rt:",
["help", "debug", "host=", "port=", "username=", "dbname=",
"reset", "top="])
except getopt.GetoptError, err:
log.error(str(err))
usage()
sys.exit(2)
host = None
port = None
username = None
dbname = None
n_top = None
do_reset = False
debug = None
for o, a in opts:
if o in ("-h", "--host"):
host = a
elif o in ("-p", "--port"):
port = int(a)
elif o in ("-U", "--username"):
username = a
elif o in ("-d", "--dbname"):
dbname = a
elif o in ("-R", "--reset"):
do_reset = True
elif o in ("-t", "--top"):
n_top = int(a)
elif o in ("--debug"):
log.setLevel(log.DEBUG)
debug = True
elif o in ("--help"):
usage()
sys.exit(0)
else:
log.error("unknown option: " + o + "," + a)
sys.exit(1)
p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, on_error_stop=True, debug=debug)
if do_reset is True:
log.info("Resetting statistics.")
snap = SnapStatements(p, 0, 0, debug=debug)
snap.reset()
sys.exit(0)
try:
if (len(args) == 0):
log.info("Interval is 10 seconds.")
interval = 10
else:
interval = int(args[0])
except ValueError, err:
log.error(str(err))
usage()
sys.exit(2)
snap = SnapStatements(p, interval, n_top, debug=debug)
try:
if snap.get() is False:
sys.exit(1)
except KeyboardInterrupt, err:
log.info("Terminated.")
sys.exit(1)
sys.exit(0)