Internal change
PiperOrigin-RevId: 519848376
Change-Id: I518bbf6c4fd583d87309c10c8988f32d788b416c
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..67eda5a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+include Makefile.global
+
+all:
+ make -C docs all
+ make -C src all
+ make -C deps all
+ make install.sh
+
+install.sh: install.sh.in
+ sed 's/__VERSION__/$(VERSION)/' ./install.sh.in > install.sh
+
+check:
+ make -C lib check
+ make -C bin check
+ make -C src check
+
+install:
+ mkdir -p $(PREFIX)
+ install -m 644 LICENSE $(PREFIX)
+ make -C bin install
+ make -C lib install
+ make -C src install
+ make -C docs install
+
+uninstall:
+ rm -rf $(PREFIX)
+
+dist: uninstall install installcheck
+ tar zcvf postgres-toolkit-$(VERSION).tar.gz $(PREFIX)
+
+installcheck:
+ find $(PREFIX)/bin -name 'pt-*' -type f | sort | awk '{ print $$1 " --help" }' | sh > installcheck.out
+ diff -rc installcheck.expected installcheck.out
+
+clean:
+ make -C bin clean
+ make -C lib clean
+ make -C docs clean
+ make -C src clean
+ make -C deps clean
+ rm -rf postgres-toolkit-$(VERSION).tar.gz
+ rm -f installcheck.out
+ rm -f install.sh
+
diff --git a/Makefile.global b/Makefile.global
new file mode 100644
index 0000000..9b26801
--- /dev/null
+++ b/Makefile.global
@@ -0,0 +1,2 @@
+VERSION=0.2.2
+PREFIX=/opt/uptime/postgres-toolkit-$(VERSION)
diff --git a/bin/Makefile b/bin/Makefile
new file mode 100644
index 0000000..492345e
--- /dev/null
+++ b/bin/Makefile
@@ -0,0 +1,29 @@
+include ../Makefile.global
+
+all:
+ make -C ../src/verifychecksum all
+ cp ../src/verifychecksum/verifychecksum.bin .
+
+check:
+ make -C t check
+
+install:
+ mkdir -p $(PREFIX)/bin
+ install -m 755 pt-config $(PREFIX)/bin
+ install -m 755 pt-index-usage $(PREFIX)/bin
+ install -m 755 pt-kill $(PREFIX)/bin
+ install -m 755 pt-proc-stat $(PREFIX)/bin
+ install -m 755 pt-session-profiler $(PREFIX)/bin
+ install -m 755 pt-set-tablespace $(PREFIX)/bin
+ install -m 755 pt-snap-statements $(PREFIX)/bin
+ install -m 755 pt-replication-stat $(PREFIX)/bin
+ install -m 755 pt-stat-snapshot $(PREFIX)/bin
+ install -m 755 pt-table-usage $(PREFIX)/bin
+ install -m 755 pt-tablespace-usage $(PREFIX)/bin
+ install -m 755 pt-verify-checksum $(PREFIX)/bin
+ install -m 755 pt-xact-stat $(PREFIX)/bin
+
+clean:
+ rm -f *~
+ rm -f verifychecksum.bin
+ make -C t clean
diff --git a/bin/pt-config b/bin/pt-config
new file mode 100755
index 0000000..1bd0191
--- /dev/null
+++ b/bin/pt-config
@@ -0,0 +1,296 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-config
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import os, sys
+libpath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../lib")
+sys.path.append(libpath)
+
+import getopt
+import os
+import re
+from stat import *
+
+import log
+
+class PostgressqlConf():
+ fp = None
+
+ def __init__(self, conffile):
+ self.conffile = conffile
+ log.info("Reading: " + self.conffile)
+
+ def open(self):
+ try:
+ self.fp = open(self.conffile)
+ except OSError, err:
+ log.error(err.strerror + " (" + self.conffile + ")")
+ sys.exit(1)
+ except IOError, err:
+ log.error(err.strerror + " (" + self.conffile + ")")
+ sys.exit(1)
+
+
+ def find(self, line, key):
+ m = re.search(key + " *=", line[0:len(key) + 10])
+ if m is not None:
+ return True
+
+ return False
+
+ def is_disabled(self, line, key):
+ m = re.search("#.*" + key + " *=", line[0:len(key) + 10])
+ if m is not None:
+ return True
+
+ return False
+
+ def parse(self, line):
+ # quoted value accepts a zero-length string.
+ m = re.search("(.*=[ ]*')([^']*)('.*)", line)
+
+ # non-quoted value
+ if m is None:
+ m = re.search("(.*=[ ]*)([^ \t]+)(.*)", line)
+
+ return [m.groups(0)[0], m.groups(0)[1], m.groups(0)[2]]
+
+ def get(self, key):
+ self.open()
+
+ val = []
+ for l in self.fp.readlines():
+ l = l.rstrip('\r\n')
+
+# log.debug("readlines: " + l)
+
+ if self.find(l, key) is True:
+ log.debug("find: " + l)
+
+ if self.is_disabled(l, key) is True:
+ log.debug("disabled: " + l)
+ a = self.parse(l)
+
+ # value and is_disabled?
+ val.append([ a[1], True ])
+
+ else:
+ log.debug(l)
+ a = self.parse(l)
+
+ log.debug(a[1])
+
+ val.append([ a[1], False ])
+
+ self.fp.close()
+
+ # not found
+ if len(val) == 0:
+ return None
+
+ return val
+
+ def set(self, key, value, dryrun=True):
+ self.open()
+
+ lines = ""
+
+ oldline = None
+ newline = None
+ for l in self.fp.readlines():
+ l = l.rstrip('\r\n')
+
+ if self.find(l, key) is True:
+ oldline = l
+
+ a = self.parse(l)
+ l = a[0].replace('#', '') + value + a[2]
+
+ newline = l
+
+ log.debug("set: " + l)
+
+ lines = lines + l + "\n"
+
+ if oldline is None and newline is None:
+ log.error("Parameter %s not found." % args[1])
+ return False
+
+ self.fp.close()
+
+ if dryrun is True:
+ log.info("Dry-run mode:")
+ log.info(" Old: " + oldline)
+ log.info(" New: " + newline)
+ return True
+ else:
+ log.info("Applying:")
+ log.info(" Old: " + oldline)
+ log.info(" New: " + newline)
+
+ return self.update_conf(lines)
+
+
+ def disable(self, key, dryrun=True):
+ self.open()
+
+ lines = ""
+
+ oldline = None
+ newline = None
+ for l in self.fp.readlines():
+ l = l.rstrip('\r\n')
+
+ if self.find(l, key) is True:
+ oldline = l
+
+ a = self.parse(l)
+ l = '#' + a[0].replace('#', '') + a[1] + a[2]
+
+ newline = l
+
+ log.debug("set: " + l)
+
+ lines = lines + l + "\n"
+
+ self.fp.close()
+
+ if oldline is None and newline is None:
+ log.error("Parameter %s not found." % args[1])
+ return False
+
+ if dryrun is True:
+ log.info("Dry-run mode:")
+ log.info(" Old: " + oldline)
+ log.info(" New: " + newline)
+ return True
+ else:
+ log.info("Applying:")
+ log.info(" Old: " + oldline)
+ log.info(" New: " + newline)
+
+ return self.update_conf(lines)
+
+ def update_conf(self, conf):
+ st = os.stat(self.conffile)
+ log.debug(st)
+
+ try:
+ os.rename(self.conffile, self.conffile + ".bak")
+ except OSError, err:
+ log.error(err.strerror + " (" + self.conffile + ")")
+ sys.exit(1)
+ except IOError, err:
+ log.error(err.strerror + " (" + self.conffile + ")")
+ sys.exit(1)
+
+ f = open(self.conffile, "w")
+ f.write(conf)
+ f.close()
+
+ os.chmod(self.conffile, st[ST_MODE])
+
+ # if running with superuser, the file owner/group need to be changed.
+ if os.getuid() == 0:
+ os.chown(self.conffile, st[ST_UID], st[ST_GID])
+
+ log.info("Updated: " + self.conffile)
+
+ return True
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [command] [param [value]]"
+ print ""
+ print "Commands:"
+ print " get [PARAM] Get a current value of a parameter."
+ print " set [PARAM] [VALUE] Set a new value for a parameter."
+ print " disable [PARAM] Comment a parameter out."
+ print ""
+ print "Options:"
+ print " -D, --pgdata=PGDATA Specify a PostgreSQL database cluster."
+ print " --apply Apply change(s)."
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "D:",
+ ["help", "debug", "pgdata=", "apply", "disable"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ pgdata = None
+ if os.environ.get("PGDATA") is not None:
+ pgdata = os.environ.get("PGDATA")
+
+ dryrun = True
+ debug = False
+
+ for o, a in opts:
+ if o in ("-D", "--pgdata"):
+ pgdata = a
+ elif o in ("--debug"):
+ debug = True
+ log.setLevel(log.DEBUG)
+ elif o in ("--apply"):
+ dryrun = False
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ if pgdata is None:
+ log.info("Specify PGDATA directory.")
+ usage()
+ sys.exit(0)
+
+ log.debug(args)
+ if len(args) != 2 and len(args) != 3:
+ usage()
+ sys.exit(1)
+
+ p = PostgressqlConf(pgdata + '/postgresql.conf')
+
+ exit_code = 0
+
+ if args[0] == 'get':
+ v = p.get(args[1])
+ if v is not None:
+ for vv in v:
+ if vv[1] is True:
+ print(vv[0] + " (disabled)")
+ else:
+ print(vv[0])
+ exit_code = 0
+ else:
+ log.error("Parameter %s not found." % args[1])
+ exit_code = 1
+
+ elif args[0] == 'set':
+ if len(args) < 3:
+ usage()
+ sys.exit(1)
+
+ r = p.set(args[1], args[2], dryrun)
+ if r is True:
+ exit_code = 0
+ else:
+ exit_code = 1
+
+ elif args[0] == 'disable':
+ r = p.disable(args[1], dryrun)
+ if r is True:
+ exit_code = 0
+ else:
+ exit_code = 1
+
+ sys.exit(exit_code)
diff --git a/bin/pt-index-usage b/bin/pt-index-usage
new file mode 100755
index 0000000..3bf3ba3
--- /dev/null
+++ b/bin/pt-index-usage
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-index-usage
+#
+# 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
+
+class IndexUsage:
+ 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, owner, schema, table, index, unused, debug=False):
+ self.debug = debug
+
+ self.psql = psql
+
+ where_clause = None
+
+ if schema is not None:
+ where_clause = self.build_where_clause(where_clause, "s.schemaname = '" + schema + "'")
+
+ if owner is not None:
+ where_clause = self.build_where_clause(where_clause, "u.usename = '" + owner + "'")
+
+ if table is not None:
+ where_clause = self.build_where_clause(where_clause, "s.relname = '" + table + "'")
+
+ if index is not None:
+ where_clause = self.build_where_clause(where_clause, "s.indexrelname = '" + index + "'")
+
+ if unused is True:
+ where_clause = self.build_where_clause(where_clause, "idx_scan = 0")
+
+ if where_clause is None:
+ where_clause = ''
+
+ # indislive: 9.3 or later
+ indislive = ''
+ if self.psql.get_version() >= 9.3:
+ indislive = ' || case when indislive then \',\' else \'NOTLIVE\' end '
+
+ self.query = ' \
+select \
+ s.indexrelid as "OID", \
+ u.usename as "OWNER", \
+ s.schemaname as "SCHEMA", \
+ s.relname as "TABLE", \
+ s.indexrelname as "INDEX", \
+ pg_relation_size(s.indexrelid)/8192 as "BLKS", \
+ idx_scan as "SCAN", \
+ idx_tup_read as "T_READ", \
+ idx_tup_fetch as "T_FTCH", \
+ idx_blks_read as "B_READ", \
+ idx_blks_hit as "B_HIT", \
+ regexp_replace( \
+ case when indisvalid then \',\' else \'INVALID,\' end || \
+ case when indisready then \',\' else \'NOTREADY,\' end \
+ %s , \
+ \',+$\', \'\') as "STATUS", \
+ coalesce(spcname, (select spcname from pg_database d left outer join pg_tablespace t on dattablespace = t.oid where datname = current_database())) as "TABLESPACE" \
+ from \
+ pg_stat_user_indexes s left outer join pg_statio_user_indexes s2 \
+ on s.indexrelid = s2.indexrelid \
+ left outer join pg_index i \
+ on s.indexrelid = i.indexrelid \
+ left outer join pg_class c \
+ on s.indexrelid = c.oid \
+ left outer join pg_user u \
+ on c.relowner = u.usesysid \
+ left outer join pg_tablespace t \
+ on c.reltablespace = t.oid \
+%s \
+ order by \
+ 2,3,4 \
+;' % (indislive, where_clause)
+
+ if self.debug is True:
+ print self.query
+
+ def get(self):
+ rs = self.psql.execute_query(self.query)
+
+ self.psql.print_result(rs)
+
+ return True
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...]"
+ 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 " -o, --owner=STRING Owner name"
+ print " -n, --schema=STRING Schema name"
+ print " -t, --table=STRING Table name"
+ print " -i, --index=STRING Index name"
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:o:n:t:i:u",
+ ["help", "debug", "host=", "port=", "username=", "dbname=",
+ "owner=", "schema=", "table=", "index=", "unused"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ owner = None
+ schema = None
+ table = None
+ index = None
+ unused = 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 ("-o", "--owner"):
+ owner = a
+ elif o in ("-n", "--schema"):
+ schema = a
+ elif o in ("-t", "--table"):
+ table = a
+ elif o in ("-i", "--index"):
+ index = a
+ elif o in ("-u", "--unused"):
+ unused = True
+ elif o in ("--debug"):
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ iu = IndexUsage(p, owner, schema, table, index, unused, debug=debug)
+ if iu.get() is False:
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-kill b/bin/pt-kill
new file mode 100755
index 0000000..c05250c
--- /dev/null
+++ b/bin/pt-kill
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-kill
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import os, sys
+libpath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../lib")
+sys.path.append(libpath)
+
+import getopt
+import os
+import signal
+
+import log
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [command] [pid]"
+ print ""
+ print "Commands:"
+ print " cancel Cancel a running query."
+ print " terminate Terminate a backend with canceling query."
+ print ""
+ print "Options:"
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "",
+ ["help", "debug"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ debug = False
+
+ for o, a in opts:
+ if o in ("--debug"):
+ debug = True
+ log.setLevel(log.DEBUG)
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ log.debug(args)
+
+ if len(args) < 2:
+ log.info("Specify signal command with pid.")
+ usage()
+ sys.exit(0)
+
+ command = args[0]
+ pid = args[1]
+
+ if command == 'cancel':
+ sig = signal.SIGINT
+ elif command == 'terminate':
+ sig = signal.SIGTERM
+ else:
+ log.error("Unknown command %s" % command)
+ sys.exit(1)
+
+ try:
+ os.kill(int(pid), sig)
+ except OSError, err:
+ log.error(err)
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-proc-stat b/bin/pt-proc-stat
new file mode 100755
index 0000000..0594688
--- /dev/null
+++ b/bin/pt-proc-stat
@@ -0,0 +1,374 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-proc-stat
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import os, sys
+libpath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../lib")
+sys.path.append(libpath)
+
+import copy
+import getopt
+import log
+import re
+import subprocess
+import time
+
+class ProcInfo:
+ pid = None
+ ppid = None
+ proc_stat = None
+ proc_io = None
+ proc_net = None
+ debug = None
+
+ def __init__(self, pid, pagesize=4096, debug=False):
+ self.pid = pid
+ self.pagesize = int(pagesize)
+ self.debug = debug
+
+ self.proc_stat = []
+ self.proc_io = {}
+ self.proc_net = {}
+
+ if self.debug is True:
+ log.setLevel(log.DEBUG)
+
+ def read(self):
+ base = "/proc/" + str(self.pid)
+
+ # /proc/pid/cmdline
+ try:
+ f = open(base + "/cmdline")
+ except IOError, err:
+ log.debug(err)
+ return False
+
+ self.proc_cmdline = None
+
+ for l in f.readlines():
+ self.proc_cmdline = l.split('\0')
+ self.name = self.proc_cmdline[0]
+
+ f.close()
+
+ if self.proc_cmdline is None:
+ log.debug(base + "/cmdline is empty.")
+ return False
+
+ log.debug(base + "/cmdline ok.")
+
+ self.name = self.name.replace('postgres:', '')
+ self.name = self.name.replace('process', '')
+ self.name = re.sub('.*/', '', self.name)
+ self.name = re.sub('^ +', '', self.name)
+ self.name = re.sub(' +$', '', self.name)
+ self.name = re.sub(' +', ' ', self.name)
+
+ log.debug("name = %s" % self.name)
+
+ # /proc/pid/stat
+ try:
+ f = open(base + "/stat")
+ except IOError, err:
+ log.debug(err)
+ return False
+
+ log.debug(base + "/stat ok.")
+
+ for l in f.readlines():
+ self.proc_stat = l.split(' ')
+ log.debug(self.proc_stat)
+
+ self.ppid = int(self.proc_stat[3])
+ break
+
+ f.close()
+
+ log.debug("ppid = %d" % self.ppid)
+
+ # /proc/pid/io
+ try:
+ f = open(base + "/io")
+ except IOError, err:
+ log.debug(err)
+ return False
+
+ log.debug(base + "/io ok.")
+
+ for l in f.readlines():
+ key, value = l.rstrip('\r\n').split(': ')
+ log.debug("key = %s, value = %s" % (key, value))
+
+ self.proc_io[key] = int(value)
+
+ f.close()
+
+ return True
+
+ def print_stat(self, prev=None):
+ if prev is not None:
+ # disk
+ rbytes = ( self.proc_io["read_bytes"] - prev.proc_io["read_bytes"] ) / 1024
+ wbytes = ( self.proc_io["write_bytes"] - prev.proc_io["write_bytes"] ) / 1024
+ # net
+ rchar = ( self.proc_io["rchar"] - prev.proc_io["rchar"] ) / 1024 - rbytes
+ wchar = ( self.proc_io["wchar"] - prev.proc_io["wchar"] ) / 1024 - wbytes
+ # cpu
+ cpu_usr = int(self.proc_stat[13]) - int(prev.proc_stat[13])
+ cpu_sys = int(self.proc_stat[14]) - int(prev.proc_stat[14])
+ # mem
+ vsz = int(self.proc_stat[22]) / 1024 / 1024
+ rss = int(self.proc_stat[23]) * self.pagesize / 1024 / 1024
+ print("%20s[%5d] %4s %4d %4d %4d %4d %6d %6d %6d %6d" % (self.name[:20], int(self.pid), self.proc_stat[2], cpu_usr, cpu_sys, vsz, rss, int(rbytes), int(wbytes), int(rchar), int(wchar)))
+ else:
+ # disk
+ rbytes = ( self.proc_io["read_bytes"] ) / 1024
+ wbytes = ( self.proc_io["write_bytes"] ) / 1024
+ # net
+ rchar = ( self.proc_io["rchar"] ) / 1024 - rbytes
+ wchar = ( self.proc_io["wchar"] ) / 1024 - wbytes
+ # cpu
+ cpu_usr = int(self.proc_stat[13])
+ cpu_sys = int(self.proc_stat[14])
+ # mem
+ vsz = int(self.proc_stat[22]) / 1024 / 1024
+ rss = int(self.proc_stat[23]) * self.pagesize / 1024 / 1024
+ print("%20s[%5d] %4s %4d %4d %4d %4d %6d %6d %6d %6d" % (self.name[:20], int(self.pid), self.proc_stat[2], cpu_usr, cpu_sys, vsz, rss, int(rbytes), int(wbytes), int(rchar), int(wchar)))
+
+
+class PostgresProcessStat():
+ pgdata = None
+ postmaster_pid = None
+ prev_stat = {}
+ stat = {}
+ update_count = 0
+ debug = False
+
+ def __init__(self, pgdata=None, pid=None, debug=False):
+ self.pgdata = pgdata
+ self.postmaster_pid = pid
+ self.debug = debug
+ return
+
+ def get_postmaster_pid(self):
+ if self.postmaster_pid is not None:
+ return self.postmaster_pid
+
+ # Get pid from postmaster.pid if available.
+ if self.pgdata is not None:
+ log.debug("reading " + self.pgdata + "/postmaster.pid to to determine the postmaster pid.")
+ try:
+ f = open(self.pgdata + "/postmaster.pid")
+ for l in f.readlines():
+ pid = int(l.replace('\n', ''))
+ break
+ f.close()
+ except IOError, err:
+ log.error(err.strerror + " (" + self.pgdata + "/postmaster.pid" + ")")
+ sys.exit(1)
+
+ if pid is not None:
+ log.debug("found postmaster pid " + str(pid))
+ return pid
+
+ # Find postmaster pid from /proc/<pid> files.
+ pid = None
+ for p in self._get_pids():
+ i = ProcInfo(p)
+ if i.read() is False:
+ continue
+
+ log.debug("name: %s" % i.name)
+
+ # Name of the process is "postmaster" or "postgres".
+ if re.search('postmaster', i.name) is not None or re.search('postgres', i.name) is not None:
+ pid = int(p)
+ log.debug("found postmaster pid " + str(pid))
+
+ if pid is None:
+ log.error("Cannot find postmaster pid.")
+ usage()
+ sys.exit(1)
+
+ self.postmaster_pid = pid
+
+ return pid
+
+ def get_postmaster_child_pids(self):
+ child_pids = []
+
+ for p in self._get_pids():
+ i = ProcInfo(p)
+ if i.read() is False:
+ continue
+
+ if i.ppid == self.get_postmaster_pid():
+ child_pids.append(p)
+
+ log.debug("child pids = " + str(child_pids))
+
+ return child_pids
+
+ def _get_pids(self):
+ pids = []
+ for p in os.listdir("/proc"):
+ if re.search('^\d+$', p) is not None:
+ pids.append(p)
+
+ log.debug("_get_pids = " + str(pids))
+
+ return pids
+
+ def update_stat(self):
+ pids = self.get_postmaster_child_pids()
+
+ self.prev_stat = {}
+ self.prev_stat = copy.deepcopy(self.stat)
+
+ self.stat = {}
+
+ # postmaster stat
+ i = ProcInfo(self.get_postmaster_pid())
+ if i.read() is False:
+ log.error("Cannot get postmaster stat.")
+ sys.exit(1)
+
+ self.stat[self.get_postmaster_pid()] = i
+
+ # child process stat
+ for p in pids:
+ i = ProcInfo(p)
+ if i.read() is False:
+ continue
+
+ log.debug("update_stat: %s %s" % (i.name, i.pid))
+
+ self.stat[p] = i
+
+ self.update_count = self.update_count + 1
+ log.debug("updae_stat: done count = %d" % self.update_count)
+
+ def print_stat(self):
+ os.system("date")
+ print("%20s[%5s] %4s %4s %4s %4s %4s %6s %6s %6s %6s" % ('PROCESS NAME', 'PID', 'STAT', 'USR', 'SYS', 'VSZ', 'RSS', 'READ', 'WRITE', 'READ2', 'WRITE2'))
+
+ if self.prev_stat is None or len(self.prev_stat) == 0:
+ log.debug("no prev records. printing the first records.")
+
+ # print process stat.
+ for i in sorted(self.stat):
+ self.stat[i].print_stat()
+
+ print("")
+ return True
+
+ log.debug("printing the diffs.")
+
+ # print process stat.
+ for i in sorted(self.stat):
+ if self.prev_stat.has_key(i) is True:
+ self.stat[i].print_stat(self.prev_stat[i])
+ else:
+ self.stat[i].print_stat()
+
+ print("")
+ return True
+
+if __name__ == "__main2__":
+# p = ProcInfo('21185', debug=True)
+# p.read()
+# p.print_stat()
+
+# log.setLevel(log.DEBUG)
+
+ s = PostgresProcessStat(None)
+# print(s._get_pids())
+
+# print(s.get_postmaster_pid())
+# print(s.get_postmaster_pid())
+
+# print(s.get_postmaster_child_pids())
+
+ s.update_stat()
+ s.print_stat()
+
+ time.sleep(1)
+
+ s.update_stat()
+ s.print_stat()
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [delay [count]]"
+ print ""
+ print "Options:"
+ print " -D, --pgdata=DATADIR Location of the database storage area"
+ print " -P, --pid=PID Process ID of the postmaster"
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "D:P:",
+ ["help", "debug", "pgdata=", "pid="])
+ except getopt.GetoptError, err:
+ log.error(str(err))
+ usage()
+ sys.exit(2)
+
+ postmaster_pid = None
+ pgdata = None
+ debug = None
+
+ for o, a in opts:
+ if o in ("-D", "--pgdata"):
+ pgdata = a
+ elif o in ("-P", "--pid"):
+ postmaster_pid = int(a)
+ elif o in ("--debug"):
+ debug = True
+ log.setLevel(log.DEBUG)
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ log.error("unknown option: " + o + "," + a)
+ sys.exit(1)
+
+ delay = None
+ count = None
+
+ if len(args) == 2:
+ delay = int(args[0])
+ count = int(args[1])
+ elif len(args) == 1:
+ delay = int(args[0])
+
+ stat = PostgresProcessStat(pgdata=pgdata, pid=postmaster_pid, debug=debug)
+
+ log.debug("postmaster pid = " + str(postmaster_pid))
+
+ c = 0
+ done = False
+ while done is False:
+ stat.update_stat()
+ stat.print_stat()
+
+ c = c + 1
+ if count is not None and c >= count:
+ break
+
+ if delay is None:
+ break
+
+ try:
+ time.sleep(delay)
+ except KeyboardInterrupt, err:
+ log.info("Terminated.")
+ break
+
+ sys.exit(0)
diff --git a/bin/pt-replication-stat b/bin/pt-replication-stat
new file mode 100755
index 0000000..d182d88
--- /dev/null
+++ b/bin/pt-replication-stat
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-replication-stat
+#
+# 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 copy
+import getopt
+import time
+
+import log
+import PsqlWrapper
+
+class ReplicationStatistics:
+ def __init__(self, psql, debug=False):
+ self.debug = debug
+ self.psql = psql
+ self.header = None
+
+ def get(self):
+ if self.psql.get_version() == 9.0:
+ log.error("PostgreSQL 9.0 is not supported.")
+ sys.exit(1)
+
+ pidcol = "pid"
+
+ if self.psql.get_version() == 9.1:
+ pidcol = "procpid"
+
+ query = ' \
+select null as "PID", \
+ null as "NAME", \
+ null as "HOST", \
+ null as "PORT", \
+ \'local\' as "STATE", \
+ pg_current_xlog_insert_location() as "SENT", \
+ pg_current_xlog_location() as "WRITTTEN", \
+ null as "FLUSHED", \
+ null as "REPLAYED", \
+ null as "PRI", \
+ \'master\' as "MODE" \
+union all \
+select %s, \
+ application_name as name, \
+ client_addr as addr, \
+ client_port as port, \
+ state, \
+ sent_location as sent, \
+ write_location as write, \
+ flush_location as flush, \
+ replay_location as reply, \
+ sync_priority as pri, \
+ sync_state as mode \
+ from pg_stat_replication \
+;' % pidcol
+
+ log.debug(query)
+
+ rs = p.execute_query(query)
+ if rs is None or len(rs) == 0:
+ log.error("Cannot retreive statitics from the server. Connecting to wrong server?")
+ sys.exit(1)
+ else:
+ p.print_result(rs)
+
+ print("")
+
+ return True
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [delay [count]]"
+ 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 " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:",
+ ["help", "debug", "host=", "port=", "username=", "dbname="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ 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 ("--debug"):
+ log.setLevel(log.DEBUG)
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ delay = None
+ count = None
+
+ if len(args) >= 1:
+ delay = int(args[0])
+ if len(args) >= 2:
+ count = int(args[1])
+
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ i = 0
+ while True:
+ os.system("date")
+ stat = ReplicationStatistics(p, debug=debug)
+ stat.get()
+ i = i + 1
+
+ if delay is None:
+ break
+
+ if count is not None and i >= count:
+ break
+
+ try:
+ time.sleep(delay)
+ except KeyboardInterrupt, err:
+ log.info("Terminated.")
+ break
+
+ sys.exit(0)
diff --git a/bin/pt-session-profiler b/bin/pt-session-profiler
new file mode 100755
index 0000000..15f807b
--- /dev/null
+++ b/bin/pt-session-profiler
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-session-profiler
+#
+# 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 copy
+from datetime import datetime, timedelta, date, time
+import getopt
+import re
+import subprocess
+
+import log
+
+import TcpdumpWrapper
+
+class SessionProfiler:
+ def __init__(self, host=None, port=None, interface=None, inputfile=None, threshold=1000, debug=None):
+ self.host = host
+ self.port = port
+ self.interface = interface
+ self.inputfile = inputfile
+ self.threshold = threshold
+ self.debug = debug
+
+ self.query_starttime = {}
+ self.query_endtime = {}
+ self.query_string = {}
+
+ log.info("Threshold: %d ms" % self.threshold)
+
+ def run(self):
+ dump = TcpdumpWrapper.TcpdumpWrapper(host=self.host, port=self.port, interface=self.interface, inputfile=self.inputfile, debug=self.debug)
+
+ bindname_to_query = {}
+ session_to_bindname = {}
+
+ cont = True
+ while cont:
+ try:
+ pkt = dump.get_packet()
+ except KeyboardInterrupt, err:
+ pkt = dump.get_last_packet()
+ cont = False
+
+ if pkt is None:
+ break
+
+ # messages available?
+ log.debug("SessionProfiler#run: %s" % pkt.session_id)
+ log.debug("SessionProfiler#run: %s" % str(pkt.ts))
+
+ for m in pkt.get_messages():
+ log.debug("SessionProfiler#run: %c, %s" % (m[0], m[1]))
+
+ if m[0] == 'Q':
+ self.query_start(pkt.session_id, m[1], pkt.ts)
+
+ elif m[0] == 'P':
+ bindname_to_query[m[1]] = m[2]
+ log.debug("[prep] name:%s, query:%s" % (m[1], m[2]))
+
+ elif m[0] == 'B':
+ session_to_bindname[pkt.session_id] = m[2]
+ log.debug("[bind] name:%s" % (m[2]))
+
+ elif m[0] == 'E':
+ if session_to_bindname.has_key(pkt.session_id) is False:
+ continue
+ prep_name = session_to_bindname[pkt.session_id]
+
+ if bindname_to_query.has_key(prep_name) is False:
+ continue
+
+ log.debug("[exec] name:%s, query:%s" % (prep_name, bindname_to_query[prep_name]))
+ self.query_start(pkt.session_id, bindname_to_query[prep_name], pkt.ts)
+
+ elif m[0] == 'C':
+ self.query_end(pkt.session_id, pkt.ts)
+
+ if self.over_threshold(pkt.session_id, self.threshold) is True:
+ q = ""
+
+ if session_to_bindname.has_key(pkt.session_id):
+ bindname = session_to_bindname[pkt.session_id]
+ q = bindname_to_query[bindname]
+ else:
+ q = self.get_query_string(pkt.session_id)
+
+ log.info("sess:%s, time:%s, query:%s" % (pkt.session_id, self.elapsed_time(pkt.session_id), q))
+
+ if cont is False:
+ log.info("Terminated.")
+ sys.exit(0)
+
+ def over_threshold(self, session_id, threshold):
+ if self.query_starttime.has_key(session_id) is False:
+ log.warning("Session Id %s not found in the start timer." % session_id)
+ return None
+ if self.query_endtime.has_key(session_id) is False:
+ log.warning("Session Id %s not found in the end timer." % session_id)
+ return None
+
+ return self.query_starttime[session_id] + timedelta(microseconds=threshold*1000) < self.query_endtime[session_id]
+
+ def query_start(self, session_id, query, timestamp):
+ log.debug("query_start: %s" % session_id)
+
+ # clean up previous session info.
+ if self.query_starttime.has_key(session_id):
+ del self.query_starttime[session_id]
+ if self.query_endtime.has_key(session_id):
+ del self.query_endtime[session_id]
+ if self.query_string.has_key(session_id):
+ del self.query_string[session_id]
+
+ self.query_string[session_id] = query
+ self.query_starttime[session_id] = timestamp
+
+ def query_end(self, session_id, timestamp):
+ log.debug("query_end: %s" % session_id)
+ self.query_endtime[session_id] = timestamp
+
+ def elapsed_time(self, session_id):
+ if self.query_starttime.has_key(session_id) is False:
+ log.warning("Session Id %s not found in the start timer." % session_id)
+ return None
+ if self.query_endtime.has_key(session_id) is False:
+ log.warning("Session Id %s not found in the end timer." % session_id)
+ return None
+
+ t = self.query_endtime[session_id] - self.query_starttime[session_id]
+ return t
+
+ def get_query_string(self, session_id):
+ if self.query_string.has_key(session_id):
+ return self.query_string[session_id]
+ else:
+ return None
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...]"
+ print ""
+ print "Options:"
+ print " -h, --host=HOSTNAME Host name of the postgres server"
+ print " -p, --port=PORT Port number of the postgres server"
+ print " -i STRING Interface name to listen"
+ print " -T, --threshold=NUMBER Threashold of query elapsed time (in millisecond)"
+ print ""
+ print " --help Print this help."
+ print ""
+
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:i:r:T:",
+ ["help", "debug", "host=", "port=", "threshold="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = '5432'
+ interface = 'any'
+ inputfile = None
+ threshold = 1000
+ 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 ("-i"):
+ interface = a
+ elif o in ("-r"):
+ inputfile = a
+ elif o in ("-T", "--threshold"):
+ threshold = int(a)
+ elif o in ("--debug"):
+ debug = True
+ log.setLevel(log.DEBUG)
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = SessionProfiler(host=host, port=port, interface=interface, threshold=threshold, inputfile=inputfile, debug=debug)
+ p.run()
+
+ sys.exit(0)
diff --git a/bin/pt-set-tablespace b/bin/pt-set-tablespace
new file mode 100755
index 0000000..c555eaf
--- /dev/null
+++ b/bin/pt-set-tablespace
@@ -0,0 +1,320 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-set-tablespace
+#
+# 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 re
+import getopt
+import subprocess
+import log
+import PsqlWrapper
+
+class SetTablespace:
+ alter_stmt = []
+
+ 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, owner, schema, table, tablespace, debug=False):
+ self.debug = debug
+
+ self.psql = psql
+ self.owner = owner
+ self.schema = schema
+ self.table = table
+
+ self.tablespace = tablespace
+
+ def build_where(self):
+ where_clause = None
+
+ if self.schema is not None:
+ where_clause = self.build_where_clause(where_clause, "t.schemaname = '" + self.schema + "'")
+
+ if self.owner is not None:
+ where_clause = self.build_where_clause(where_clause, "u.usename = '" + self.owner + "'")
+
+ if self.table is not None:
+ where_clause = self.build_where_clause(where_clause, "t.relname = '" + self.table + "'")
+
+ if where_clause is None:
+ where_clause = ''
+
+ return where_clause
+
+ def alter_table(self):
+ where_clause = self.build_where()
+
+ self.query = ' \
+select \
+ t.relid, \
+ u.usename, \
+ t.schemaname, \
+ t.relname, \
+ s.spcname \
+ from \
+ pg_stat_user_tables t left outer join pg_class c \
+ on t.relid = c.oid \
+ left outer join pg_tablespace s \
+ on c.reltablespace = s.oid \
+ left outer join pg_user u \
+ on c.relowner = u.usesysid \
+%s \
+ order by \
+ 2,3,4 \
+;' % (where_clause)
+
+ log.debug(self.query)
+
+ rs = self.psql.execute_query(self.query)
+
+ row = 0
+ for r in rs:
+ if row > 0 and len(r) == 5:
+ self.alter_stmt.append("ALTER TABLE \"%s\".\"%s\" SET TABLESPACE \"%s\";" % (r[2], r[3], self.tablespace))
+ row = row + 1
+
+ if self.debug is True:
+ self.psql.print_result(rs)
+
+ def alter_index(self):
+ where_clause = self.build_where()
+
+ self.query = ' \
+select \
+ t.indexrelid, \
+ u.usename, \
+ t.schemaname, \
+ t.relname, \
+ t.indexrelname, \
+ s.spcname \
+ from \
+ pg_stat_user_indexes t left outer join pg_class c \
+ on t.indexrelid = c.oid \
+ left outer join pg_tablespace s \
+ on c.reltablespace = s.oid \
+ left outer join pg_user u \
+ on c.relowner = u.usesysid \
+%s \
+;' % (where_clause)
+
+ log.debug(self.query)
+
+ rs = self.psql.execute_query(self.query)
+
+ row = 0
+ for r in rs:
+ if row > 0 and len(r) == 6:
+ self.alter_stmt.append("ALTER INDEX \"%s\".\"%s\" SET TABLESPACE \"%s\";" % (r[2], r[4], self.tablespace))
+ row = row + 1
+
+ if self.debug is True:
+ self.psql.print_result(rs)
+
+ def apply(self, do_apply):
+ self.alter_table()
+ self.alter_index()
+
+ if do_apply is True:
+ log.info("Applying ALTER TABLE/INDEX...")
+ else:
+ log.info("Dry-run mode:")
+
+ failed = 0
+ succeeded = 0
+
+ for s in self.alter_stmt:
+ if do_apply is True:
+ log.debug(s)
+
+ rs = self.psql.execute_query(s)
+
+ log.debug(str(rs))
+
+ if rs is None or len(rs) == 0:
+ log.error(str(self.psql.stderr_data).replace("ERROR: ", "").replace("\n", ""))
+ log.error(" " + s)
+ failed = failed + 1
+
+ elif rs[0][0] != 'ALTER TABLE' and rs[0][0] != 'ALTER INDEX':
+ log.error(str(self.psql.stderr_data).replace("ERROR: ", "").replace("\n", ""))
+ log.error(" " + s)
+ failed = failed + 1
+
+ else:
+ succeeded = succeeded + 1
+ log.debug("Succeeded")
+
+ else:
+ log.info(s)
+
+ if do_apply is True:
+ log.info("%d tables/indexes moved. %d failed." % (succeeded, failed))
+
+ if failed > 0:
+ return False
+
+ return True
+
+
+def tablespace_size(spcloc):
+ log.debug("location: " + spcloc)
+
+ if spcloc is None or len(spcloc) == 0:
+ return ['', '']
+
+ cmd = "df -khPl " + re.sub('/[^/]+$', '', spcloc) + " | grep ^/ | awk '{ print $5 \" \" $4 }'"
+
+ p = subprocess.Popen([cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ p.wait()
+
+ if p.returncode != 0:
+ log.error("Cannot execute df command.")
+ sys.exit(1)
+
+ line = None
+ for l in p.stdout.readlines():
+ if line is None:
+ line = l.replace('\n', '')
+ break
+
+ return str(line).split(' ')
+
+
+def list_tablespace(psql):
+ if psql.get_version() <= 9.1:
+ query = ' \
+select s.oid as "OID", \
+ usename as "OWNER", \
+ spcname as "TABLESPACE", \
+ spclocation as "LOCATION", \
+ null as "USE%", \
+ null as "AVAIL" \
+ from pg_tablespace s left outer join pg_user u \
+ on s.spcowner = u.usesysid; \
+';
+ else:
+ query = ' \
+select s.oid as "OID", \
+ usename as "OWNER", \
+ spcname as "TABLESPACE", \
+ pg_tablespace_location(s.oid) as "LOCATION", \
+ null as "USE%", \
+ null as "AVAIL" \
+ from pg_tablespace s left outer join pg_user u \
+ on s.spcowner = u.usesysid; \
+';
+
+ rs = psql.execute_query(query)
+
+ for r in rs:
+ if len(r) >= 3 and len(r[3]) > 0 and r[3] != 'LOCATION':
+ a = tablespace_size(r[3])
+ r[4] = a[0]
+ r[5] = a[1]
+
+ psql.print_result(rs)
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [tablespace]"
+ 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 " -o, --owner=STRING Owner name"
+ print " -n, --schema=STRING Schema name"
+ print " -t, --table=STRING Table name"
+ print ""
+ print " -l, --list List table spaces"
+ print " --apply Apply change(s)"
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:o:n:lt:i:u",
+ ["help", "debug", "host=", "port=", "username=", "dbname=",
+ "owner=", "schema=", "table=", "list", "apply"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ owner = None
+ schema = None
+ table = None
+
+ do_list = False
+ do_apply = 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 ("-o", "--owner"):
+ owner = a
+ elif o in ("-n", "--schema"):
+ schema = a
+ elif o in ("-t", "--table"):
+ table = a
+ elif o in ("-l", "--list"):
+ do_list = True
+ elif o in ("--apply"):
+ do_apply = True
+ elif o in ("--debug"):
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ # list tablespaces and exit.
+ if do_list is True:
+ list_tablespace(p)
+ sys.exit(0)
+
+ if len(args) == 1:
+ tablespace = args[0]
+ else:
+ usage()
+ sys.exit(0)
+
+ cmd = SetTablespace(p, owner, schema, table, tablespace, debug=debug)
+
+ if cmd.apply(do_apply) is False:
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-snap-statements b/bin/pt-snap-statements
new file mode 100755
index 0000000..5404eb5
--- /dev/null
+++ b/bin/pt-snap-statements
@@ -0,0 +1,273 @@
+#!/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)
diff --git a/bin/pt-stat-snapshot b/bin/pt-stat-snapshot
new file mode 100755
index 0000000..f12a4af
--- /dev/null
+++ b/bin/pt-stat-snapshot
@@ -0,0 +1,471 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-stat-snapshot
+#
+# 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 re
+import log
+import PsqlWrapper
+
+class StatSnapshot():
+ def __init__(self, host=None, port=None, username=None, dbname=None, debug=None):
+ self.host = host
+ self.port = port
+ self.username = username
+ self.dbname = dbname
+ self.debug = debug
+
+ def snap_install_handler(self, args):
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+ ver = p.get_version()
+ log.debug("version: " + str(ver))
+
+ # check if pg_stat_statements is enabled.
+ rs = p.execute_query("show shared_preload_libraries")
+ log.debug(rs)
+ if len(rs) < 2:
+ log.error(p.stderr_data.split('\n')[0])
+ sys.exit(1)
+
+ if re.search('pg_stat_statements', rs[1][0]) is None:
+ log.error("pg_stat_statements module is not enabled. Check your shared_preload_libraries parameter first.")
+ sys.exit(1)
+
+ # create pg_stat_statemnts and pgstattuple functions/veiws.
+ rs = p.execute_query("create extension pg_stat_statements")
+ log.debug(rs)
+ rs = p.execute_query("create extension pgstattuple")
+ log.debug(rs)
+
+ rs = p.execute_query("select count(*) from pg_class where relname = 'pg_stat_statements'")
+ log.debug(rs)
+ if rs[1][0] != '1':
+ log.error("pg_stat_statements is not installed and not enabled.")
+ sys.exit(1)
+
+ installfile = None
+
+ if len(str(ver)) == 3:
+ installfile = "pgperf_snapshot_install%s.sql" % str(ver).replace(".", "")
+ log.debug("installfile: " + installfile)
+
+ sharepath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../share")
+
+ sql = self.file2string(sharepath + "/" + installfile)
+
+ rs = p.execute_query(sql)
+
+ if len(rs) == 0 or rs[len(rs)-1][0] != 'COMMIT':
+ log.debug(rs)
+ log.debug(p.stderr_data)
+ log.error("Failed to install pgperf snapshot.")
+ sys.exit(1)
+ else:
+ log.info("Succeeded to install pgperf snapshot.")
+
+
+ def file2string(self, filepath):
+ s = ""
+ f = open(filepath)
+ for l in f:
+ s = s + l
+ f.close()
+
+ return s
+
+ def snap_uninstall_handler(self, args):
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+ ver = p.get_version()
+ log.debug("version: " + str(ver))
+
+ # uninstall all
+ rs = p.execute_query("drop schema pgperf cascade")
+
+ if len(rs) == 0 or rs[0][0] != 'DROP SCHEMA':
+ log.debug(rs)
+ log.error(p.stderr_data.split('\n')[0])
+ log.error("Failed to uninstall pgperf snapshot.")
+ sys.exit(1)
+ else:
+ log.info("Succeeded to uninstall pgperf snapshot.")
+
+ def snap_create_handler(self, args):
+ level = 1
+
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ if len(args) == 0:
+ log.error("Missing argument.")
+ usage()
+ sys.exit(1)
+
+ level = int(args[0])
+
+ log.debug("create snapshot level " + str(level))
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+
+ rs = p.execute_query("SELECT pgperf.create_snapshot(%d)" % level)
+
+ if len(rs) != 0 and int(rs[1][0]) >= 0:
+ if len(p.stderr_data) > 0:
+ for l in p.stderr_data.split("\n"):
+ if len(l) > 0:
+ log.warning(l)
+ log.info("Succeeded to take a snapshot.")
+ sys.exit(0)
+ else:
+ if len(p.stderr_data) > 0:
+ for l in p.stderr_data.split("\n"):
+ if len(l) > 0:
+ log.error(l)
+ log.error("Failed to take a snapshot.")
+ sys.exit(1)
+
+ def snap_list_handler(self, args):
+ level = 1
+
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+
+ rs = p.execute_query("SELECT sid as \"SID\", date_trunc('second', ts) as \"TIMESTAMP\", level as \"LEVEL\" from pgperf.snapshot")
+ log.debug(rs)
+
+ if len(rs) == 2:
+ log.info("No snapshot found.")
+ elif len(rs) >= 3:
+ p.print_result(rs)
+ else:
+ log.error(p.stderr_data.split('\n')[0])
+ log.error("Failed to access snapshot table.")
+
+ def snap_delete_handler(self, args):
+ sid = None
+
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + ", " + a
+ sys.exit(1)
+
+ if len(args) == 0:
+ log.error("Missing argument.")
+ usage()
+ sys.exit(1)
+
+ sid = args[0]
+
+ if sid is None:
+ usage()
+ sys.exit(0)
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+
+ sids = []
+ if re.search(':', sid) is None:
+ sid_min = int(sid)
+ sid_max = int(sid)
+ else:
+ tmp = sid.split(':')
+ sid_min = None
+ sid_max = None
+ if len(tmp[0]) > 0:
+ sid_min = int(tmp[0])
+ if len(tmp[1]) > 0:
+ sid_max = int(tmp[1])
+
+ if sid_min is None or sid_max is None:
+ rs = p.execute_query("SELECT min(sid), max(sid) from pgperf.snapshot")
+ log.debug(rs)
+
+ if len(rs) < 3:
+ log.error(p.stderr_data.split('\n')[0])
+ sys.exit(1)
+
+ if sid_min is None and len(rs[1][0]) > 0:
+ sid_min = int(rs[1][0])
+ if sid_max is None and len(rs[1][1]) > 0:
+ sid_max = int(rs[1][1])
+
+ if sid_min is None or sid_max is None:
+ log.error("Cannot determine min and/or max sid.")
+ sys.exit(1)
+
+ if sid_max < sid_min:
+ tmp = sid_max
+ sid_max = sid_min
+ sid_min = tmp
+
+ log.debug("min = %d, max = %d" % (sid_min, sid_max))
+
+ if sid_min == sid_max:
+ log.info("Deleting snapshot (%d)." % sid_min)
+ else:
+ log.info("Deleting snapshots (%d-%d)." % (sid_min, sid_max))
+
+ sid = sid_min
+ sql = "begin;\n"
+ while sid <= sid_max:
+ log.debug("pgperf.delete_snapshot(%d)" % sid)
+ sql = sql + "select pgperf.delete_snapshot(%d);\n" % sid
+ sid = sid + 1
+ sql = sql + "commit;\n"
+
+ log.debug(sql)
+ rs = p.execute_query(sql)
+ log.debug(rs)
+
+ if len(rs) > 0 and rs[len(rs)-1][0] == 'COMMIT':
+ log.info("Succeded to delete snapshot(s).")
+ sys.exit(0)
+ else:
+ log.error(p.stderr_data.split('\n')[0])
+ log.error("Failed to delete snapshot(s).")
+ sys.exit(1)
+
+ def snap_export_handler(self, args):
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + ", " + a
+ sys.exit(1)
+
+ if len(args) == 0:
+ log.error("Missing argument.")
+ usage()
+ sys.exit(1)
+
+ filename = args[0]
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+ rs = p.execute_query("select count(*) from pgperf.snapshot")
+ count = int(rs[1][0])
+
+ cmd = "pg_dump -h %s -p %s -U %s -a -n pgperf %s > %s" % (p.host, p.port, p.username, p.dbname, filename)
+
+ log.info("Exporting %d snapshot(s) to file `%s'." % (count, filename))
+ log.debug(cmd)
+ os.system(cmd)
+ log.info("Exporting done.")
+
+ def snap_import_handler(self, args):
+ if len(args) > 0:
+ try:
+ opts, args = getopt.getopt(args[1:], "",
+ ["help"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ for o, a in opts:
+ if o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + ", " + a
+ sys.exit(1)
+
+ if len(args) == 0:
+ log.error("Missing argument.")
+ usage()
+ sys.exit(1)
+
+ filename = args[0]
+
+ p = PsqlWrapper.PsqlWrapper(host=self.host, port=self.port, username=self.username, dbname=self.dbname, debug=self.debug)
+
+ cmd = "psql --set=ON_ERROR_STOP=on -q -h %s -p %s -U %s -a -n pgperf -d %s -f %s" % (p.host, p.port, p.username, p.dbname, filename)
+
+ log.info("Importing snapshot(s) from file `%s'." % filename)
+ log.debug(cmd)
+ exitcode = os.system(cmd + " > log 2>&1")
+
+ if exitcode == 0:
+ log.info("Succeeded to import snapshot(s).")
+ sys.exit(0)
+ else:
+ log.error("Failed to import snapshot(s).")
+ sys.exit(1)
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] install"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] create [level]"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] list"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] delete [sid]"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] export [file]"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] import [file]"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] uninstall"
+ 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 " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:",
+ ["help", "debug", "host=", "port=", "username=", "dbname="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ 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 ("--debug"):
+ debug = True
+ log.setLevel(log.DEBUG)
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ log.debug(args)
+ if len(args) == 0:
+ usage()
+ sys.exit(0)
+
+ ss = StatSnapshot(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ if args[0] == 'install':
+ ss.snap_install_handler(args)
+
+ elif args[0] == 'uninstall':
+ ss.snap_uninstall_handler(args)
+
+ elif args[0] == 'create':
+ ss.snap_create_handler(args)
+
+ elif args[0] == 'list':
+ ss.snap_list_handler(args)
+
+ elif args[0] == 'delete':
+ ss.snap_delete_handler(args)
+
+ elif args[0] == 'export':
+ ss.snap_export_handler(args)
+
+ elif args[0] == 'import':
+ ss.snap_import_handler(args)
+
+ else:
+ log.error("unknown command - " + args[0])
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-table-usage b/bin/pt-table-usage
new file mode 100755
index 0000000..3bbb650
--- /dev/null
+++ b/bin/pt-table-usage
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-table-usage
+#
+# 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
+
+class TableUsage:
+ 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, owner, schema, table, debug=False):
+ self.debug = debug
+
+ self.psql = psql
+
+ where_clause = None
+
+ if schema is not None:
+ where_clause = self.build_where_clause(where_clause, "s.schemaname = '" + schema + "'")
+
+ if owner is not None:
+ where_clause = self.build_where_clause(where_clause, "u.usename = '" + owner + "'")
+
+ if table is not None:
+ where_clause = self.build_where_clause(where_clause, "s.relname = '" + table + "'")
+
+ if where_clause is None:
+ where_clause = ''
+
+ self.query = ' \
+select \
+ s.relid as "OID", \
+ u.usename as "OWNER", \
+ s.schemaname "SCHEMA", \
+ s.relname AS "TABLE", \
+ pg_relation_size(s.relid)/8192 AS "BLKS", \
+ seq_scan AS "SCAN", \
+ seq_tup_read AS "T_READ", \
+ n_tup_ins AS "T_INS", \
+ n_tup_upd AS "T_UPD", \
+ n_tup_del AS "T_DEL", \
+ heap_blks_read AS "B_READ", \
+ heap_blks_hit AS "B_HIT", \
+/* n_tup_hot_upd AS "T_HUPD", \
+ n_live_tup AS "T_LIVE", \
+ n_dead_tup AS "T_DEAD", */ \
+ to_char(CASE WHEN last_vacuum IS NULL THEN last_autovacuum \
+ WHEN last_autovacuum IS NULL THEN last_vacuum \
+ WHEN last_vacuum > last_autovacuum THEN last_vacuum \
+ ELSE last_autovacuum END, \'YYYY-MM-DD HH24:MI:SS\') AS "VACUUMED", \
+ to_char(CASE WHEN last_analyze IS NULL THEN last_autoanalyze \
+ WHEN last_autoanalyze IS NULL THEN last_analyze \
+ WHEN last_analyze > last_autoanalyze THEN last_analyze \
+ ELSE last_autoanalyze END, \'YYYY-MM-DD HH24:MI:SS\') AS "ANALYZED", \
+ coalesce(spcname, (select spcname from pg_database d left outer join pg_tablespace t on dattablespace = t.oid where datname = current_database())) as "TABLESPACE" \
+ from \
+ pg_stat_user_tables s left outer join pg_statio_user_tables s2 \
+ on s.relid = s2.relid \
+ left outer join pg_class c \
+ on s.relid = c.oid \
+ left outer join pg_user u \
+ on c.relowner = u.usesysid \
+ left outer join pg_tablespace t \
+ on c.reltablespace = t.oid \
+%s \
+ order by \
+ 2,3,4 \
+;' % (where_clause)
+
+ if self.debug is True:
+ print self.query
+
+ def get(self):
+ rs = self.psql.execute_query(self.query)
+
+ self.psql.print_result(rs)
+
+ return True
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...]"
+ 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 " -o, --owner=STRING Owner name"
+ print " -n, --schema=STRING Schema name"
+ print " -t, --table=STRING Table name"
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:o:n:t:i:u",
+ ["help", "debug", "host=", "port=", "username=", "dbname=",
+ "owner=", "schema=", "table="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ owner = None
+ schema = None
+ table = None
+
+ 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 ("-o", "--owner"):
+ owner = a
+ elif o in ("-n", "--schema"):
+ schema = a
+ elif o in ("-t", "--table"):
+ table = a
+ elif o in ("--debug"):
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ iu = TableUsage(p, owner, schema, table, debug=debug)
+ if iu.get() is False:
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-tablespace-usage b/bin/pt-tablespace-usage
new file mode 100755
index 0000000..cc7fb7c
--- /dev/null
+++ b/bin/pt-tablespace-usage
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-tablespace-usage
+#
+# 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 TablespaceUsage:
+ def __init__(self, psql, debug=False):
+ self.psql = psql
+ self.debug = debug
+ self.rs = []
+ return
+
+ def get_database_names(self):
+ query = "select datname from pg_database where datallowconn = true order by datname"
+
+ d = []
+ rs = self.psql.execute_query(query)
+ for r in rs[1:len(rs)-1]:
+ d.append(r[0])
+
+ log.debug(d)
+
+ return d
+
+ def get_tablespace_per_database(self, dbname):
+ p = PsqlWrapper.PsqlWrapper(host=self.psql.host, port=self.psql.port, username=self.psql.username, dbname=dbname, debug=self.debug)
+
+ query = " \
+SELECT coalesce(spcname, (select spcname from pg_database d left outer join pg_tablespace t on dattablespace = t.oid where datname = current_database())), \
+ ceil(sum(pg_relation_size)/1024/1024) \
+ FROM ( SELECT oid, \
+ pg_relation_size(oid), \
+ reltablespace \
+ FROM pg_class ) r \
+ LEFT OUTER JOIN pg_tablespace t \
+ on r.reltablespace = t.oid \
+ GROUP BY spcname;"
+
+ rs = p.execute_query(query)
+ log.debug(rs)
+
+ for r in rs[1:len(rs)-1]:
+ log.debug(r)
+ a = []
+ a.append(r[0])
+ if r[0] == 'pg_global':
+ a.append('')
+ else:
+ a.append(dbname)
+ a.append(r[1])
+ self.rs.append(a)
+
+ log.debug(a)
+
+ def get(self):
+ header = []
+ header.append(['TABLESPACE', 'DBNAME', 'SIZE (MB)'])
+
+ d = self.get_database_names()
+
+ for dbname in d:
+ self.get_tablespace_per_database(dbname)
+
+ log.debug(self.rs)
+
+ found = False
+ rs = []
+ for r in self.rs:
+ if r[0] == 'pg_global':
+ if found is False:
+ rs.append(r)
+ found = True
+ else:
+ rs.append(r)
+
+ header.extend(sorted(rs))
+
+ log.debug(header)
+ self.psql.print_result(header)
+
+ return False
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...]"
+ 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 " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:U:d:",
+ ["help", "debug", "host=", "port=", "username=", "dbname="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ username = None
+ dbname = None
+
+ owner = None
+ schema = None
+ table = None
+
+ 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 ("--debug"):
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+
+ tu = TablespaceUsage(p, debug=debug)
+ if tu.get() is False:
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-verify-checksum b/bin/pt-verify-checksum
new file mode 100755
index 0000000..cb6c5e7
--- /dev/null
+++ b/bin/pt-verify-checksum
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-verify-checksum
+#
+# 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 log
+import re
+import subprocess
+from stat import *
+
+import DirectoryTree
+
+class VerifyChecksum():
+ filelist = []
+ verifychecksum_bin = None
+
+ def __init__(self, path, recursive=False, verbose=False, debug=False):
+ self.verifychecksum_bin = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../bin/verifychecksum.bin")
+
+ self.path = path
+ self.recursive = recursive
+ self.verbose = verbose
+
+ log.debug("VerifyChecksum")
+ log.debug(" path = %s" % (self.path))
+ log.debug(" recursive = %s" % str(self.recursive))
+
+
+ def check_filename(self, filepath):
+ # Excluding files not in 'base' and 'global' directories.
+ if re.search('/base/', filepath) is None and re.search('/global/', filepath) is None:
+ return False
+
+ # NNNNNNNN
+ if re.search('/\d+$', filepath) is not None:
+ return True
+ # NNNNNNNN.N
+ elif re.search('/\d+\.\d+$', filepath) is not None:
+ return True
+ # NNNNNNNN_fsm
+ elif re.search('/\d+_fsm$', filepath) is not None:
+ return True
+ # NNNNNNNN_vm
+ elif re.search('/\d+_vm$', filepath) is not None:
+ return True
+
+ return False
+
+ def verify_one(self, path):
+ opt = ""
+ if self.verbose is True:
+ opt = "-v"
+
+ cmd = self.verifychecksum_bin + " " + opt + " " + path
+
+ log.debug("popen: %s" % cmd)
+
+ p = subprocess.Popen([cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ p.wait()
+
+ log.debug("verify_one: return code %d" % p.returncode)
+
+ o = p.stdout.readlines()
+ e = p.stderr.readlines()
+
+ # corrupted file(s) found.
+ if p.returncode == 1:
+ # messages must come through stdout.
+ for l in o:
+ log.info(l.replace('\n', ''))
+ return False
+
+ # other error
+ elif p.returncode >= 2:
+ for l in e:
+ log.error(l.replace('\n', ''))
+ return False
+
+ if len(o) > 0:
+ log.info(o[0].replace('\n', ''))
+
+ return True
+
+ def verify(self):
+ count = 0
+ corrupted = 0
+
+ d = DirectoryTree.DirectoryTree(self.path, self.recursive)
+ try:
+ filelist = d.get_file_list()
+ except OSError, err:
+ log.error(err)
+ sys.exit(2)
+
+ for f in filelist:
+ if self.check_filename(f) is True:
+ log.debug("verifing %s" % f)
+ if self.verify_one(f) is False:
+ corrupted = corrupted + 1
+
+ count = count + 1
+
+ log.info("Verified %d files. %d files corrupted." % (count, corrupted))
+
+ if corrupted == 0:
+ return True
+
+ return False
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [file]"
+ print " " + os.path.basename(sys.argv[0]) + " [option...] [directory]"
+ print ""
+ print "Options:"
+ print " -r, --recursive Find files recursively."
+ print " -v, --verbose Enable verbose output."
+ print ""
+ print " --help Print this help."
+ print ""
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "rv",
+ ["help", "debug", "recursive", "verbose"])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ recursive = False
+ verbose = False
+ debug = None
+
+ for o, a in opts:
+ if o in ("-r", "--recursive"):
+ recursive = True
+ elif o in ("-v", "--verbose"):
+ verbose = True
+ elif o in ("--debug"):
+ log.setLevel(log.DEBUG)
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(2)
+
+ if len(args) == 0:
+ usage()
+ sys.exit(0)
+
+ log.debug("recursive = %s" % str(recursive))
+ log.debug("verbose = %s" % str(verbose))
+
+ v = VerifyChecksum(args[0], recursive=recursive, verbose=verbose, debug=debug)
+
+ if v.verify() is False:
+ sys.exit(1)
+
+ sys.exit(0)
diff --git a/bin/pt-xact-stat b/bin/pt-xact-stat
new file mode 100755
index 0000000..34b635f
--- /dev/null
+++ b/bin/pt-xact-stat
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# pt-xact-stat
+#
+# 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 copy
+import getopt
+import time
+
+import log
+import PsqlWrapper
+
+class TransactionStatistics:
+ def __init__(self, psql, debug=False):
+ self.debug = debug
+ self.psql = psql
+ self.header = None
+
+ def get(self):
+ data = []
+
+ for p in self.psql:
+ log.debug("get: " + p.host)
+
+ query = ' \
+select \'%s\' as "HOST", \
+ \'%d\' as "PORT", \
+ datname as "DBNAME", \
+ numbackends as "CONN", \
+ xact_commit as "COMMITS", \
+ xact_rollback as "ROLLBACKS", \
+ blks_read AS "B_READ", \
+ blks_hit as "B_HIT" \
+ from pg_stat_database \
+ where datname = current_database() \
+;' % (p.host, p.port)
+
+ log.debug(query)
+
+ rs = p.execute_query(query, ignore_error=True)
+ if rs is None:
+ r = []
+ r.append(p.host)
+ r.append(str(p.port))
+ r.append(p.dbname)
+ data.append(r)
+ else:
+ if self.header is None:
+ self.header = rs[0]
+ log.debug(self.header)
+
+ data.append(rs[1])
+ log.debug(data)
+
+ if self.header is None:
+ log.error("Cannot connect to any server.")
+ sys.exit(1)
+
+ for d in data:
+ while len(d) < len(self.header):
+ d.append("")
+ log.debug(d)
+
+ data.insert(0, self.header)
+ p.print_result(data)
+ print("")
+
+ return True
+
+def usage():
+ print ""
+ print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [delay [count]]"
+ print ""
+ print "Options:"
+ print " -h, --host=HOSTNAME Host name of the postgres server"
+ print " -p, --port=PORT Port number of the postgres server"
+ print " -H, --host-list=HOSTLIST List of pairs of hostname and port number"
+ print " (c.f. host1:port1,host2:port2)"
+ print " -U, --username=USERNAME User name to connect"
+ print " -d, --dbname=DBNAME Database name to connect"
+ print ""
+ print " --help Print this help."
+ print ""
+
+def parse_host_list(s):
+ host_list = []
+ for h in s.split(','):
+ hp = h.split(':')
+
+ a = [];
+ if len(hp) == 2:
+ a.append(hp[0])
+ a.append(int(hp[1]))
+ if len(hp) == 1:
+ a.append(hp[0])
+ a.append(PsqlWrapper.get_port(None))
+
+ log.debug("host" + str(a))
+ host_list.append(a)
+
+ log.debug("host_list: " + str(host_list))
+
+ return host_list
+
+if __name__ == "__main__":
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "h:p:H:U:d:",
+ ["help", "debug", "host=", "port=", "host-list=", "username=", "dbname="])
+ except getopt.GetoptError, err:
+ print str(err)
+ usage()
+ sys.exit(2)
+
+ host = None
+ port = None
+ host_list = None
+ username = None
+ dbname = None
+
+ 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 ("-H", "--host-list"):
+ host_list = a
+ elif o in ("-U", "--username"):
+ username = a
+ elif o in ("-d", "--dbname"):
+ dbname = a
+ elif o in ("--debug"):
+ log.setLevel(log.DEBUG)
+ debug = True
+ elif o in ("--help"):
+ usage()
+ sys.exit(0)
+ else:
+ print "unknown option: " + o + "," + a
+ sys.exit(1)
+
+ delay = None
+ count = None
+
+ if len(args) >= 1:
+ delay = int(args[0])
+ if len(args) >= 2:
+ count = int(args[1])
+
+ psql_array = []
+
+ if host_list is not None:
+ for h in parse_host_list(host_list):
+ log.debug("foo" + str(h))
+ p = PsqlWrapper.PsqlWrapper(host=h[0], port=h[1], username=username, dbname=dbname, debug=debug)
+ psql_array.append(p)
+ else:
+ p = PsqlWrapper.PsqlWrapper(host=host, port=port, username=username, dbname=dbname, debug=debug)
+ psql_array.append(p)
+
+ i = 0
+ while True:
+ os.system("date")
+ stat = TransactionStatistics(psql_array, debug=debug)
+ stat.get()
+ i = i + 1
+
+ # just print once, and exit.
+ if delay is None:
+ break
+
+ if count is not None and i >= count:
+ break
+
+ try:
+ time.sleep(delay)
+ except KeyboardInterrupt, err:
+ log.info("Terminated.")
+ break
+
+ sys.exit(0)
diff --git a/bin/t/Makefile b/bin/t/Makefile
new file mode 100644
index 0000000..9618e70
--- /dev/null
+++ b/bin/t/Makefile
@@ -0,0 +1,17 @@
+include ../../Makefile.global
+
+all:
+ @grep -c ^function.test test-*.sh
+
+check:
+ ./regress.sh
+
+clean:
+ -killall postgres
+ rm -rf data
+ rm -rf *.out *.diff
+ rm -rf *~
+ rm -rf out?? data?? data??_slave
+ rm -rf *.log _postgresql.conf
+ rm -rf *.expected
+ rm -f snapshot.exp
diff --git a/bin/t/regress.sh b/bin/t/regress.sh
new file mode 100755
index 0000000..ea18ad8
--- /dev/null
+++ b/bin/t/regress.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+
+_PATH=$PATH
+LANG=C
+export LANG
+
+PGPORT=5433
+export PGPORT
+
+T="test-pt-config test-pt-index-usage test-pt-kill test-pt-proc-stat test-pt-replication-stat test-pt-session-profiler test-pt-set-tablespace test-pt-snap-statements test-pt-stat-snapshot test-pt-table-usage test-pt-tablespace-usage test-pt-verify-checksum test-pt-xact-stat"
+
+function _setUp()
+{
+ PGHOME=$1
+ PGDATA=$2
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ killall postgres
+
+ echo "Initializing a master..."
+ rm -rf $PGDATA /tmp/spc1
+ mkdir $PGDATA /tmp/spc1
+ initdb -D $PGDATA --no-locale -E utf-8 $_INITDB_OPTS
+
+ cat _postgresql.conf >> ${PGDATA}/postgresql.conf
+ echo "host replication $USER 127.0.0.1/32 trust" >> ${PGDATA}/pg_hba.conf
+
+ pg_ctl -D ${PGDATA} -w start -o "-p ${PGPORT}"
+ createdb testdb
+
+ # add a replica/slave
+ echo "Adding a slave..."
+
+ rm -rf ${PGDATA}_slave
+
+ psql -c 'checkpoint' postgres
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ # pg_basebackup does not support 9.0
+ psql -c "select pg_start_backup('regress')" postgres
+ cp -rv ${PGDATA} ${PGDATA}_slave
+ rm -f ${PGDATA}_slave/postmaster.pid
+ psql -c "select pg_stop_backup()" postgres
+ else
+ pg_basebackup -h 127.0.0.1 -p ${PGPORT} -U $USER -D ${PGDATA}_slave --xlog --progress --verbose
+ fi
+
+ echo "standby_mode = 'on'" > recovery.conf
+ echo "primary_conninfo = 'host=127.0.0.1 port=${PGPORT} user=$USER application_name=slave'" >> recovery.conf
+
+ cat recovery.conf
+ mv -v recovery.conf ${PGDATA}_slave
+
+ _PORT=`expr $PGPORT + 1`
+ echo pg_ctl -w -D ${PGDATA}_slave start -o -p${_PORT}
+ pg_ctl -w -D ${PGDATA}_slave start -o "-p ${_PORT}"
+}
+
+
+function _tearDown()
+{
+ PGHOME=$1
+ PGDATA=$2
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ pg_ctl -D ${PGDATA}_slave -w stop
+ pg_ctl -D ${PGDATA} -w stop
+}
+
+
+function testsuite()
+{
+ PGHOME=$1
+ PGDATA=$2
+ OUTDIR=$3
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ _MAJORVERSION=`pg_config --version | perl -e 's/.* (\d+\.\d+).*/\1/;' -p`
+ export _MAJORVERSION
+
+ _setUp $PGHOME $PGDATA > setup.log 2>&1
+
+ echo "=========================================="
+ echo "PGHOME: $PGHOME"
+ echo "PGDATA: $PGDATA"
+ echo "PATH: $PATH"
+ echo "=========================================="
+
+ _SUCCEEDED=0
+ _FAILED=0
+
+ for t in $T; do
+ ./${t}.sh
+ if [ $? -ne 0 ]; then
+ _FAILED=`expr $_FAILED + 1`
+ else
+ _SUCCEEDED=`expr $_SUCCEEDED + 1`
+ fi
+ done;
+
+ _tearDown $PGHOME $PGDATA > teardown.log 2>&1
+
+ rm -rf $OUTDIR
+ mkdir -p $OUTDIR
+ cp setup.log teardown.log *.out $OUTDIR
+
+ echo ==========================================
+ echo Version: $_MAJORVERSION, Succeeded: $_SUCCEEDED, Failed: $_FAILED
+ echo ==========================================
+}
+
+rm -rf *.out
+
+# -------------------------------------------------------
+# 9.0
+# -------------------------------------------------------
+cat /dev/null > _postgresql.conf
+echo "shared_preload_libraries = 'pg_stat_statements'" >> _postgresql.conf
+echo "wal_level = 'hot_standby'" >> _postgresql.conf
+echo "wal_keep_segments = 8" >> _postgresql.conf
+echo "max_wal_senders = 2" >> _postgresql.conf
+echo "hot_standby = on" >> _postgresql.conf
+if [ -d /usr/pgsql-9.0 ]; then
+ # RHEL/CentOS
+ testsuite /usr/pgsql-9.0 ./data90 ./out90
+elif [ -d /usr/lib/postgresql/9.0 ]; then
+ # Ubuntu
+ testsuite /usr/lib/postgresql/9.0 ./data90 ./out90
+else
+ echo "passing the regression tests for 9.0"
+fi
+
+# -------------------------------------------------------
+# 9.1
+# -------------------------------------------------------
+if [ -d /usr/pgsql-9.1 ]; then
+ # RHEL/CentOS
+ testsuite /usr/pgsql-9.1 ./data91 ./out91
+elif [ -d /usr/lib/postgresql/9.1 ]; then
+ # Ubuntu
+ testsuite /usr/lib/postgresql/9.1 ./data91 ./out91
+else
+ echo "passing the regression tests for 9.1"
+fi
+
+# -------------------------------------------------------
+# 9.2
+# -------------------------------------------------------
+echo "track_io_timing = on" >> _postgresql.conf
+if [ -d /usr/pgsql-9.2 ]; then
+ # RHEL/CentOS
+ testsuite /usr/pgsql-9.2 ./data92 ./out92
+elif [ -d /usr/lib/postgresql/9.2 ]; then
+ # Ubuntu
+ testsuite /usr/lib/postgresql/9.2 ./data92 ./out92
+else
+ echo "passing the regression tests for 9.2"
+fi
+
+# -------------------------------------------------------
+# 9.3
+# -------------------------------------------------------
+# Checksum support
+_INITDB_OPTS="-k"
+if [ -d /usr/pgsql-9.3 ]; then
+ # RHEL/CentOS
+ testsuite /usr/pgsql-9.3 ./data93 ./out93
+elif [ -d /usr/lib/postgresql/9.3 ]; then
+ # Ubuntu
+ testsuite /usr/lib/postgresql/9.3 ./data93 ./out93
+else
+ echo "passing the regression tests for 9.3"
+fi
+
+# -------------------------------------------------------
+# 9.4
+# -------------------------------------------------------
+if [ -d /usr/pgsql-9.4 ]; then
+ # RHEL/CentOS
+ testsuite /usr/pgsql-9.4 ./data94 ./out94
+elif [ -d /usr/lib/postgresql/9.4 ]; then
+ # Ubuntu
+ testsuite /usr/lib/postgresql/9.4 ./data94 ./out94
+else
+ echo "passing the regression tests for 9.4"
+fi
diff --git a/bin/t/test-pt-config.sh b/bin/t/test-pt-config.sh
new file mode 100755
index 0000000..dfc180a
--- /dev/null
+++ b/bin/t/test-pt-config.sh
@@ -0,0 +1,258 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+. ./test_common.sh
+
+function _setUp()
+{
+ echo "PATH=$PATH"
+ echo "PGHOME=$PGHOME"
+ echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+}
+
+function testConfig001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config --help > $OUT
+ assertEquals 0 $?
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-config [option...] [command] [param [value]]
+
+Commands:
+ get [PARAM] Get a current value of a parameter.
+ set [PARAM] [VALUE] Set a new value for a parameter.
+ disable [PARAM] Comment a parameter out.
+
+Options:
+ -D, --pgdata=PGDATA Specify a PostgreSQL database cluster.
+ --apply Apply change(s).
+
+ --help Print this help.
+
+EOF
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testConfig002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA get shared_buffers > $OUT
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.2" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.3" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ fi
+
+ pt-config --pgdata $PGDATA get shared_buffers > $OUT
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.2" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.3" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ fi
+
+ _PGDATA=$PGDATA
+ export PGDATA
+ pt-config get shared_buffers > $OUT
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.2" ]; then
+ contains ^32MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.3" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains ^128MB $OUT
+ assertTrue $?
+ fi
+}
+
+function testConfig003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # not found
+ pt-config -D $PGDATA get shared_buffer > $OUT
+ assertEquals 1 $?
+
+ # argument missing
+ pt-config -D $PGDATA get > $OUT
+ contains 'Usage: pt-config \[option...\] \[command\] \[param \[value\]\]' $OUT
+ assertTrue $?
+
+ # directory not found
+ pt-config -D nosuchdir get shared_buffers > $OUT
+ assertEquals 1 $?
+}
+
+function testConfig004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA set shared_buffers 256MB > $OUT
+ assertEquals 0 $?
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains '^shared_buffers = 32MB' $PGDATA/postgresql.conf
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ contains '^shared_buffers = 32MB' $PGDATA/postgresql.conf
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.2" ]; then
+ contains '^shared_buffers = 32MB' $PGDATA/postgresql.conf
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.3" ]; then
+ contains '^shared_buffers = 128MB' $PGDATA/postgresql.conf
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains '^shared_buffers = 128MB' $PGDATA/postgresql.conf
+ assertTrue $?
+ fi
+}
+
+function testConfig005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA --apply set shared_buffers 256MB > $OUT
+ assertEquals 0 $?
+
+ contains '^shared_buffers = 256MB' $PGDATA/postgresql.conf
+ assertTrue $?
+}
+
+function testConfig006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA --apply set shared_buffer 128MB > $OUT
+ assertEquals 1 $?
+
+ contains '^shared_buffers = 256MB' $PGDATA/postgresql.conf
+ assertTrue $?
+}
+
+function testConfig007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+
+ pt-config -D $PGDATA disable shared_buffers > $OUT
+ assertEquals 0 $?
+
+ contains '^shared_buffers = 256MB' $PGDATA/postgresql.conf
+ assertTrue $?
+}
+
+function testConfig008()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA --apply disable shared_buffers > $OUT
+ assertEquals 0 $?
+
+ contains '^#shared_buffers = 256MB' $PGDATA/postgresql.conf
+ assertTrue $?
+}
+
+function testConfig009()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA --apply disable shared_buffer > $OUT
+ assertEquals 1 $?
+
+ contains '^#shared_buffers = 256MB' $PGDATA/postgresql.conf
+ assertTrue $?
+}
+
+function testConfig010()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # missing argument
+ pt-config -D $PGDATA set shared_buffers > $OUT 2>&1
+ contains 'Usage: pt-config \[option...\] \[command\] \[param \[value\]\]' $OUT
+ assertTrue $?
+}
+
+# test for multiple pairs.
+function testConfig011()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA set shared_buffers 32MB > $OUT 2>&1
+
+ echo "shared_buffers = 64MB" >> $PGDATA/postgresql.conf
+
+ pt-config -D $PGDATA get shared_buffers > $OUT 2>&1
+
+ # set by testConfig009
+ contains '^256MB (disabled)' $OUT
+ assertTrue $?
+
+ contains '^64MB$' $OUT
+ assertTrue $?
+}
+
+# test for quoted string
+function testConfig012()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-config -D $PGDATA --apply set shared_preload_libraries "pg_stat_statements,autoexplain" > $OUT 2>&1
+ grep ^shared_preload_libraries $PGDATA/postgresql.conf >> $OUT 2>&1
+
+ contains "shared_preload_libraries = 'pg_stat_statements'" $OUT
+ assertTrue $?
+ contains "shared_preload_libraries = pg_stat_statements" $OUT
+ assertFalse $?
+
+ pt-config -D $PGDATA --apply set log_line_prefix "[%t] %p: %u/%d: " >> $OUT 2>&1
+ grep ^log_line_prefix $PGDATA/postgresql.conf >> $OUT 2>&1
+
+ contains "log_line_prefix = '\[%t\] %p: %u/%d: '" $OUT
+ assertTrue $?
+ contains "log_line_prefix = \[%t\] %p: %u/%d: " $OUT
+ assertFalse $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-index-usage.sh b/bin/t/test-pt-index-usage.sh
new file mode 100755
index 0000000..81edbf7
--- /dev/null
+++ b/bin/t/test-pt-index-usage.sh
@@ -0,0 +1,192 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ pgbench -i >> setUp.log 2>&1
+
+ psql<<EOF >> setUp.log
+CREATE SCHEMA nsp1;
+CREATE TABLESPACE spc1 LOCATION '/tmp/spc1';
+SELECT * FROM pg_tablespace;
+
+ALTER INDEX pgbench_accounts_pkey SET TABLESPACE spc1;
+EOF
+}
+
+function tearDown()
+{
+ psql<<EOF >> tearDown.log
+ALTER INDEX pgbench_accounts_pkey SET TABLESPACE pg_default;
+
+DROP TABLESPACE spc1;
+DROP SCHEMA nsp1;
+EOF
+
+ rm -rf /tmp/spc1/*
+}
+
+function testIndexUsage001()
+{
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-index-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+ -i, --index=STRING Index name
+
+ --help Print this help.
+
+EOF
+
+ pt-index-usage --help > ${_SHUNIT_TEST_}.out
+
+ diff -rc ${_SHUNIT_TEST_}.expected ${_SHUNIT_TEST_}.out
+ assertEquals 0 $?
+}
+
+function testIndexUsage002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # To avoid following error.
+ # ERROR: index "pgbench_accounts_pkey" contains unexpected zero page at block 0
+ # HINT: Please REINDEX it.
+ reindexdb > $OUT 2>&1
+
+ pt-index-usage -h localhost > $OUT 2>&1
+
+ COUNT=`grep -c pgbench_accounts_pkey $OUT`
+ assertEquals 1 $COUNT
+
+ COUNT=`grep -c pgbench_branches_pkey $OUT`
+ assertEquals 1 $COUNT
+
+ COUNT=`grep -c pgbench_tellers_pkey $OUT`
+ assertEquals 1 $COUNT
+
+ SCAN=`grep pgbench_accounts_pkey $OUT | awk '{ print $14 }'`
+ T_READ=`grep pgbench_accounts_pkey $OUT | awk '{ print $16 }'`
+ T_FTCH=`grep pgbench_accounts_pkey $OUT | awk '{ print $18 }'`
+
+ psql -c 'select * from pgbench_accounts where aid = 1' > /dev/null
+ sleep 1
+
+ pt-index-usage --host localhost >> $OUT 2>&1
+
+ SCAN2=`grep pgbench_accounts_pkey $OUT | tail -1 | awk '{ print $14 }'`
+ T_READ2=`grep pgbench_accounts_pkey $OUT | tail -1 | awk '{ print $16 }'`
+ T_FTCH2=`grep pgbench_accounts_pkey $OUT | tail -1 | awk '{ print $18 }'`
+
+ assertTrue "[ $SCAN2 -gt $SCAN ]"
+ assertTrue "[ $T_READ2 -gt $T_READ ]"
+ assertTrue "[ $T_FTCH2 -gt $T_FTCH ]"
+}
+
+
+function testIndexUsage003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -p $PGPORT > $OUT 2>&1
+ pt-index-usage --port $PGPORT >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+function testIndexUsage004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -U $USER > $OUT 2>&1
+ pt-index-usage --username $USER >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+function testIndexUsage005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -d testdb > $OUT 2>&1
+ pt-index-usage --dbname testdb >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+function testIndexUsage006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -o $USER > $OUT 2>&1
+ pt-index-usage --owner $USER >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+function testIndexUsage007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -n public > $OUT 2>&1
+ pt-index-usage --schema public >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+function testIndexUsage008()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -t pgbench_accounts > $OUT 2>&1
+ pt-index-usage --table pgbench_accounts >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 10 $COUNT
+}
+
+function testIndexUsage009()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -i pgbench_branches_pkey > $OUT 2>&1
+ pt-index-usage --index pgbench_branches_pkey >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 10 $COUNT
+}
+
+function testIndexUsage010()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-index-usage -u > $OUT 2>&1
+ pt-index-usage --unused >> $OUT 2>&1
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 14 $COUNT
+}
+
+. shunit2
diff --git a/bin/t/test-pt-kill.sh b/bin/t/test-pt-kill.sh
new file mode 100755
index 0000000..8f78a83
--- /dev/null
+++ b/bin/t/test-pt-kill.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+. ./test_common.sh
+
+function _setUp()
+{
+ echo "PATH=$PATH"
+ echo "PGHOME=$PGHOME"
+ echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+}
+
+function testKill001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-kill --help > $OUT
+ assertEquals 0 $?
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-kill [option...] [command] [pid]
+
+Commands:
+ cancel Cancel a running query.
+ terminate Terminate a backend with canceling query.
+
+Options:
+ --help Print this help.
+
+EOF
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testKill002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -e -c 'select pg_sleep(10)' postgres > $OUT 2>&1 &
+ sleep 1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ q="select procpid from pg_stat_activity where current_query like '%sleep%' and procpid <> pg_backend_pid()"
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ q="select procpid from pg_stat_activity where current_query like '%sleep%' and procpid <> pg_backend_pid()"
+ else
+ q="select pid from pg_stat_activity where query like '%sleep%' and pid <> pg_backend_pid()"
+ fi
+
+ _PID=`psql -A -t -c "$q" postgres`
+
+ pt-kill cancel $_PID
+
+ contains 'ERROR: canceling statement due to user request' $OUT
+ assertTrue $?
+}
+
+function testKill003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -e -c 'select pg_sleep(10)' postgres > $OUT 2>&1 &
+ sleep 1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ q="select procpid from pg_stat_activity where current_query like '%sleep%' and procpid <> pg_backend_pid()"
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ q="select procpid from pg_stat_activity where current_query like '%sleep%' and procpid <> pg_backend_pid()"
+ else
+ q="select pid from pg_stat_activity where query like '%sleep%' and pid <> pg_backend_pid()"
+ fi
+
+ _PID=`psql -A -t -c "$q" postgres`
+
+ pt-kill terminate $_PID
+
+ contains 'FATAL: terminating connection due to administrator command' $OUT
+ assertTrue $?
+}
+
+function testKill004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # No such process error
+ pt-kill terminate 32769
+ assertFalse $?
+}
+
+function testKill005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ _UID=`id -u`
+ if [ $_UID -eq 0 ]; then
+ echo "!!! CAUTION: DO NOT RUN TEST WITH SUPERUSER ACCOUNT !!!"
+ fail
+ return
+ fi
+
+ # Operation not permitted error
+ pt-kill terminate 1
+ assertFalse $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-proc-stat.sh b/bin/t/test-pt-proc-stat.sh
new file mode 100755
index 0000000..5f07450
--- /dev/null
+++ b/bin/t/test-pt-proc-stat.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+function _setUp()
+{
+ echo "PATH=$PATH"
+ echo "PGHOME=$PGHOME"
+ echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+}
+
+function testProcStat001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-proc-stat --help > $OUT
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-proc-stat [option...] [delay [count]]
+
+Options:
+ -D, --pgdata=DATADIR Location of the database storage area
+ -P, --pid=PID Process ID of the postmaster
+
+ --help Print this help.
+
+EOF
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testProcStat002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ sudo env PATH=$PATH pt-proc-stat > $OUT
+
+ grep 'postmaster\|postgres' ${_SHUNIT_TEST_}.out > /dev/null
+ assertEquals 0 $?
+
+ grep stats.collector $OUT > /dev/null
+ assertEquals 0 $?
+}
+
+function testProcStat003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ sudo env PATH=$PATH pt-proc-stat -D $PGDATA > $OUT
+
+ grep 'postmaster\|postgres' $OUT > /dev/null
+ assertEquals 0 $?
+
+ grep stats.collector ${_SHUNIT_TEST_}.out > /dev/null
+ assertEquals 0 $?
+}
+
+function testProcStat004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+ PID=`ps auxx | grep bin/post | grep -v grep | sort | head -1 | awk '{ print $2 }'`
+
+ sudo env PATH=$PATH pt-proc-stat -P $PID > $OUT
+
+ grep 'postmaster\|postgres' $OUT > /dev/null
+ assertEquals 0 $?
+
+ grep stats.collector ${_SHUNIT_TEST_}.out > /dev/null
+ assertEquals 0 $?
+
+ sudo env PATH=$PATH pt-proc-stat --pid $PID > $OUT
+
+ grep 'postmaster\|postgres' $OUT > /dev/null
+ assertEquals 0 $?
+
+ grep stats.collector ${_SHUNIT_TEST_}.out > /dev/null
+ assertEquals 0 $?
+}
+
+function testProcStat005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ sudo env PATH=$PATH pt-proc-stat -D nosuchdir > $OUT
+ assertEquals 1 $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-replication-stat.sh b/bin/t/test-pt-replication-stat.sh
new file mode 100755
index 0000000..7490fde
--- /dev/null
+++ b/bin/t/test-pt-replication-stat.sh
@@ -0,0 +1,199 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ pgbench -i >> setUp.log 2>&1
+
+ install_pg_stat_statements >> setUp.log 2>&1
+}
+
+function testReplicationStat001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-replication-stat [option...] [delay [count]]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+EOF
+
+ pt-replication-stat --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testReplicationStat002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-replication-stat -h localhost 1 1 > $OUT 2>&1
+# +-------+-------+-----------+-------+-----------+------------+------------+------------+-----------+-----+--------+
+# | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+# +-------+-------+-----------+-------+-----------+------------+------------+------------+-----------+-----+--------+
+# | | | | | local | 0/101D9D00 | 0/101D9D00 | | | | master |
+# | 28463 | slave | 127.0.0.1 | 59821 | streaming | 0/101D9D00 | 0/101D9D00 | 0/101D9D00 | 0/FFFFFD0 | 0 | async |
+# +-------+-------+-----------+-------+-----------+------------+------------+------------+-----------+-----+--------+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+
+ pt-replication-stat --host localhost 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+}
+
+function testReplicationStat003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-replication-stat -p 5433 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+
+ pt-replication-stat --port 5433 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+}
+
+function testReplicationStat004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-replication-stat -U $USER 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+
+ pt-replication-stat --username $USER 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+}
+
+function testReplicationStat005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-replication-stat -d testdb 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+
+ pt-replication-stat --dbname testdb 1 1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.0" ]; then
+ contains 'ERROR: PostgreSQL 9.0 is not supported.' $OUT
+ assertTrue $?
+ else
+ contains 'master' $OUT
+ assertTrue $?
+ contains 'slave | 127.0.0.1' $OUT
+ assertTrue $?
+ contains 'streaming' $OUT
+ assertTrue $?
+ contains 'async' $OUT
+ assertTrue $?
+ fi
+}
+
+. shunit2
diff --git a/bin/t/test-pt-session-profiler.sh b/bin/t/test-pt-session-profiler.sh
new file mode 100755
index 0000000..9f5d3e9
--- /dev/null
+++ b/bin/t/test-pt-session-profiler.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+}
+
+function testSessionProfiler001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-session-profiler [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -i STRING Interface name to listen
+ -T, --threshold=NUMBER Threashold of query elapsed time (in millisecond)
+
+ --help Print this help.
+
+EOF
+
+ pt-session-profiler --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testSessionProfiler002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-session-profiler -h 192.168.1.201 > $OUT 2>&1
+ contains 'INFO: tcpdump -tttt -l -i any -s 0 -X -p tcp port 5432 and host 192.168.1.201' $OUT
+ assertTrue $?
+
+ pt-session-profiler --host 192.168.1.201 > $OUT 2>&1
+ contains 'INFO: tcpdump -tttt -l -i any -s 0 -X -p tcp port 5432 and host 192.168.1.201' $OUT
+ assertTrue $?
+}
+
+function testSessionProfiler003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-session-profiler -p 5433 > $OUT 2>&1
+ contains 'INFO: tcpdump -tttt -l -i any -s 0 -X -p tcp port 5433' $OUT
+ assertTrue $?
+
+ pt-session-profiler --port 5433 > $OUT 2>&1
+ contains 'INFO: tcpdump -tttt -l -i any -s 0 -X -p tcp port 5433' $OUT
+ assertTrue $?
+
+ assertEquals 0 $?
+}
+
+function testSessionProfiler004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-session-profiler -i lo > $OUT 2>&1
+ contains 'INFO: tcpdump -tttt -l -i lo -s 0 -X -p tcp port 5432' $OUT
+ assertTrue $?
+
+ assertEquals 0 $?
+}
+
+function testSessionProfiler005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-session-profiler -T 2000 > $OUT 2>&1
+ contains 'INFO: Threshold: 2000 ms' $OUT
+ assertTrue $?
+
+ pt-session-profiler --threshold 3000 > $OUT 2>&1
+ contains 'INFO: Threshold: 3000 ms' $OUT
+ assertTrue $?
+
+ assertEquals 0 $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-set-tablespace.sh b/bin/t/test-pt-set-tablespace.sh
new file mode 100755
index 0000000..51a1152
--- /dev/null
+++ b/bin/t/test-pt-set-tablespace.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+source ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ rm -rf /tmp/spc1
+ mkdir -p /tmp/spc1
+
+ psql<<EOF >> setUp.log
+CREATE SCHEMA nsp1;
+CREATE TABLESPACE spc1 LOCATION '/tmp/spc1';
+SELECT * FROM pg_tablespace;
+
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+
+SELECT relname,spcname FROM pg_class c LEFT OUTER JOIN pg_tablespace t ON c.reltablespace = t.oid WHERE relname LIKE 't1%';
+EOF
+}
+
+function tearDown()
+{
+ psql<<EOF >> tearDown.log
+DROP TABLE t1;
+DROP TABLESPACE spc1;
+DROP SCHEMA nsp1;
+EOF
+}
+
+function testSetTablespace001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-set-tablespace [option...] [tablespace]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+
+ -l, --list List table spaces
+ --apply Apply change(s)
+
+ --help Print this help.
+
+EOF
+
+ pt-set-tablespace --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testSetTablespace002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-set-tablespace --list > $OUT 2>&1
+
+ grep spc1 $OUT | grep /tmp/spc1 > /dev/null
+ assertEquals 0 $?
+}
+
+function testSetTablespace003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ #_DEBUG="--debug"
+ pt-set-tablespace $_DEBUG -t t1 spc1 > $OUT 2>&1
+
+ psql<<EOF >> $OUT
+SELECT relname,spcname FROM pg_class c LEFT OUTER JOIN pg_tablespace t ON c.reltablespace = t.oid WHERE relname LIKE 't1%';
+EOF
+
+ contains 'ALTER TABLE "public"."t1" SET TABLESPACE "spc1";' $OUT
+ assertTrue $?
+ contains 'ALTER INDEX "public"."t1_pkey" SET TABLESPACE "spc1";' $OUT
+ assertTrue $?
+
+ contains ' t1 | $' $OUT
+ assertTrue $?
+ contains ' t1_pkey | $' $OUT
+ assertTrue $?
+}
+
+function testSetTablespace004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # move t1 to spc1
+ pt-set-tablespace $_DEBUG -t t1 --apply spc1 > $OUT 2>&1
+
+ psql<<EOF >> $OUT
+SELECT relname,spcname FROM pg_class c LEFT OUTER JOIN pg_tablespace t ON c.reltablespace = t.oid WHERE relname LIKE 't1%';
+EOF
+
+ contains ' t1 | spc1$' $OUT
+ assertTrue $?
+ contains ' t1_pkey | spc1$' $OUT
+ assertTrue $?
+}
+
+function testSetTablespace005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ # move t1 back to pg_default
+ pt-set-tablespace $_DEBUG --table t1 --apply pg_default > $OUT 2>&1
+
+ psql<<EOF >> $OUT
+SELECT relname,spcname FROM pg_class c LEFT OUTER JOIN pg_tablespace t ON c.reltablespace = t.oid WHERE relname LIKE 't1%';
+EOF
+
+ contains ' t1 | $' $OUT
+ assertTrue $?
+ contains ' t1_pkey | $' $OUT
+ assertTrue $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-snap-statements.sh b/bin/t/test-pt-snap-statements.sh
new file mode 100755
index 0000000..6f6e76d
--- /dev/null
+++ b/bin/t/test-pt-snap-statements.sh
@@ -0,0 +1,253 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ pgbench -i >> setUp.log 2>&1
+
+ install_pg_stat_statements >> setUp.log 2>&1
+
+ psql -c 'select pg_stat_statements_reset()' >> setUp.log 2>&1
+}
+
+
+function testSnapStatement001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-snap-statements [option...] [interval]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -t, --top=NUMBER Number of queries to be listed
+ -R, --reset Reset statistics
+
+ --help Print this help.
+
+EOF
+
+ pt-snap-statements --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected ${_SHUNIT_TEST_}.out
+ assertEquals 0 $?
+}
+
+function wait_and_exec()
+{
+ WAIT=$1
+ QUERY=$2
+
+ sleep $WAIT
+ psql -c "$QUERY" > /dev/null
+}
+
+function testSnapStatement002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements -h 127.0.0.1 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements --host 127.0.0.1 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+}
+
+function testSnapStatement003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements -p $PGPORT 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements --port $PGPORT 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+}
+
+function testSnapStatement004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements -U $USER 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements --username $USER 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+}
+
+function testSnapStatement005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+# -t, --top=NUMBER Number of queries to be listed
+# -R, --reset Reset statistics
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements -d testdb 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ pt-snap-statements --dbname testdb 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ fi
+}
+
+function testSnapStatement006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+# -t, --top=NUMBER Number of queries to be listed
+# -R, --reset Reset statistics
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ wait_and_exec 1 "select count(*) from pgbench_branches" &
+ wait_and_exec 1 "select count(*) from pgbench_history" &
+ pt-snap-statements -t 1 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_b | 1 ' $OUT
+ assertFalse $?
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_h | 1 ' $OUT
+ assertFalse $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | select count(\*) from pgbench_b | 1 ' $OUT
+ assertFalse $?
+ contains ' | testdb | select count(\*) from pgbench_h | 1 ' $OUT
+ assertFalse $?
+ fi
+
+ wait_and_exec 1 "select count(*) from pgbench_accounts" &
+ wait_and_exec 1 "select count(*) from pgbench_branches" &
+ wait_and_exec 1 "select count(*) from pgbench_history" &
+ pt-snap-statements -t 2 2 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.4" ]; then
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_b | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | .\{7,8\} | select count(\*) from pgbench_h | 1 ' $OUT
+ assertFalse $?
+ else
+ contains ' | testdb | select count(\*) from pgbench_a | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | select count(\*) from pgbench_b | 1 ' $OUT
+ assertTrue $?
+ contains ' | testdb | select count(\*) from pgbench_h | 1 ' $OUT
+ assertFalse $?
+ fi
+}
+
+function testSnapStatement007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c "select count(*) from pgbench_accounts" > /dev/null
+ psql -c "select count(*) from pgbench_branches" > /dev/null
+ psql -c "select count(*) from pgbench_history" > /dev/null
+ psql -A -t -q -c 'select count(*) from pg_stat_statements' > $OUT 2>&1
+
+ pt-snap-statements --reset > $OUT 2>&1
+ sleep 1
+
+ psql -A -t -q -c 'select count(*) from pg_stat_statements' > $OUT 2>&1
+
+ contains '^1$' $OUT
+ assertTrue $?
+
+ psql -c "select count(*) from pgbench_accounts" > /dev/null
+ psql -c "select count(*) from pgbench_branches" > /dev/null
+ psql -c "select count(*) from pgbench_history" > /dev/null
+ psql -A -t -q -c 'select count(*) from pg_stat_statements' > $OUT 2>&1
+
+ pt-snap-statements -R > $OUT 2>&1
+ sleep 1
+
+ psql -A -t -q -c 'select count(*) from pg_stat_statements' > $OUT 2>&1
+
+ contains '^1$' $OUT
+ assertTrue $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-stat-snapshot.sh b/bin/t/test-pt-stat-snapshot.sh
new file mode 100755
index 0000000..6ce54f6
--- /dev/null
+++ b/bin/t/test-pt-stat-snapshot.sh
@@ -0,0 +1,187 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+
+ install_pg_stat_statements >> setUp.log 2>&1
+ install_pgstattuple >> setUp.log 2>&1
+
+ ps auxx > setUp.log
+}
+
+function testStatSnapshot001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-stat-snapshot [option...] install
+ pt-stat-snapshot [option...] create [level]
+ pt-stat-snapshot [option...] list
+ pt-stat-snapshot [option...] delete [sid]
+ pt-stat-snapshot [option...] export [file]
+ pt-stat-snapshot [option...] import [file]
+ pt-stat-snapshot [option...] uninstall
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+EOF
+
+ pt-stat-snapshot --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testStatSnapshot002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug install > $OUT 2>&1
+ assertTrue $?
+}
+
+function testStatSnapshot003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug create > $OUT 2>&1
+ assertFalse $?
+ contains 'ERROR: Missing argument.' $OUT
+ assertTrue $?
+
+ sleep 1
+
+ pt-stat-snapshot --debug create 1 > $OUT 2>&1
+ contains 'INFO: Succeeded to take a snapshot.' $OUT
+ assertTrue $?
+
+ sleep 1
+
+ pt-stat-snapshot --debug create 2 > $OUT 2>&1
+ contains 'INFO: Succeeded to take a snapshot.' $OUT
+ assertTrue $?
+
+ sleep 1
+
+ pt-stat-snapshot --debug create 4 > $OUT 2>&1
+ contains 'INFO: Succeeded to take a snapshot.' $OUT
+ assertTrue $?
+
+ sleep 1
+
+ psql -c 'select * from pgperf.snapshot order by sid' > $OUT
+ contains ' 0 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 1' $OUT
+ assertTrue $?
+ contains ' 1 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 2' $OUT
+ assertTrue $?
+ contains ' 2 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 4' $OUT
+ assertTrue $?
+}
+
+function testStatSnapshot004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug list > $OUT 2>&1
+ contains '| SID | TIMESTAMP | LEVEL |' $OUT
+ assertTrue $?
+ contains '| 0 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\} | 1 |' $OUT
+ assertTrue $?
+ contains '| 1 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\} | 2 |' $OUT
+ assertTrue $?
+ contains '| 2 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\} | 4 |' $OUT
+ assertTrue $?
+}
+
+function testStatSnapshot005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug delete > $OUT 2>&1
+ assertFalse $?
+ contains 'ERROR: Missing argument.' $OUT
+ assertTrue $?
+
+ # delete sid 0
+ pt-stat-snapshot --debug delete 0 > $OUT 2>&1
+ assertTrue $?
+
+ psql -c 'select * from pgperf.snapshot order by sid' >> $OUT
+ contains ' 0 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 1' $OUT
+ assertFalse $?
+ contains ' 1 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 2' $OUT
+ assertTrue $?
+ contains ' 2 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 4' $OUT
+ assertTrue $?
+
+ # delete sid 1 and later
+ pt-stat-snapshot --debug delete 1: > $OUT 2>&1
+ assertTrue $?
+ psql -c 'select * from pgperf.snapshot order by sid' >> $OUT
+ contains ' 0 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 1' $OUT
+ assertFalse $?
+ contains ' 1 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 2' $OUT
+ assertFalse $?
+ contains ' 2 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 4' $OUT
+ assertFalse $?
+}
+
+function testStatSnapshot006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug create 4 > $OUT 2>&1
+ assertTrue $?
+
+ pt-stat-snapshot --debug export snapshot.exp > $OUT 2>&1
+ assertTrue $?
+
+ contains 'INFO: Exporting done.' $OUT
+ assertTrue $?
+}
+
+function testStatSnapshot007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug delete 0: > $OUT 2>&1
+ assertTrue $?
+
+ pt-stat-snapshot --debug import snapshot.exp > $OUT 2>&1
+ assertTrue $?
+
+ contains 'INFO: Succeeded to import snapshot(s).' $OUT
+ assertTrue $?
+
+ psql -c 'select * from pgperf.snapshot order by sid' >> $OUT
+ contains ' 0 | [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} [0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}.[0-9 ]\{1,6\} | 4' $OUT
+ assertTrue $?
+}
+
+# dummy test to clean up.
+function testStatSnapshot999()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-stat-snapshot --debug uninstall > $OUT 2>&1
+ assertTrue $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-table-usage.sh b/bin/t/test-pt-table-usage.sh
new file mode 100755
index 0000000..9ccc9c6
--- /dev/null
+++ b/bin/t/test-pt-table-usage.sh
@@ -0,0 +1,157 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ pgbench -i -n >> setUp.log 2>&1
+}
+
+function testTableUsage001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage --help > $OUT
+
+ cat<<EOF > ${_SHUNIT_TEST_}.expected
+
+Usage: pt-table-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+
+ --help Print this help.
+
+EOF
+
+ diff -rc ${_SHUNIT_TEST_}.expected ${_SHUNIT_TEST_}.out
+ assertEquals 0 $?
+}
+
+function testTableUsage002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ sleep 1
+ pt-table-usage -h localhost > $OUT
+
+ COUNT=`grep -c pgbench_accounts $OUT`
+ assertEquals 1 $COUNT
+
+ COUNT=`grep -c pgbench_branches $OUT`
+ assertEquals 1 $COUNT
+
+ COUNT=`grep -c pgbench_history $OUT`
+ assertEquals 1 $COUNT
+
+ COUNT=`grep -c pgbench_tellers $OUT`
+ assertEquals 1 $COUNT
+
+ SCAN=`grep pgbench_accounts $OUT | awk '{ print $12 }'`
+ T_READ=`grep pgbench_accounts $OUT | awk '{ print $14 }'`
+ T_INS=`grep pgbench_accounts $OUT | awk '{ print $16 }'`
+ T_UPD=`grep pgbench_accounts $OUT | awk '{ print $18 }'`
+ assertEquals 1 $SCAN
+ assertEquals 100000 $T_READ
+ assertEquals 100000 $T_INS
+ assertEquals 0 $T_UPD
+
+ psql -c 'select * from pgbench_accounts' > /dev/null
+
+ sleep 1
+ pt-table-usage --host localhost >> $OUT
+
+ SCAN2=`grep pgbench_accounts $OUT | tail -1 | awk '{ print $12 }'`
+ T_READ2=`grep pgbench_accounts $OUT | tail -1 | awk '{ print $14 }'`
+ T_INS2=`grep pgbench_accounts $OUT | tail -1 | awk '{ print $16 }'`
+ T_UPD2=`grep pgbench_accounts $OUT | tail -1 | awk '{ print $18 }'`
+ assertEquals 2 $SCAN2
+ assertEquals 200000 $T_READ2
+ assertEquals 100000 $T_INS2
+ assertEquals 0 $T_UPD2
+
+# cat $OUT
+}
+
+function testTableUsage003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -p $PGPORT > $OUT
+ pt-table-usage --port $PGPORT >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 16 $COUNT
+}
+
+function testTableUsage004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -U $USER > $OUT
+ pt-table-usage --username $USER >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 16 $COUNT
+}
+
+function testTableUsage005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -d postgres > $OUT
+ pt-table-usage --dbname postgres >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 8 $COUNT
+}
+
+function testTableUsage006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -o $USER > $OUT
+ pt-table-usage --owner $USER >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 16 $COUNT
+}
+
+function testTableUsage007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -n public > $OUT
+ pt-table-usage --schema public >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 16 $COUNT
+}
+
+function testTableUsage008()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-table-usage -t pgbench_accounts > $OUT
+ pt-table-usage --table pgbench_accounts >> $OUT
+
+ COUNT=`wc -l $OUT | awk '{ print $1 }'`
+ assertEquals 10 $COUNT
+}
+
+. shunit2
diff --git a/bin/t/test-pt-tablespace-usage.sh b/bin/t/test-pt-tablespace-usage.sh
new file mode 100755
index 0000000..2655cdb
--- /dev/null
+++ b/bin/t/test-pt-tablespace-usage.sh
@@ -0,0 +1,136 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+
+ pgbench -i -n >> setUp.log 2>&1
+}
+
+function testTablespaceUsage001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-tablespace-usage --help > $OUT
+
+ cat<<EOF > ${_SHUNIT_TEST_}.expected
+
+Usage: pt-tablespace-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+EOF
+
+ diff -rc ${_SHUNIT_TEST_}.expected ${_SHUNIT_TEST_}.out
+ assertEquals 0 $?
+}
+
+function testTablespaceUsage002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-tablespace-usage -h localhost > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+
+ pt-tablespace-usage --host localhost > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+}
+
+function testTablespaceUsage003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-tablespace-usage -p $PGPORT > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+
+ pt-tablespace-usage --port $PGPORT > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+}
+
+function testTablespaceUsage004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-tablespace-usage -U $USER > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+
+ pt-tablespace-usage --username $USER > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+}
+
+function testTablespaceUsage005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ pt-tablespace-usage -d postgres > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+
+ pt-tablespace-usage --dbname postgres > $OUT
+
+ contains '| pg_default | postgres |' $OUT
+ assertTrue $?
+ contains '| pg_default | template1 |' $OUT
+ assertTrue $?
+ contains '| pg_default | testdb |' $OUT
+ assertTrue $?
+}
+
+. shunit2
diff --git a/bin/t/test-pt-verify-checksum.sh b/bin/t/test-pt-verify-checksum.sh
new file mode 100755
index 0000000..6b337f9
--- /dev/null
+++ b/bin/t/test-pt-verify-checksum.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+source ./test_common.sh
+
+function _version_check()
+{
+ if [ $_MAJORVERSION == "9.0" ]; then
+ return 0
+ elif [ $_MAJORVERSION == "9.1" ]; then
+ return 0
+ elif [ $_MAJORVERSION == "9.2" ]; then
+ return 0
+ fi
+
+ return 1
+}
+
+function testVerifyChecksum001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ _version_check
+ if [ $? -eq 0 ]; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ cat<<EOF > ${_SHUNIT_TEST_}.expected
+
+Usage: pt-verify-checksum [option...] [file]
+ pt-verify-checksum [option...] [directory]
+
+Options:
+ -r, --recursive Find files recursively.
+ -v, --verbose Enable verbose output.
+
+ --help Print this help.
+
+EOF
+
+ pt-verify-checksum --help > $OUT
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testVerifyChecksum002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ _version_check
+ if [ $? -eq 0 ]; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ psql -c 'checkpoint'
+ pt-verify-checksum $PGDATA/base/1 > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.3" ]; then
+ contains 'Verified 2[0-9][0-9] files. 0 files corrupted.' $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains 'Verified 2[0-9][0-9] files. 0 files corrupted.' $OUT
+ assertTrue $?
+ else
+ fail
+ fi
+}
+
+function testVerifyChecksum003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ _version_check
+ if [ $? -eq 0 ]; then
+ return ${SHUNIT_TRUE}
+ fi
+
+ psql -c 'checkpoint'
+ pt-verify-checksum -r $PGDATA/base > $OUT 2>&1
+
+ if [ $_MAJORVERSION == "9.3" ]; then
+ contains 'Verified 9[0-9][0-9] files. 0 files corrupted.' $OUT
+ assertTrue $?
+ elif [ $_MAJORVERSION == "9.4" ]; then
+ contains 'Verified 9[0-9][0-9] files. 0 files corrupted.' $OUT
+ assertTrue $?
+ else
+ fail
+ fi
+}
+
+. shunit2
diff --git a/bin/t/test-pt-xact-stat.sh b/bin/t/test-pt-xact-stat.sh
new file mode 100755
index 0000000..908a8ba
--- /dev/null
+++ b/bin/t/test-pt-xact-stat.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+PATH=$PATH:..:../../deps/shunit2-2.1.6/src
+export PATH
+
+export PGDATABASE=testdb
+
+. ./test_common.sh
+
+function setUp()
+{
+# echo "PATH=$PATH"
+# echo "PGHOME=$PGHOME"
+# echo "PGDATA=$PGDATA"
+ ps auxx > setUp.log
+}
+
+function testXactStat001()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ cat<<EOF >${_SHUNIT_TEST_}.expected
+
+Usage: pt-xact-stat [option...] [delay [count]]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -H, --host-list=HOSTLIST List of pairs of hostname and port number
+ (c.f. host1:port1,host2:port2)
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+EOF
+
+ pt-xact-stat --help > $OUT 2>&1
+
+ diff -rc ${_SHUNIT_TEST_}.expected $OUT
+ assertEquals 0 $?
+}
+
+function testXactStat002()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat -h localhost 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 2 | 0 |' $OUT
+ assertTrue $?
+
+ # wait for stat update
+ sleep 1
+
+ pt-xact-stat --host localhost 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 4 | 0 |' $OUT
+ assertTrue $?
+}
+
+function testXactStat003()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat -p 5433 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 2 | 0 |' $OUT
+ assertTrue $?
+
+ # wait for stat update
+ sleep 1
+
+ pt-xact-stat --port 5433 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 4 | 0 |' $OUT
+ assertTrue $?
+}
+
+function testXactStat004()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat -U $USER 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 2 | 0 |' $OUT
+ assertTrue $?
+
+ # wait for stat update
+ sleep 1
+
+ pt-xact-stat --username $USER 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 4 | 0 |' $OUT
+ assertTrue $?
+}
+
+function testXactStat005()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat -d testdb 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 2 | 0 |' $OUT
+ assertTrue $?
+
+ # wait for stat update
+ sleep 1
+
+ pt-xact-stat --dbname testdb 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 4 | 0 |' $OUT
+ assertTrue $?
+}
+
+function testXactStat006()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 2 | 0 |' $OUT
+ assertTrue $?
+
+ # wait for stat update
+ sleep 1
+
+ pt-xact-stat 1 1 > $OUT 2>&1
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS |' $OUT
+ assertTrue $?
+ contains '| localhost | 5433 | testdb | 1 | 4 | 0 |' $OUT
+ assertTrue $?
+}
+
+function testXactStat007()
+{
+ OUT=${_SHUNIT_TEST_}.out
+
+ psql -c 'select pg_stat_reset()' > $OUT
+ sleep 1
+
+ pt-xact-stat -H 127.0.0.1:5433,127.0.0.1:5434 1 1 > $OUT 2>&1
+
+ contains '| HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |' $OUT
+ assertTrue $?
+ contains '| 127.0.0.1 | 5433 | testdb | 1 | ' $OUT
+ assertTrue $?
+ contains '| 127.0.0.1 | 5434 | testdb | 1 | ' $OUT
+ assertTrue $?
+}
+
+. shunit2
diff --git a/bin/t/test_common.sh b/bin/t/test_common.sh
new file mode 100644
index 0000000..6e11230
--- /dev/null
+++ b/bin/t/test_common.sh
@@ -0,0 +1,44 @@
+function contains()
+{
+ STRING=$1
+ FILE=$2
+
+ grep "$STRING" $FILE >> contains.log
+ # found
+ if [ $? -eq 0 ]; then
+ # 0
+ return ${SHUNIT_TRUE}
+ # not found
+ elif [ $? -eq 1 ]; then
+ # 1
+ return ${SHUNIT_FALSE}
+ # error
+ else
+ # 2
+ return ${SHUNIT_ERROR}
+ fi
+}
+
+function install_pg_stat_statements()
+{
+ _SHARE_DIR=`$PGHOME/bin/pg_config --sharedir`
+
+ if [ -f $_SHARE_DIR/contrib/pg_stat_statements.sql ]; then
+ psql -f $_SHARE_DIR/contrib/pg_stat_statements.sql
+ else
+ psql -c 'create extension pg_stat_statements'
+ fi
+}
+
+function install_pgstattuple()
+{
+ _SHARE_DIR=`$PGHOME/bin/pg_config --sharedir`
+
+ if [ -f $_SHARE_DIR/contrib/pgstattuple.sql ]; then
+ psql -f $_SHARE_DIR/contrib/pgstattuple.sql
+ else
+ psql -c 'create extension pgstattuple'
+ fi
+}
+
+
diff --git a/deps/Makefile b/deps/Makefile
new file mode 100644
index 0000000..18533c6
--- /dev/null
+++ b/deps/Makefile
@@ -0,0 +1,9 @@
+all:
+ @if [ ! -d shunit2-2.1.6 ]; then \
+ tar zxvf shunit2-2.1.6.tgz; \
+ patch -p0 < shunit2-2.1.6.diff; \
+ fi
+
+clean:
+ rm -rf shunit2-2.1.6
+
diff --git a/deps/shunit2-2.1.6.diff b/deps/shunit2-2.1.6.diff
new file mode 100644
index 0000000..df84fce
--- /dev/null
+++ b/deps/shunit2-2.1.6.diff
@@ -0,0 +1,13 @@
+diff -rc shunit2-2.1.6.orig/src/shunit2 shunit2-2.1.6/src/shunit2
+*** shunit2-2.1.6.orig/src/shunit2 2011-05-01 20:10:33.000000000 +0000
+--- shunit2-2.1.6/src/shunit2 2015-04-13 01:07:52.704733895 +0000
+***************
+*** 783,788 ****
+--- 783,789 ----
+
+ # execute the test
+ echo "${_shunit_test_}"
++ export _SHUNIT_TEST_=${_shunit_test_}
+ eval ${_shunit_test_}
+
+ # execute the per-test tear-down function
diff --git a/deps/shunit2-2.1.6.tgz b/deps/shunit2-2.1.6.tgz
new file mode 100644
index 0000000..d2445a6
--- /dev/null
+++ b/deps/shunit2-2.1.6.tgz
Binary files differ
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..9c7bd77
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,16 @@
+TOPDIR=..
+
+include $(TOPDIR)/Makefile.global
+
+all:
+ make -C en html
+ make -C ja html
+
+clean:
+ make -C en clean
+ make -C ja clean
+
+install:
+ make -C en install
+ make -C ja install
+
diff --git a/docs/en/Makefile b/docs/en/Makefile
new file mode 100644
index 0000000..1b383e6
--- /dev/null
+++ b/docs/en/Makefile
@@ -0,0 +1,187 @@
+include ../../Makefile.global
+
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PostgresToolkit.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PostgresToolkit.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/PostgresToolkit"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PostgresToolkit"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+all: html
+
+install:
+ mkdir -p $(PREFIX)/docs
+ rm -rf $(PREFIX)/docs/en
+ rm -rf _build/html/_sources
+ cp -rv _build/html $(PREFIX)/docs/en
diff --git a/docs/en/conf.py b/docs/en/conf.py
new file mode 100644
index 0000000..a7e03b3
--- /dev/null
+++ b/docs/en/conf.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+#
+# Postgres Toolkit documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 07 14:44:31 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Postgres Toolkit'
+copyright = u'2015, Uptime Technologies, LLC'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.2.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'PostgresToolkitdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'PostgresToolkit.tex', u'Postgres Toolkit Documentation',
+ u'Uptime Technologies, LLC', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'postgrestoolkit', u'Postgres Toolkit Documentation',
+ [u'Uptime Technologies, LLC'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'PostgresToolkit', u'Postgres Toolkit Documentation',
+ u'Uptime Technologies, LLC', 'PostgresToolkit', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs/en/index.rst b/docs/en/index.rst
new file mode 100644
index 0000000..af1b73a
--- /dev/null
+++ b/docs/en/index.rst
@@ -0,0 +1,43 @@
+.. Postgres Toolkit documentation master file, created by
+ sphinx-quickstart on Sat Feb 07 14:44:31 2015.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Postgres Toolkit
+================
+
+Postgres Toolkit is a collection of script and command that can easily perform on the complex DBA works when performing PostgreSQL server operation management, performance tuning and Troubleshooting.
+
+If you have any questions, requests or report bugs about Postgres Toolkit, please email me or use Github issues.
+email:``postgres-toolkit at uptime dot jp``
+Github: http://www.github.com/uptimejp/postgres-toolkit/
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ install
+ pt-config
+ pt-index-usage
+ pt-kill
+ pt-proc-stat
+ pt-replication-stat
+ pt-session-profiler
+ pt-set-tablespace
+ pt-snap-statements
+ pt-stat-snapshot
+ pt-table-usage
+ pt-tablespace-usage
+ pt-verify-checksum
+ pt-xact-stat
+ releasenote
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/docs/en/install.rst b/docs/en/install.rst
new file mode 100644
index 0000000..ba3906e
--- /dev/null
+++ b/docs/en/install.rst
@@ -0,0 +1,51 @@
+
+Install the Toolkit
+===================
+
+
+Supported OS
+------------
+
+For a list of Operating System supported.
+
+* Red Hat Enterprise Linux 6 / CentOS 6
+* Red Hat Enterprise Linux 7 / CentOS 7
+* Ubuntu 14.04 LTS
+
+Make sure you have Python2.6 or Python2.7 is installed.
+
+
+PostgreSQL Version
+------------------
+
+For a list of PostgreSQL supported.
+
+* PostgreSQL 9.0
+* PostgreSQL 9.1
+* PostgreSQL 9.2
+* PostgreSQL 9.3
+* PostgreSQL 9.4
+
+
+Installation
+------------
+
+Use following command, you can install the toolkit via the Internet.
+
+Run
+
+.. code-block:: none
+
+ curl -L http://dl.uptimeforce.com/postgres-toolkit/install.sh | sh
+
+or
+
+.. code-block:: none
+
+ wget http://dl.uptimeforce.com/postgres-toolkit/install.sh
+ sh install.sh
+
+When the installs are done, copying related files under ``/opt/uptime/postgres-toolkit-<VERSION>``.
+
+
+
diff --git a/docs/en/make.bat b/docs/en/make.bat
new file mode 100644
index 0000000..bc9cc6b
--- /dev/null
+++ b/docs/en/make.bat
@@ -0,0 +1,242 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PostgresToolkit.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PostgresToolkit.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs/en/pgperf_functions.rst b/docs/en/pgperf_functions.rst
new file mode 100644
index 0000000..83d409b
--- /dev/null
+++ b/docs/en/pgperf_functions.rst
@@ -0,0 +1,162 @@
+Snapshot Functions
+==================
+
+This chapter is intended to give detailed information about several snapshot functions, provided in the PgPerf package, to deal with performance snaphsots.
+
+Snapshot Function List
+----------------------
+
+===================================== ======================================
+Function Name Description
+===================================== ======================================
+pgperf.create_snapshot(level) Takes a new snapshot.
+pgperf.delete_snapshot(snapid) Drops a snapshot specified by the snapshot id.
+pgperf.purge_snapshots(interval) Drops snapshots older than the specified period.
+pgperf.get_interval(snapid1, snapid2) Gets an interval (in second) between two snapshots.
+===================================== ======================================
+
+
+pgperf.create_snapshot() Function
+---------------------------------
+
+Description
+^^^^^^^^^^^
+
+``pgperf.create_snapshot()`` function takes a snapshot of the performance statistics, which can be obtained in the PostgreSQL database, and stores them in the snapshot tables.
+
+Declaration
+^^^^^^^^^^^
+
+::
+
+ integer pgperf.create_snapshot(integer level)
+
+Parameters
+^^^^^^^^^^
+
+======== ======== ================================
+Name Type Description
+======== ======== ================================
+level integer Snapshot level to be taken.
+======== ======== ================================
+
+Unfortunately, heavy performance impact could sometimes be generated while taking a snapshot for all the performance statistics available in the database.
+
+To avoid that, ``pgperf.create_snapshot()`` function allows DBA to obtain a snapshot with less performance impact by specifying the snapshot level. Then, DBA can avoid taking a snapshot which generates heavy performance impact so frequently.
+
+============== =====================================================================
+Snapshot Level Snapshot Contents
+============== =====================================================================
+1 Obtains a snapshot of the basic access statistics and the session statistics.
+
+ pg_stat_database, pg_database_size()
+
+ pg_stat_user_tables, pg_statio_user_tables
+
+ pg_stat_user_indexes, pg_statio_user_indexes
+
+ pg_relation_size(), pg_total_relation_size()
+
+ pg_current_xlog_location(), pg_current_xlog_insert_location()
+
+ pg_stat_bgwriter
+
+ pg_stat_activity, pg_locks, pg_stat_statements
+
+
+2 In addition to the level 1, obtains a snapshot of the optimizer statistics.
+
+ pg_statistic
+
+3 Not used.
+4 In addition to the level 2, obtains a snapshot of the table/index fragmentation statistics.
+
+ pgstattuple(), pgstatindex()
+
+5 Not used.
+============== =====================================================================
+
+
+
+pgperf.delete_snapshot() Function
+---------------------------------
+
+Description
+^^^^^^^^^^^
+
+``pgperf.delete_snapshot()`` function deletes a snapshot of the performance statistics specified by the snapshot id.
+
+Declaration
+^^^^^^^^^^^
+
+::
+
+ integer pgperf.delete_snapshot(integer snapid);
+
+Parameters
+^^^^^^^^^^
+
+======== ======== ================================
+Name Type Description
+======== ======== ================================
+snapid integer A snapshot id accociated with the snapshot to be deleted.
+======== ======== ================================
+
+
+pgperf.purge_snapshots() Function
+---------------------------------
+
+Description
+^^^^^^^^^^^
+
+``pgperf.purge_snapshots()`` function purges older performance snapshots at once.
+
+Declaration
+^^^^^^^^^^^
+::
+
+ integer pgperf.purge_snapshots(interval period);
+
+Parameters
+^^^^^^^^^^
+
+======== ======== ================================
+Name Type Description
+======== ======== ================================
+period interval A period of the snapshots to be kept in the snapshot tables.
+======== ======== ================================
+
+This function drops snapshots older than the period (interval) specified by the parameter ``period`` .
+
+See the PostgreSQL manual for more details about how to express an ``interval`` value.
+
+* `PostgreSQL: Documentation: 9.0: Date/Time Types <http://www.postgresql.org/docs/9.0/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT>`_
+
+
+pgperf.get_interval() Function
+------------------------------
+
+Description
+^^^^^^^^^^^
+
+``pgperf.get_interval()`` function obtains an interval between two snapshots.
+
+This function is intended to give some convenience to convert an obtained value into a 'per-second' value, particularly in SQL scripts.
+
+
+Declaration
+^^^^^^^^^^^
+::
+
+ integer pgperf.get_interval(integer snapid1, integer snapid2)
+
+Parameters
+^^^^^^^^^^
+
+======== ======== ================================
+Name Type Description
+======== ======== ================================
+snapid1 integer A snapshot id at the start point.
+snapid2 integer A snapshot id at the end point.
+======== ======== ================================
+
diff --git a/docs/en/pgperf_intro.rst b/docs/en/pgperf_intro.rst
new file mode 100644
index 0000000..2d90974
--- /dev/null
+++ b/docs/en/pgperf_intro.rst
@@ -0,0 +1,147 @@
+Introduction to PgPerf package
+==============================
+
+This chapter is intended to give a brief introduction to the PgPerf snapshot package.
+
+Installing PgPerf package
+-------------------------
+
+To install the PgPerf package, ``pgperf_snapshot_install.sql`` script needs to be executed on a database, where the DBA wants to take performance snapshots, to create ``pgperf`` schema, snapshot functions, and snapshot tables.
+
+.. code-block:: none
+
+ psql -f pgperf_snapshot_install<VERSION>.sql <DBNAME>
+
+Uninstalling PgPerf package
+---------------------------
+
+To uninstall the PgPerf package, ``pgperf_snapshot_uninstall.sql`` script needs to be executed on the database where the PgPerf package has been installed.
+
+.. code-block:: none
+
+ psql -f pgperf_snapshot_uninstall.sql <DBNAME>
+
+Once ``pgperf_snapshot_uninstall.sql`` is executed on a database, it would drops``pgperf`` schema, snapshot functions and snapshot tables installed in the database.
+
+Taking a performance snapshot with using PgPerf package
+-------------------------------------------------------
+
+In order to take a performance snapshot, a SQL function ``pgperf.create_snapshot()`` needs to be called.
+
+Once ``pgperf.create_snapshot()`` function is called with specifying a snapshot level, a snapshot of the several performance statistics would be taken, and a snapshot id associated with the performance snaphsot would be returned.
+
+In the following example, a snapshot level is specified as ``4``, and then, ``1005`` is returned as the snapshot id.
+
+::
+
+ postgres=# SELECT pgperf.create_snapshot(4);
+ create_snapshot
+ -----------------
+ 1005
+ (1 row)
+
+ postgres=#
+
+
+Showing a list for snapshots
+----------------------------
+
+``pgperf.snapshot`` table would hold a list of the snapshots.
+
+By querying the table, snapshot ids and those timestamps can be obtained as following.
+
+::
+
+ postgres=# SELECT * FROM pgperf.snapshot;
+ sid | ts | level
+ -----+----------------------------+-------
+ 0 | 2015-04-11 19:11:24.04428 | 1
+ 1 | 2015-04-11 19:11:24.060965 | 2
+ 2 | 2015-04-11 19:11:24.110034 | 4
+ (3 rows)
+
+ postgres=#
+
+
+Deleting a snapshot
+-------------------
+
+To delete a snapshot, ``pgperf.delete_snapshot()`` function with specifying a snapshot id can be used.
+
+In the following example, a snapshot associated with snaphsot id ``2`` is going to be dropped.
+
+::
+
+ postgres=# SELECT pgperf.delete_snapshot(2);
+ delete_snapshot
+ -----------------
+ t
+ (1 row)
+
+ postgres=# SELECT * FROM pgperf.snapshot;
+ sid | ts | level
+ -----+----------------------------+-------
+ 0 | 2015-04-11 19:11:24.04428 | 1
+ 1 | 2015-04-11 19:11:24.060965 | 2
+ (2 rows)
+
+ postgres=#
+
+
+Taking snapshots as a routine
+-----------------------------
+
+To take a snapshot as a routine, ``pgperf.create_snapshot()`` function needs to be called with using a cron-like tool.
+
+A shell script,``get_snapshot.sh``, which is contained in the package, executes ``pgperf.create_snapshot()`` function on the specified database or all databases, which can be connected by the script (and not a template database).
+
+In the following example, the script is taking a snapshot for ``postgres`` database.
+
+::
+
+$ ./get_snapshot.sh postgres
+
+On the other hand, the script is taking a snapshot for every database in the following example.
+
+::
+
+$ ./get_snapshot.sh
+
+By setting a crontab, ``get_snapshot.sh`` can be called to take snapshots periodically.
+
+A crontab shown in below is intended to take performance snapshots of all the databases with calling ``get_snapshot.sh`` script in every 10 minutes.
+
+::
+
+ 0-59/10 * * * * /path/to/get_snapshot.sh > /dev/null 2>&1
+
+
+Purging snapshots
+-----------------
+
+``pgperf.purge_snapshots()`` function can be used to purge older snapshots at once.
+
+In the following example, snapshots, which are older than 1 week or more, are going to be purged.
+
+::
+
+ postgres=# SELECT sid,ts FROM pgperf.snapshot ORDER BY ts LIMIT 1;
+ sid | ts
+ -----+----------------------------
+ 2 | 2012-10-21 18:20:01.238885
+ (1 row)
+
+ postgres=# SELECT now(),pgperf.purge_snapshots('1 weeks');
+ now | purge_snapshots
+ -------------------------------+-----------------
+ 2012-10-29 14:57:04.092243+09 | 121
+ (1 row)
+
+ postgres=# SELECT sid,ts FROM pgperf.snapshot ORDER BY ts LIMIT 1;
+ sid | ts
+ -----+--------------------------
+ 123 | 2012-10-22 15:00:01.8397
+ (1 row)
+
+ postgres=#
+
diff --git a/docs/en/pgperf_overview.rst b/docs/en/pgperf_overview.rst
new file mode 100644
index 0000000..6875f0e
--- /dev/null
+++ b/docs/en/pgperf_overview.rst
@@ -0,0 +1,49 @@
+PgPerf Package Overview
+=======================
+
+What is PgPerf package?
+-----------------------
+
+The PgPerf package is a collection of SQL functions and tables to take snapshots of the performance statistics in PostgreSQL, and store them into dedicated snapshot tables.
+
+"Performance statistics" mentioned here is following statistics available in PostgreSQL:
+
+ * Database object access statistics in system views. (c.f. pg_stat_database)
+ * Optimizer statistics in pg_statistic system table.
+ * Other system statistics which could by taken by system functions. (c.f. pg_current_xlog_location())
+ * Fragmentation statistics which could be taken by system functions. (c.f. pgstattuple())
+
+By taking those performance statistics and storing them, DBA can understand what's actually going on inside the PostgreSQL databases, and also it would help DBA predict the future trend in terms of the performance.
+
+
+Advantages of PgPerf package
+----------------------------
+
+The PgPerf package has following advantages:
+
+ * It does not depend the platform where PostgreSQL is running. Provided scripts (SQL, PL/pgSQL) should work whereever you want.
+ * It's easy to take and store several performance metrics, and it's also easy to analyze them later whatever you want.
+ * It's easy to deploy (and undeploy) without changing any configuration in the production environment.
+
+
+Supported Platform
+------------------
+
+The PgPerf package would work with following platforms.
+
+ * PostgreSQL 9.0, 9,1, 9.2, 9.3, 9.4
+
+It does not depend on the operating system.
+
+
+PgPerf Components
+-----------------
+
+The PgPerf package consists of following components:
+
+ * Dedicated schema to be used by the package.
+ * SQL functions to take performance snapshots.
+ * Snapshot tables to store performace snapshots.
+ * Some shell scripts for convenience.
+
+
diff --git a/docs/en/pgperf_tables.rst b/docs/en/pgperf_tables.rst
new file mode 100644
index 0000000..6e53a6e
--- /dev/null
+++ b/docs/en/pgperf_tables.rst
@@ -0,0 +1,441 @@
+Snapshot Tables
+===============
+
+This chapter is intended to give detailed information about several snapshot tables, where the performance snapshot to be stored.
+
+Snapshot Table List
+-------------------
+
+Performance snapshot is going to be stored in the following tables associated with each performance statistics available in PostgreSQL.
+
+.. csv-table::
+ :header-rows: 1
+
+ Table Name, Description, Note
+ pgperf.snapshot, Stores snapshot id and timestamp of the performance snapshot.
+ pgperf.snapshot_pg_stat_database, Stores a snapshot of the pg_stat_database system view.
+ pgperf.snapshot_pg_database_size, Stores a snapshot of the database size.
+ pgperf.snapshot_pg_stat_user_tables, Stores a snapshot of the pg_stat_user_tables system view.
+ pgperf.snapshot_pg_statio_user_tables, Stores a snapshot of the pg_statio_user_tables system view.
+ pgperf.snapshot_pg_stat_user_indexes, Stores a snapshot of the pg_stat_user_indexes system view.
+ pgperf.snapshot_pg_statio_user_indexes, Stores a snapshot of the pg_statio_user_indexes system view.
+ pgperf.snapshot_pg_statio_user_sequences, Stores a snapshot of the pg_statio_user_sequences system view.
+ pgperf.snapshot_pg_stat_user_functions, Stores a snapshot of the pg_stat_user_functions system view.
+ pgperf.snapshot_pg_relation_size, Stores a snaphsot of the table and index size.
+ pgperf.snapshot_pg_current_xlog, Stores a snapshot of the current xlog location and the current insert location.
+ pgperf.snapshot_pg_stat_bgwriter, Stores a snapshot of the pg_stat_bgwriter system view.
+ pgperf.snapshot_pg_stat_activity, Stores a snapshot of the pg_stat_activity system view.
+ pgperf.snapshot_pg_locks, Stores a snapshot of the pg_locks system view.
+ pgperf.snapshot_pg_statistic, Stores a snapshot of the pg_statistics system table.
+ pgperf.snapshot_pg_stat_statements, Stores a snapshot of the pg_stat_statements view., 8.4 or later
+ pgperf.snapshot_pgstattuple, Stores a snapshot of the result of pgstattuple function.
+ pgperf.snapshot_pgstatindex, Stores a snapshot of the result of pgstatindex function.
+
+pgperf.snapshot Table
+---------------------
+
+This table stores snapshot id and timestamp of each snapshot taken by the snapshot function.
+
+===================== ================ ========================= ============
+Column Name Type Source Note
+===================== ================ ========================= ============
+sid integer Snapshot ID Monotone increasing
+ts timestamp Timestamp of the snapshot
+level integer Snapshot level
+===================== ================ ========================= ============
+
+
+
+pgperf.snapshot_pg_stat_database Table
+--------------------------------------
+
+This table stores snapshots of the ``pg_stat_database`` system view which contains the database access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+datid oid pg_stat_database.datid
+datname name pg_stat_database.datname
+numbackends integer pg_stat_database.numbackends
+xact_commit bigint pg_stat_database.xact_commit
+xact_rollback bigint pg_stat_database.xact_rollback
+blks_read bigint pg_stat_database.blks_read
+blks_hit bigint pg_stat_database.blks_hit
+tup_returned bigint pg_stat_database.tup_returned
+tup_fetched bigint pg_stat_database.tup_fetched
+tup_inserted bigint pg_stat_database.tup_inserted
+tup_updated bigint pg_stat_database.tup_updated
+tup_deleted bigint pg_stat_database.tup_deleted
+conflicts bigint pg_stat_database.conflicts 9.1 or later
+stats_reset timestampz pg_stat_database.stats_reset 9.1 or later
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_database_size Table
+--------------------------------------
+
+This table stores snapshots of result of the ``pg_database_size()`` function which gets database size.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+datname name pg_database.datname
+pg_database_size bigint pg_database_size()
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_tables Table
+-----------------------------------------
+
+This table stores snapshots of the ``pg_stat_user_tables`` system view which contains the table access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+relid oid pg_stat_user_tables.relid
+schemaname name pg_stat_user_tables.schemaname
+relname name pg_stat_user_tables.relname
+seq_scan bigint pg_stat_user_tables.seq_scan
+seq_tup_read bigint pg_stat_user_tables.seq_tup_read
+idx_scan bigint pg_stat_user_tables.idx_scan
+idx_tup_fetch bigint pg_stat_user_tables.idx_tup_fetch
+n_tup_ins bigint pg_stat_user_tables.n_tup_ins
+n_tup_upd bigint pg_stat_user_tables.n_tup_upd
+n_tup_del bigint pg_stat_user_tables.n_tup_del
+n_tup_hot_upd bigint pg_stat_user_tables.n_tup_hot_upd
+n_live_tup bigint pg_stat_user_tables.n_live_tup
+n_dead_tup bigint pg_stat_user_tables.n_dead_tup
+last_vacuum timestampz pg_stat_user_tables.last_vacuum
+last_autovacuum timestampz pg_stat_user_tables.last_autovacuum
+last_analyze timestampz pg_stat_user_tables.last_analyze
+last_autoanalyze timestampz pg_stat_user_tables.last_autoanalyze
+vacuum_count bigint pg_stat_user_tables.vacuum_count 9.1 or later
+autovacuum_count bigint pg_stat_user_tables.autovacuum_count 9.1 or later
+analyze_count bigint pg_stat_user_tables.analyze_count 9.1 or later
+autoanalyze_count bigint pg_stat_user_tables.autoanalyze_count 9.1 or later
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_statio_user_tables Table
+-------------------------------------------
+
+This table stores snapshots of the ``pg_statio_user_tables`` system view which contains the table access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+relid oid pg_statio_user_tables.relid
+schemaname name pg_statio_user_tables.schemaname
+relname name pg_statio_user_tables.relname
+heap_blks_read bigint pg_statio_user_tables.heap_blks_read
+heap_blks_hit bigint pg_statio_user_tables.heap_blks_hit
+idx_blks_read bigint pg_statio_user_tables.idx_blks_read
+idx_blks_hit bigint pg_statio_user_tables.idx_blks_hit
+toast_blks_read bigint pg_statio_user_tables.toast_blks_read
+toast_blks_hit bigint pg_statio_user_tables.toast_blks_hit
+tidx_blks_read bigint pg_statio_user_tables.tidx_blks_read
+tidx_blks_hit bigint pg_statio_user_tables.tidx_blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_indexes Table
+------------------------------------------
+
+This table stores snapshots of the ``pg_stat_user_indexes`` system view which contains the index access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+relid oid pg_stat_user_indexes.relid
+indexrelid oid pg_stat_user_indexes.indexrelid
+schemaname name pg_stat_user_indexes.schemaname
+relname name pg_stat_user_indexes.relname
+indexrelname name pg_stat_user_indexes.indexrelname
+idx_scan bigint pg_stat_user_indexes.idx_scan
+idx_tup_read bigint pg_stat_user_indexes.idx_tup_read
+idx_tup_fetch bigint pg_stat_user_indexes.idx_tup_fetch
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_statio_user_indexes Table
+--------------------------------------------
+
+This table stores snapshots of the ``pg_statio_user_indexes`` system view which contains the index access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+relid oid pg_statio_user_indexes.relid
+indexrelid oid pg_statio_user_indexes.indexrelid
+schemaname name pg_statio_user_indexes.schemaname
+relname name pg_statio_user_indexes.relname
+indexrelname name pg_statio_user_indexes.indexrelname
+idx_blks_read bigint pg_statio_user_indexes.idx_blks_read
+idx_blks_hit bigint pg_statio_user_indexes.idx_blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_statio_user_sequences Table
+----------------------------------------------
+
+This table stores snapshots of the ``pg_statio_user_sequences`` system view which contains the sequence access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+relid oid pg_statio_user_sequences.relid
+schemaname name pg_statio_user_sequences.schemaname
+relname name pg_statio_user_sequences.relname
+blks_read int8 pg_statio_user_sequences.blks_read
+blks_hit int8 pg_statio_user_sequences.blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_functions Table
+--------------------------------------------
+
+This table stores snapshots of the ``pg_stat_user_functions`` system view which contains the function access statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+funcid oid pg_stat_user_functions.funcid
+schemaname name pg_stat_user_functions.schemaname
+funcname name pg_stat_user_functions.funcname
+calls int8 pg_stat_user_functions.calls
+total_time int8 pg_stat_user_functions.total_time
+self_time int8 pg_stat_user_functions.self_time
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_relation_size Table
+--------------------------------------
+
+This table stores snapshots of the result of ``pg_relation_size()`` and ``pg_total_relation_size()`` function which gets table and/or index size.
+
+====================== ================ ====================================== =============================
+Column Name Type Source Note
+====================== ================ ====================================== =============================
+sid integer Snapshot ID
+schemaname name pg_stat_user_tables.schemaname,
+ pg_stat_user_indexes.schemaname
+relid oid pg_stat_user_tables.relid,
+ pg_stat_user_indexes.indexrelid
+relname name pg_class.relname
+pg_relation_size bigint pg_relaion_size()
+pg_total_relation_size bigint pg_total_relaion_size() Available only for tables
+====================== ================ ====================================== =============================
+
+
+pgperf.snapshot_pg_current_xlog Table
+-------------------------------------
+
+This table stores snapshots of the result of ``pg_current_xlog_location()`` and ``pg_current_xlog_insert_location()`` function which gets current WAL locations.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snaphsot ID
+location text pg_current_xlog_location()
+insert_location text pg_current_xlog_insert_location()
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_stat_bgwriter Table
+--------------------------------------
+
+This table stores snapshots of the ``pg_stat_bgwriter`` system view which contains the background writer statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+checkpoints_timed bigint pg_stat_bgwriter.checkpoints_timed
+checkpoints_req bigint pg_stat_bgwriter.checkpoints_req
+checkpoint_write_time double precision pg_stat_bgwriter.checkpoint_write_time 9.2 or later
+checkpoint_sync_time double precision pg_stat_bgwriter.checkpoint_sync_time 9.2 or later
+buffers_checkpoint bigint pg_stat_bgwriter.buffers_checkpoint
+buffers_clean bigint pg_stat_bgwriter.buffers_clean
+maxwritten_clean bigint pg_stat_bgwriter.maxwritten_clean
+buffers_backend bigint pg_stat_bgwriter.buffers_backend
+buffers_backend_fsync bigint pg_stat_bgwriter.buffers_backend_fsync 9.1 or later
+buffers_alloc bigint pg_stat_bgwriter.buffers_alloc
+stats_reset timestampz pg_stat_bgwriter.stats_reset 9.1 or later
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_activity Table
+--------------------------------------
+
+This table stores snapshots of the ``pg_stat_activity`` system view which contains the session information.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+datid oid pg_stat_activity.datid
+datname name pg_stat_activity.datname
+procpid int4 pg_stat_activity.procpid 9.1 or before
+pid int4 pg_stat_activity.pid 9.2 or later
+usesysid oid pg_stat_activity.usesysid
+usename name pg_stat_activity.usename
+application_name text pg_stat_activity.application_name 9.0 or later
+client_addr inet pg_stat_activity.client_addr
+client_hostname text pg_stat_activity.client_hostname 9.1 or later
+client_port int4 pg_stat_activity.client_port
+backend_start timestamptz pg_stat_activity.backend_start
+xact_start timestamptz pg_stat_activity.xact_start
+query_start timestamptz pg_stat_activity.query_start
+state_change timestamptz pg_stat_activity.state_change 9.2 or later
+waiting bool pg_stat_activity.waiting
+state text pg_stat_activity.state 9.2 or later
+current_query text pg_stat_activity.current_query 9.1 or before
+query text pg_stat_activity.query 9.2 or later
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_locks Table
+------------------------------
+
+This table stores snapshots of the ``pg_locks`` system view which contains the lock information.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+locktype text pg_locks.locktype
+database oid pg_locks.database
+relation oid pg_locks.relation
+page int4 pg_locks.page
+tuple int2 pg_locks.tuple
+virtualxid text pg_locks.virtualxid
+transactionid xid pg_locks.transactionid
+classid oid pg_locks.classid
+objid oid pg_locks.objid
+objsubid int2 pg_locks.objsubid
+virtualtransaction text pg_locks.virtualtransaction
+pid int4 pg_locks.pid
+mode text pg_locks.mode
+granted bool pg_locks.granted
+fastpath bool pg_locks.fastpath 9.2 or later
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_statistic Table
+----------------------------------
+
+This table stores snapshots of the ``pg_statistic`` system table which contains the optimizer statistics.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+starelid oid pg_statistic.starelid
+starelname name pg_class.relname
+staattnum smallint pg_statistic.staattnum
+staattname name pg_attribute.attname
+stainherit boolean pg_statistic.stainherit 9.0 or later
+stanullfrac real pg_statistic.stanullfrac
+stawidth integer pg_statistic.stawidth
+stadistinct real pg_statistic.stadistinct
+stakind1 smallint pg_statistic.stakind1
+stakind2 smallint pg_statistic.stakind2
+stakind3 smallint pg_statistic.stakind3
+stakind4 smallint pg_statistic.stakind4
+stakind5 smallint pg_statistic.stakind5 9.2 or later
+staop1 oid pg_statistic.staop1
+staop2 oid pg_statistic.staop2
+staop3 oid pg_statistic.staop3
+staop4 oid pg_statistic.staop4
+staop5 oid pg_statistic.staop5 9.2 or later
+stanumbers1 real[] pg_statistic.stanumbers1
+stanumbers2 real[] pg_statistic.stanumbers2
+stanumbers3 real[] pg_statistic.stanumbers3
+stanumbers4 real[] pg_statistic.stanumbers4
+stanumbers5 real[] pg_statistic.stanumbers5 9.2 or later
+stavalues1 text pg_statistic.stavalues1
+stavalues2 text pg_statistic.stavalues2
+stavalues3 text pg_statistic.stavalues3
+stavalues4 text pg_statistic.stavalues4
+stavalues5 text pg_statistic.stavalues5 9.2 or later
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_stat_statements Table
+----------------------------------------
+
+This table stores snapshots of the ``pg_stat_statements`` view which contains the session statistics. This table is available only when the ``pg_stat_statements`` module has been installed and enabled.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+userid oid pg_stat_statements.userid
+dbid oid pg_stat_statements.dbid
+query text pg_stat_statements.query
+calls bigint pg_stat_statements.calls
+total_time double precision pg_stat_statements.total_time
+rows bigint pg_stat_statements.rows
+shared_blks_hit bigint pg_stat_statements.shared_blks_hit
+shared_blks_read bigint pg_stat_statements.shared_blks_read
+shared_blks_dirtied bigint pg_stat_statements.shared_blks_dirtied 9.2 or later
+shared_blks_written bigint pg_stat_statements.shared_blks_written
+local_blks_hit bigint pg_stat_statements.local_blks_hit
+local_blks_read bigint pg_stat_statements.local_blks_read
+local_blks_dirtied bigint pg_stat_statements.local_blks_dirtied 9.2 or later
+local_blks_written bigint pg_stat_statements.local_blks_written
+temp_blks_read bigint pg_stat_statements.temp_blks_read
+temp_blks_written bigint pg_stat_statements.temp_blks_written
+blk_read_time double precision pg_stat_statements.blk_read_time 9.2 or later
+blk_write_time double precision pg_stat_statements.blk_write_time 9.2 or later
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pgstattuple Table
+---------------------------------
+
+This table stores snapshots of the result of the ``pgstattuple()`` function which gets the table fragmentation statistics. This table is available only when the ``pgstattuple`` module has been installed.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+schemaname name pg_stat_user_tables.schemaname
+relname name pg_stat_user_tables.relname
+table_len int8 pgstattuple().table_len
+tuple_count int8 pgstattuple().tuple_count
+tuple_len int8 pgstattuple().tuple_len
+tuple_percent float8 pgstattuple().tuple_percent
+dead_tuple_count int8 pgstattuple().dead_tuple_count
+dead_tuple_len int8 pgstattuple().dead_tuple_len
+dead_tuple_percent float8 pgstattuple().dead_tuple_percent
+free_space int8 pgstattuple().free_space
+free_percent float8 pgstattuple().free_percent
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pgstatindex Table
+---------------------------------
+
+This table stores snapshots of the result of the ``pgstatindex()`` function which gets the index fragmentation statistics. This table is available only when the ``pgstattuple`` module has been installed.
+
+===================== ================ ====================================== ===========
+Column Name Type Source Note
+===================== ================ ====================================== ===========
+sid integer Snapshot ID
+schemaname name pg_stat_user_indexes.schemaname
+relname name pg_stat_user_indexes.relname
+indexrelname name pg_stat_user_indexes.indexrelname
+version int4 pgstatindex().version
+tree_level int4 pgstatindex().tree_level
+index_size int8 pgstatindex().index_size
+root_block_no int8 pgstatindex().root_block_no
+internal_pages int8 pgstatindex().internal_pages
+leaf_pages int8 pgstatindex().leaf_pages
+empty_pages int8 pgstatindex().empty_pages
+deleted_pages int8 pgstatindex().deleted_pages
+avg_leaf_density float8 pgstatindex().avg_leaf_density
+leaf_fragmentation float8 pgstatindex().leaf_fragmentation
+===================== ================ ====================================== ===========
+
diff --git a/docs/en/pt-config.rst b/docs/en/pt-config.rst
new file mode 100644
index 0000000..3bb69b0
--- /dev/null
+++ b/docs/en/pt-config.rst
@@ -0,0 +1,109 @@
+pt-config
+=========
+
+Summary
+-------
+
+Refer the setting of the PosgreSQL configuration file ``postgresql.conf``, and then modify it.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-config [options...] get [PARAM]
+ pt-config [options...] set [PARAM] [VALUE]
+ pt-config [options...] disable [PARAM]
+
+Commands
+--------
+
+.. csv-table::
+
+ ``get [PARAM]``, Displays the current value. If it is disabled(comment-out). ``(disabled)`` is displayed.
+ ``set [PARAM] [VALUE]``, Sets the new value. The value will be enabled if it is disabled (comment-out).
+ ``disable [PARAM]``, Disable the setting value.(comment-out)
+
+Options
+-------
+
+.. code-block:: none
+
+ -D, --pgdata=PGDATA Specify a PostgreSQL database cluster.
+ --apply Apply change(s).
+ --help Print this help.
+
+``-D``, ``--pgdata`` Specifies the PostgreSQL database cluster. If not specified, to set the value of PGDATA.
+
+``--apply`` It will actually apply the changes to the postgresql.conf when it runs ``set`` / ``disable`` command.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``Reading:``, Loading the ``postgresql.conf`` file displays in the full path.
+ ``Dry-run mode:``, The before and after values is only displayed. It does not provide the actual configuration changes.
+ ``Applying:``, Doing the actual configuration changes.
+ ``Old``, Displays the value of before change.
+ ``New``, Displays the value of after change.
+ ``Updating:``, Modifying the ``postgresql.conf`` file displays in the full path.
+
+
+Examples
+--------
+
+Displays the current value of ``shared_buffers``
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data get shared_buffers
+ [2015-04-16 17:08:12] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ 256MB
+ $
+
+Sets the value ``512MB`` of ``shared_buffers``. (Does not do the actual configuration changes)
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data set shared_buffers 512MB
+ [2015-04-16 17:08:44] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:08:44] INFO: Dry-run mode:
+ [2015-04-16 17:08:44] INFO: Old: shared_buffers = 256MB # min 128kB
+ [2015-04-16 17:08:44] INFO: New: shared_buffers = 512MB # min 128kB
+ $
+
+Sets the value ``512MB`` of ``shared_buffers``. (Doing the actual configuration change)
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data --apply set shared_buffers 512MB
+ [2015-04-16 17:09:11] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:09:11] INFO: Applying:
+ [2015-04-16 17:09:11] INFO: Old: shared_buffers = 256MB # min 128kB
+ [2015-04-16 17:09:11] INFO: New: shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:11] INFO: Updated: /var/lib/pgsql/9.4/data/postgresql.conf
+ $
+
+Sets the disable(comment-out) of ``shared_buffers``.
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data --apply disable shared_buffers
+ [2015-04-16 17:09:52] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:09:52] INFO: Applying:
+ [2015-04-16 17:09:52] INFO: Old: shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:52] INFO: New: #shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:52] INFO: Updated: /var/lib/pgsql/9.4/data/postgresql.conf
+ $
+
+Displays the value of ``shared_buffers`` (Disabled).
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data get shared_buffers
+ [2015-04-16 17:10:00] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ 512MB (disabled)
+ $
diff --git a/docs/en/pt-index-usage.rst b/docs/en/pt-index-usage.rst
new file mode 100644
index 0000000..00b528e
--- /dev/null
+++ b/docs/en/pt-index-usage.rst
@@ -0,0 +1,113 @@
+
+pt-index-usage
+==============
+
+Summary
+-------
+
+Displays the indexes usage.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-index-usage [option...]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ -i, --index=STRING
+ -u, --unused
+ --help
+
+``-h``, ``--host`` Specifies the connecting the PostgreSQL database server name or its IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no other value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the user name of the PostgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used If no other value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. Database name as database user name will be used if no other value has been defined in PGDATABASE.
+
+``-o``, ``--owner`` The index information is only displayed which matches to a string specified where is a name of the owner.
+
+``-n``, ``--schema`` The index information is only displayed which matches to a string specified where is in the schema.
+
+``-t``, ``--table`` The index information is only displayed which matches to a string specified where is in the table.
+
+``-i``, ``--index`` The index information is only displayed which matches to a string specified.
+
+``-u``, ``--unused`` The index information is displayed which not use the index.
+
+``-d`` (or ``--dbname`` ), ``-o`` (or ``--owner`` ), ``-n`` (or ``--schema`` ), ``-t`` (or ``--table`` ), ``-i`` (or ``--index`` ), ``-u`` (or ``--unused`` ), if specified the options at the same time, the index is displayed which matches to it.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``OID``, Object ID of index
+ ``OWNER``, Owner name of index
+ ``SCHEMA``, Schema name on being indexed
+ ``TABLE``, Table name on being indexed
+ ``INDEX``, Index name
+ ``BLKS``, Block (every 8kb) of index
+ ``SCAN``, Index scan number of executions
+ ``T_READ``, Number of index entries by index scan
+ ``T_FTCH``, Number of tuple on table by index scan
+ ``B_READ``, Number of index blocks read from disk
+ ``B_HIT``, Number of index pages read from shared buffer
+ ``STATUS``, Index status. It reads from pg_index system table.
+ ``TABLESPACE``, The tablespace name that has index
+
+Examples
+--------
+
+For the index of table in the ``public`` schema, displays the usage situation.
+
+.. code-block:: none
+
+ $ pt-index-usage -n public -d postgres
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26793 | snaga | public | pgbench_accounts | pgbench_accounts_pkey | 276 | 1 | 1 | 1 | 4 | 0 | | spc1 |
+ | 26789 | snaga | public | pgbench_branches | pgbench_branches_pkey | 2 | 1 | 1 | 0 | 2 | 0 | | pg_default |
+ | 26791 | snaga | public | pgbench_tellers | pgbench_tellers_pkey | 2 | 0 | 0 | 0 | 0 | 0 | | pg_default |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
+
+For the index of ``pgbench_accounts`` table in the ``public`` schema, displays the usage situation.
+
+.. code-block:: none
+
+ $ pt-index-usage -n public -d postgres -t pgbench_accounts
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26793 | snaga | public | pgbench_accounts | pgbench_accounts_pkey | 276 | 1 | 1 | 1 | 4 | 0 | | spc1 |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
+
+For the index not used even once, in the ``public`` schema, displays the usage situation.
+
+.. code-block:: none
+
+ $ pt-index-usage -d postgres -n public -u
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26791 | snaga | public | pgbench_tellers | pgbench_tellers_pkey | 2 | 0 | 0 | 0 | 0 | 0 | | pg_default |
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
diff --git a/docs/en/pt-kill.rst b/docs/en/pt-kill.rst
new file mode 100644
index 0000000..d129974
--- /dev/null
+++ b/docs/en/pt-kill.rst
@@ -0,0 +1,54 @@
+pt-kill
+=======
+
+Summary
+-------
+
+Interrupting the processing of PostgreSQL backend, or to exit it.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-kill [options...] [command] [pid]
+
+
+Commands
+--------
+
+.. code-block:: none
+
+ cancel Cancel a running query.
+ terminate Terminate a backend with canceling query.
+
+Options
+-------
+
+.. code-block:: none
+
+ --help Print this help.
+
+Output Items
+------------
+
+None.
+
+
+Examples
+--------
+
+Cancel the SQL running in the process ID 3289.
+
+.. code-block:: none
+
+ $ pt-kill cancel 3289
+
+
+Exit the backend in the process ID 3219.
+
+.. code-block:: none
+
+ $ pt-kill terminate 3291
+
diff --git a/docs/en/pt-proc-stat.rst b/docs/en/pt-proc-stat.rst
new file mode 100644
index 0000000..00077ab
--- /dev/null
+++ b/docs/en/pt-proc-stat.rst
@@ -0,0 +1,94 @@
+
+pt-proc-stat
+============
+
+Summary
+-------
+
+Displays statistics I/O for each process.
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-proc-stat [option...] [delay [count]]
+
+
+``pt-proc-stat`` Referring to proc file system. Make sure you run by running PostgreSQL user or root user.
+
+Options
+-------
+
+.. code-block:: none
+
+ -D, --pgdata=DATADIR
+ -P, --pid=PID
+ --help
+
+``-D``, ``--pgdata``
+Specifies the directory of the database cluster.
+
+``-P``, ``--pid``
+Specifies the process ID of the postmaster process.
+
+When both ``-D`` (or ``--pgdata``) and ``-P`` (or ``--pid``) are not specified, ``pt-proc-stat`` searches postmaster process(es) from all the running processes, and uses it to monitor.
+
+If two or more postmaster processes are found, one of them will be picked at random to be monitored.
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``PROCESS NAME``, Process name
+ ``PID``, Process ID
+ ``STAT``, Status of the process
+ ``USR``, User CPU time (Difference)
+ ``SYS``, System CPU time (Difference)
+ ``VSZ``, Virtual memory size (MB)
+ ``RSS``, Usage of a physical memory (MB)
+ ``READ``, Reading I/O volume is displayed differently between both of the data(kB).
+ ``WRITE``, Writing I/O volume is displayed differently between both of the data(kB).
+ ``READ2``, Load I/O except the ``READ`` (KB / Difference)
+ ``WRITE2``, Load I/O except the ``WRITE`` (KB / Difference)
+
+Examples
+--------
+
+It looks for the postmaster process automatically. Statistics I/O of the postmaster and the child process is displayed every five seconds twice.
+
+.. code-block:: none
+
+ $ sudo ./pt-proc-stat 5 2
+ Fri May 1 22:23:39 JST 2015
+ PROCESS NAME[ PID] STAT USR SYS VSZ RSS READ WRITE READ2 WRITE2
+ postmaster[24026] S 4 13 100 9 23752 1290092 1090800 155357
+ logger[24028] S 0 1 85 1 4 76 30 -45
+ checkpointer[24030] S 4 117 100 8 176 56768 -176 -46965
+ writer[24031] S 104 66 100 9 0 291080 0 130560
+ wal writer[24032] S 8 19 100 1 0 2928 0 0
+ autovacuum launcher[24033] S 3 3 101 2 8 8 288 0
+ stats collector[24034] S 13 32 85 1 0 2140 34 -649
+ snaga postgres 127.0[25473] R 32 9 101 7 296 1472 1264 0
+ snaga postgres 127.0[25474] R 33 9 101 7 424 1384 1120 0
+ snaga postgres 127.0[25475] R 33 9 101 7 424 1448 1016 0
+ snaga postgres 127.0[25476] S 32 9 101 7 580 1400 780 0
+ snaga postgres 127.0[25477] R 32 9 101 7 908 1368 492 0
+
+ Fri May 1 22:23:44 JST 2015
+ PROCESS NAME[ PID] STAT USR SYS VSZ RSS READ WRITE READ2 WRITE2
+ postmaster[24026] S 0 0 100 9 0 0 0 0
+ logger[24028] S 0 0 85 1 0 0 0 0
+ checkpointer[24030] S 0 0 100 8 0 0 0 0
+ writer[24031] S 4 1 100 9 0 11928 0 392
+ wal writer[24032] S 0 0 100 1 0 0 0 0
+ autovacuum launcher[24033] S 0 0 101 2 0 0 0 0
+ stats collector[24034] S 0 0 85 1 0 0 0 0
+ snaga postgres 127.0[25473] R 72 18 101 10 1772 3608 1740 0
+ snaga postgres 127.0[25474] R 68 20 101 10 1436 3920 2020 0
+ snaga postgres 127.0[25475] D 70 18 101 10 1304 4216 2368 0
+ snaga postgres 127.0[25476] R 70 20 101 10 1252 3384 2212 0
+ snaga postgres 127.0[25477] R 73 16 101 10 1464 3224 2080 0
+
+ $
diff --git a/docs/en/pt-replication-stat.rst b/docs/en/pt-replication-stat.rst
new file mode 100644
index 0000000..a88cb42
--- /dev/null
+++ b/docs/en/pt-replication-stat.rst
@@ -0,0 +1,92 @@
+
+pt-replication-stat
+===================
+
+Summary
+-------
+
+The replication status is displayed by referring the master node statistical information of the replication. It is possible to display continuously on every specified interval.
+
+PostgreSQL 9.1 or higher. PostgreSQL 9.0 doesn't work because it doesn't have no "pg_stat_replication" system view.
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-replication-stat [option...] [delay [count]]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``PID``, The process ID of WAL sender processes on the master node
+ ``NAME``, The slave node name of the registered in the replication
+ ``HOST``, The host name of slave node or IP address
+ ``PORT``, The port number of the master node that connected to the slave node
+ ``STATE``, The slave node status. It acquires the value of following ``startup`` or ``backup`` or ``catchup`` or ``streaming``
+ ``SENT``, Position on the WAL which sent to slave
+ ``WRITTTEN``, Position on the WAL which wrote to the WAL buffer on the slave
+ ``FLUSHED``, Position on the WAL which synchronously wrote to the WAL file on the slave
+ ``REPLAYED``, Position on WAL which applied to the data file on the slave
+ ``PRI``, If the slave node is synchronously replication that the node priority is displayed.
+ ``MODE``, The operation mode is displayed. ``sync`` is synchronous mode. ``async`` is asynchronous mode. ``potential`` is running in asynchronous mode. But there is a possibility to upgrade to the synchronous mode.
+
+
+Examples
+--------
+
+Connect to the port ``5433`` of host ``127.0.0.1`` by ``postgres`` user, the statistical information is displayed every five seconds twice.
+
+.. code-block:: none
+
+ $ pt-replication-stat -h 127.0.0.1 -p 5433 -U postgres 5 2
+ Sat Mar 28 21:45:23 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/5F30398 | 0/5F300B0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0/5F2FE48 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ Sat Mar 28 21:45:28 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/608CD68 | 0/608CAC0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/608CAC0 | 0/608CAC0 | 0/608C7D8 | 0/608C7D8 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/608CAC0 | 0/608CAC0 | 0/608C7D8 | 0/608C7D8 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ $
+
+Connect to the default port (``5432``) of ``localhost``, the statistical information is displayed every five seconds continuously. CTRL-C to end this.
+
+.. code-block:: none
+
+ $ pt-replication-stat -h localhost 5
+ Sat Mar 28 21:45:23 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/5F30398 | 0/5F300B0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0/5F2FE48 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ ^C[2015-03-28 21:45:25] INFO: Terminated.
+ $
+
diff --git a/docs/en/pt-session-profiler.rst b/docs/en/pt-session-profiler.rst
new file mode 100644
index 0000000..524acd4
--- /dev/null
+++ b/docs/en/pt-session-profiler.rst
@@ -0,0 +1,65 @@
+
+pt-session-profiler
+===================
+
+Summary
+-------
+
+Displays the query and performance information by detecting the PostgreSQL session from capturing the network traffic.
+
+For executing tcpdump requires ``root`` privileges.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-session-profiler [option...]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -i [INTERFACE]
+ -T, --threshold=MILLISECONDS
+ --help
+
+``-h``, ``--host`` Specifies the IP address or PostgreSQL server name of the PostgreSQL session to parse. If not specified, to set the value of PGHOST. Every server name or IP address packet will be analyzed if no other value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the PostgreSQL session to parse. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-i`` Specifies the network interface to capture. All network interfaces (``any``) will be used if it is omitted.
+
+``-T``, ``--threshold`` Specifies the threshold of execution time in the displayed query. The unit is milliseconds. The default value is ``1000``.
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``sess``, Unique session strings. (Different values for each session. The source IP address and the port number and the destination IP address and the port number and use the 12 characters of the hash string MD5.)
+ ``time``, Query execution time
+ ``query``, Run the query string
+
+Examples
+--------
+
+It monitors the ones destined to port 5432 of the TCP packet that passes through all of the network interface. CTRL-C to end this.
+
+.. code-block:: none
+
+ $ sudo pt-session-profiler -T 500
+ [2015-03-29 15:07:22] INFO: Threshold: 500 ms
+ [2015-03-29 15:07:22] INFO: tcpdump -l -i any -s 0 -X -p tcp port 5432
+ [2015-03-29 15:07:36] INFO: sess:e27f20dae08f, time:0:00:00.557728, query:UPDATE pgbench_tellers SET tbalance = tbalance + 2084 WHERE tid = 23;
+ [2015-03-29 15:07:36] INFO: sess:b3674d7bbea0, time:0:00:00.980950, query:INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (32, 5, 255511, 2695, CURRENT_TIMESTAMP);
+ [2015-03-29 15:07:45] INFO: sess:1c32286cab7a, time:0:00:01.115904, query:SELECT abalance FROM pgbench_accounts WHERE aid = 161999;
+ [2015-03-29 15:07:45] INFO: sess:33f8c268624c, time:0:00:00.526850, query:UPDATE pgbench_accounts SET abalance = abalance + 3877 WHERE aid = 326415;
+ [2015-03-29 15:07:46] INFO: sess:b370afd07dcf, time:0:00:00.719780, query:SELECT abalance FROM pgbench_accounts WHERE aid = 852680;
+ [2015-03-29 15:07:46] INFO: sess:0f04724051ad, time:0:00:00.543609, query:BEGIN;
+ ^C[2015-03-29 15:07:51] INFO: Terminated.
+ $
diff --git a/docs/en/pt-set-tablespace.rst b/docs/en/pt-set-tablespace.rst
new file mode 100644
index 0000000..eb3c8a4
--- /dev/null
+++ b/docs/en/pt-set-tablespace.rst
@@ -0,0 +1,127 @@
+
+pt-set-tablespace
+=================
+
+Summary
+-------
+
+Change at once the tablesapace of the index associated with the specified table.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-set-tablespace [option...] [tablespace]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ -l, --list
+ --apply
+ --help
+
+``-h``, ``--host`` Specifies the connecting PostgreSQL database server name or its IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the user name of the PosgtgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used if no value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. It connects to the database same as the database username if no value has been defined in PGDATABASE.
+
+``-o``, ``--owner`` It applies only table that is owning user that matches the specified name.
+
+``-n``, ``--schema`` It applies only table that is a schema that matches the specified name.
+
+``-t``, ``--table`` It applies only table that matches the specified name.
+
+``--apply`` The change of tablespace, it is actually reflected in the database.
+
+``-l``, ``--list`` Displays the tablespace information.
+
+``-o`` (or ``--owner`` ), ``-n`` (or ``--schema`` ), ``-t`` (or ``--table`` ), if specified the options at the same time, the only table that matches the conditions.
+
+If it fails to move one or more tables, returned an exit code of ``1``. If it's successful the movement of all of the files, returned a "0".
+
+
+Output Items
+------------
+
+``-l``, ``--list`` Items that are displayed in the options.
+
+.. csv-table::
+
+ ``OID``, Object ID of tablespace
+ ``OWNER``, Owner name of tablespace
+ ``TABLESPACE``, Tablespace name
+ ``LOCATION``, Path of the directory for tablespace
+ ``USE%``, Disk usage of the partition for tablespace
+ ``AVAIL``, Free space of the partition for tablespace
+
+Other output items are shown below.
+
+.. csv-table::
+
+ ``Dry-run mode``, Free space of the partition for tablespace.
+ ``Applying ALTER TABLE/INDEX``, Acutually running the ALTER TABLE/INDEX statement, it will change tablespace of the index.
+ ``X tables/indexes moved. Y failed.``, X of tables/indexes is moved successfully, Y failed to move.
+
+
+Examples
+--------
+
+Displays list of tablespace that exist in the PostgreSQL instance. Gets the used area of the each partition, and displays as a list together.
+
+.. code-block:: none
+
+ $ pt-set-tablespace --list
+ +--------+----------+------------+---------------------------+------+-------+
+ | OID | OWNER | TABLESPACE | LOCATION | USE% | AVAIL |
+ +--------+----------+------------+---------------------------+------+-------+
+ | 1663 | postgres | pg_default | | | |
+ | 1664 | postgres | pg_global | | | |
+ | 121263 | postgres | hddspc2 | /disk/disk2/pgsql | 85% | 80G |
+ | 16818 | postgres | ssdspc1 | /disk/disk1/tblspc1 | 67% | 127G |
+ | 305242 | postgres | ssdspc2 | /disk/disk3/pgsql/ssdspc2 | 98% | 13G |
+ +--------+----------+------------+---------------------------+------+-------+
+ $
+
+All ``orders`` tables and the indexes that was created on ``orders`` table in ``dbt3`` database, it displays ``ALTER TABLE`` and ``ALTER INDEX`` statement for to move the tablesapace. (Does not actually move)
+
+.. code-block:: none
+
+ $ pt-set-tablespace -d dbt3 --table orders ssdspc1
+ [2015-04-29 17:35:24] INFO: Dry-run mode:
+ [2015-04-29 17:35:24] INFO: ALTER TABLE "public"."orders" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."pk_orders" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."i_o_orderdate" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."i_o_custkey" SET TABLESPACE "ssdspc1";
+ $
+
+All ``orders`` tables and the indexes that were created on ``orders`` table in ``dbt3`` database, to move to the ``ssdspc1`` tablespace.
+
+.. code-block:: none
+
+ $ pt-set-tablespace -d dbt3 --table orders --apply ssdspc1
+ [2015-04-29 17:37:06] INFO: Applying ALTER TABLE/INDEX...
+ [2015-04-29 17:37:08] INFO: 4 tables/indexes moved. 0 failed.
+ $
+
+All tables in the ``dbt3`` schema and all indexes, to move to the ``ssdspc1`` tablespace.
+
+.. code-block:: none
+
+ $ pt-set-tablespace --schema dbt3 --apply ssdspc1
+ [2015-04-29 17:38:39] INFO: Applying ALTER TABLE/INDEX...
+ [2015-04-29 17:38:57] INFO: 31 tables/indexes moved. 0 failed.
+ $
diff --git a/docs/en/pt-snap-statements.rst b/docs/en/pt-snap-statements.rst
new file mode 100644
index 0000000..45d255e
--- /dev/null
+++ b/docs/en/pt-snap-statements.rst
@@ -0,0 +1,113 @@
+
+pt-snap-statements
+==================
+
+Summary
+-------
+
+Displays by calculating the difference between the statistics of two difference time of SQL statements.
+
+If it has specified options value, it can be displayed to sort of each specific items.
+
+Make sure you have ``pg_stat_statements`` of contrib module.
+
+And also make sure to enable ``track_io_timing`` option.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-snap-statements [option...] [interval]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -s, --sort=KEY
+ -l
+ -t, --top=NUMBER
+ -R, --reset
+ --help
+
+``-h``, ``--host`` Specifies the connecting the PostgreSQL database server name or its IP address. If not specified, to set the value of PGHOST.``localhost`` will be used if no other value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting the PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the user name of the PostgreSQL database. If not specified, to set the value of PGUSER. Vars USER will be used if no other value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. Database name as database user name will be used if no other value has been defined in PGDATABASE.
+
+``-s`` (not implemented) Specifies the sort item. ``KEY`` can take one of following values: ``CALLS``, ``T_TIME``, ``ROWS``, ``B_HIT``, ``B_READ``, ``B_DIRT``, ``B_WRTN``, ``R_TIME``, ``W_TIME``
+
+``-l`` (not implemented) Displays with detailed every block classification (Shared buffer, Local buffer, Temporary buffer). If not specified, it is displayed the total number of shared buffer, local buffer and temporary buffer.
+
+``-t``, ``--top`` Specifies the number of displayed queries. If not specified, it is displayed the all queries.
+
+``-R``, ``--reset`` It initializes the statistical information of``pg_stat_statements`` view.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``USER``, Username of query run
+ ``DBNAME``, Database name of query run
+ ``QUERYID``, Query ID of query run (Hexadecimal)
+ ``QUERY``, Query performed (Display up to 30 characters)
+ ``CALLS``, Number of times of query run
+ ``T_TIME``, Total number of times of query run (Millisecond)
+ ``ROWS``, Total number of rows that has received the obtain or influence
+ ``B_HIT``, Total number of blocks read from the buffer at the time blocks read
+ ``B_READ``, Total number of blocks read from the disk at the time blocks read
+ ``B_DIRT``, Total number of pages that page has been updated by the query
+ ``B_WRTN``, Total number of blocks that are written to disk by the query
+ ``R_TIME``, Total time of block read from the disk (Millisecond) ( Make sure to enable ``track_io_timing`` parameter )
+ ``W_TIME``, Total time of block write to the disk (Millisecond) ( Make sure to enable ``track_io_timing`` parameter )
+
+
+Examples
+--------
+
+Connects to the ``postgres`` database, and the SQL statements executed in 5 seconds is sorted in descending order of total execution time (``T_TIME``), and displays all.
+
+.. code-block:: none
+
+ $ pt-snap-statements -d postgres 5
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | USER | DBNAME | QUERYID | QUERY | CALLS | T_TIME | ROWS | B_HIT | B_READ | B_DIRT | B_WRTN | R_TIME | W_TIME |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | snaga | postgres | 80053daf | UPDATE pgbench_branches SET bb | 677 | 12007 | 677 | 9160 | 1 | 1 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | 1675159e | UPDATE pgbench_tellers SET tba | 681 | 7648 | 681 | 3403 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | ec088219 | UPDATE pgbench_accounts SET ab | 684 | 530 | 684 | 2289 | 585 | 568 | 0 | 125.9 | 0.0 |
+ | snaga | postgres | 198383d | SELECT abalance FROM pgbench_a | 682 | 73 | 682 | 2080 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | da8cc6f | INSERT INTO pgbench_history (t | 676 | 34 | 676 | 704 | 12 | 10 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | d4e6bf94 | BEGIN; | 684 | 4 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | a81672e | END; | 671 | 3 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | 8caa574 | select count(*) from pgbench_b | 1 | 0 | 1 | 4 | 0 | 0 | 0 | 0.0 | 0.0 |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ $
+
+User ``snaga`` connects to the ``postgres`` database of the PostgreSQL server running on the port ``5433`` of host ``192.168.1.101``, and the SQL statements executed in 5 seconds is sorted in descending order of total execution time (``T_TIME``), and the top 5 are displayed.
+
+.. code-block:: none
+
+ $ pt-snap-statements --host 192.168.1.101 -p 5433 -U snaga -d postgres -t 5 5
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | USER | DBNAME | QUERYID | QUERY | CALLS | T_TIME | ROWS | B_HIT | B_READ | B_DIRT | B_WRTN | R_TIME | W_TIME |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | snaga | postgres | 80053daf | UPDATE pgbench_branches SET bb | 503 | 9953 | 503 | 8430 | 14 | 7 | 0 | 0.6 | 0.0 |
+ | snaga | postgres | 1675159e | UPDATE pgbench_tellers SET tba | 508 | 6483 | 508 | 2551 | 10 | 9 | 0 | 0.3 | 0.0 |
+ | snaga | postgres | ec088219 | UPDATE pgbench_accounts SET ab | 511 | 560 | 511 | 1424 | 698 | 477 | 7 | 91.0 | 12.1 |
+ | snaga | postgres | 198383d | SELECT abalance FROM pgbench_a | 511 | 93 | 511 | 1550 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | da8cc6f | INSERT INTO pgbench_history (t | 503 | 20 | 503 | 530 | 13 | 11 | 0 | 0.1 | 0.0 |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ $
+
diff --git a/docs/en/pt-stat-snapshot.rst b/docs/en/pt-stat-snapshot.rst
new file mode 100644
index 0000000..6b4bfcb
--- /dev/null
+++ b/docs/en/pt-stat-snapshot.rst
@@ -0,0 +1,121 @@
+
+pt-stat-snapshot
+================
+
+Summary
+-------
+
+Get a snapshot of statistical information, save, and manage.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-stat-snapshot [option...] install
+ pt-stat-snapshot [option...] uninstall
+ pt-stat-snapshot [option...] create [level]
+ pt-stat-snapshot [option...] list
+ pt-stat-snapshot [option...] delete [sid]
+ pt-stat-snapshot [option...] export [file]
+ pt-stat-snapshot [option...] import [file]
+
+Commands
+--------
+
+.. csv-table::
+
+ ``install``, Creates a schema and tables and functions of the package.
+ ``uninstall``, Drops a schema and tables and functions of the package.
+ ``create [level]``, Takes a snapshot of the database statistics. ``[level]`` can be ``1`` or ``2`` or ``4``.
+ ``list``, Shows a list of stored snapshots.
+ ``delete [sid]``, Deletes a snapshot specified by snapshot id. Multiple snapshot ids can be specified by range as ``M:N``.
+ ``export [file]``, Exports all snapshot data into the specified file.
+ ``import [file]``, Imports snapshot data from the specified file.
+
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ --help
+
+``-h``, ``--host`` Specifies the connecting the PostgreSQL database server name or its IP address. or its IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no other value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the username of the PostgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used If no other value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. Database name as database username will be used if no other value has been defined in PGDATABASE.
+
+
+Output Items
+------------
+
+``list`` command shows following items.
+
+.. csv-table::
+
+ ``SID``, Snapshot ID (Assigned to every snapshot. Monotonic increase.)
+ ``TIMESTAMP``, Timestamp of the snapshot taken.
+ ``LEVEL``, Snapshot level.
+
+Examples
+--------
+
+It installs a schema, tables and functions of the package to ``testdb`` database.
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb install
+ [2015-03-31 17:21:37] INFO: Succeeded to install pgperf snapshot.
+ $
+
+It takes a snapshot with the snapshot level 4.
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb create 4
+ [2015-03-31 17:21:47] INFO: Succeeded to take a snapshot.
+ $
+
+It shows a list of the snapshots.
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb list
+ +-----+---------------------+-------+
+ | SID | TIMESTAMP | LEVEL |
+ +-----+---------------------+-------+
+ | 0 | 2015-03-31 17:21:47 | 1 |
+ +-----+---------------------+-------+
+ $
+
+It uninstalls the schema, tables and functions of the package from ``testdb`` database.
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb uninstall
+ [2015-03-31 17:21:59] INFO: Succeeded to uninstall pgperf snapshot.
+ $
+
+About pgperf-snapshot Module
+----------------------------
+
+``pt-stat-snapshot`` command depends on another module internally, previously known as the pgperf-snapshot.
+
+For more details about the pgperf-snapshot module, please refer to the following documents.
+
+.. toctree::
+ :maxdepth: 2
+
+ pgperf_overview.rst
+ pgperf_intro.rst
+ pgperf_functions.rst
+ pgperf_tables.rst
diff --git a/docs/en/pt-table-usage.rst b/docs/en/pt-table-usage.rst
new file mode 100644
index 0000000..5e6c4f9
--- /dev/null
+++ b/docs/en/pt-table-usage.rst
@@ -0,0 +1,90 @@
+
+pt-table-usage
+==============
+
+Summary
+-------
+
+Displays status information about the table
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-table-usage [option...]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ --help
+
+``-h``, ``--host`` Specifies the connecting PostgreSQL database server name or IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the user name of the PosgtgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used if no value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. It connect to the database same as the database username if no value has been defined in PGDATABASE.
+
+``-o``, ``--owner`` Displays only table that is the owner user that matches the specified name.
+
+``-n``, ``--schema`` Displays only table that is schema that matches the specified name.
+
+``-t``, ``--table`` Displays only table that matches the specified name.
+
+``-d`` (or ``--dbname`` ), ``-o`` (or ``--owner`` ), ``-n`` (or ``--schema`` ), ``-t`` (or ``--table`` ), if specified the options at the same time, only table that matches the conditions.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``OID``, Table object ID
+ ``OWNER``, Owner name of table
+ ``SCHEMA``, Schema name that exists in the table
+ ``TABLE``, Table name
+ ``BLKS``, Number of the table block (every 8kb)
+ ``SCAN``, Sequential scan number of executions
+ ``T_READ``, Number of tuple by sequential scan
+ ``T_INS``, Number of inserted tuple
+ ``T_UPD``, Number of updated tuple(include HOT UPDATE)
+ ``T_DEL``, Number of delated tuple
+ ``B_READ``, Number of read table block from disk
+ ``B_HIT``, Number of read table page by shared buffer
+ ``VACUUMED``, Date and time that run VACUUM on the last (VACUUM command or automatic VACUUM)
+ ``ANALYZED``, Date and time that run ANALYZE on the last (ANALYZE command or automatic ANALYZE)
+ ``TABLESPACE``, The tablespace name where the table is placed.
+
+Examples
+--------
+
+It connects to the PostgreSQL instance running on the port ``5432`` of ``localhost``, and to display all of the table usage of ``dbt3`` database.
+
+.. code-block:: none
+
+ $ pt-table-usage -d dbt3
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ | OID | OWNER | SCHEMA | TABLE | BLKS | SCAN | T_READ | T_INS | T_UPD | T_DEL | B_READ | B_HIT | VACUUMED | ANALYZED | TABLESPACE |
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ | 1273410 | snaga | public | customer | 3531 | 5 | 750000 | 150000 | 0 | 0 | 6499 | 29943 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273416 | snaga | public | lineitem | 106583 | 12 | 66656465 | 6001215 | 0 | 0 | 240547 | 1340871 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273419 | snaga | public | nation | 1 | 4 | 100 | 25 | 0 | 0 | 1 | 5 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273413 | snaga | public | orders | 25326 | 5 | 7500000 | 1500000 | 0 | 0 | 48612 | 208386 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273404 | snaga | public | part | 4064 | 3 | 600000 | 200000 | 0 | 0 | 6082 | 26558 | | 2015-03-08 18:31:40 | ssdspc1 |
+ | 1273407 | snaga | public | partsupp | 17087 | 5 | 4000000 | 800000 | 0 | 0 | 32200 | 148518 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273422 | snaga | public | region | 1 | 3 | 15 | 5 | 0 | 0 | 1 | 4 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273401 | snaga | public | supplier | 218 | 4 | 40000 | 10000 | 0 | 0 | 220 | 1802 | | 2015-03-08 18:31:40 | ssdspc1 |
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ $
diff --git a/docs/en/pt-tablespace-usage.rst b/docs/en/pt-tablespace-usage.rst
new file mode 100644
index 0000000..fc2210c
--- /dev/null
+++ b/docs/en/pt-tablespace-usage.rst
@@ -0,0 +1,65 @@
+
+pt-tablespace-usage
+===================
+
+Summary
+-------
+
+Displays the usage of table space for earch databases.
+
+For each table space, it will be able to check which database is using how much the capacity.
+
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-tablespace-usage [option...]
+
+Options
+-------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ --help
+
+``-h``, ``--host`` Specifies the connecting PostgreSQL database server name or its IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-U``, ``--username`` Specifies the user name of the PosgtgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used if no value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. It connect to the database same as the database username if no value has been defined in PGDATABASE.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``TABLESPACE``, Tablespace name
+ ``DBNAME``, Database name (The blank in case of pg_global table space)
+ ``SIZE (MB)``, The capacity that is using the database object on the table space.(MB)
+
+Examples
+--------
+
+It connects to the ``postgres`` database of PostgreSQL instance running on the port ``5432`` of localhost, and to display the capacity that they are using for each database in each tablespace.
+
+.. code-block:: none
+
+ $ pt-tablespace-usage -d postgres
+ +------------+-----------+-----------+
+ | TABLESPACE | DBNAME | SIZE (MB) |
+ +------------+-----------+-----------+
+ | pg_default | postgres | 8 |
+ | pg_default | template1 | 6 |
+ | pg_default | testdb | 8 |
+ | pg_global | | 1 |
+ | spc1 | postgres | 16 |
+ +------------+-----------+-----------+
diff --git a/docs/en/pt-verify-checksum.rst b/docs/en/pt-verify-checksum.rst
new file mode 100644
index 0000000..92742e3
--- /dev/null
+++ b/docs/en/pt-verify-checksum.rst
@@ -0,0 +1,85 @@
+
+pt-verify-checksum
+==================
+
+Summary
+-------
+
+Verifies the checksum of the specified PostgreSQL file
+
+Make sure to have created a database cluster that enable the checksum. (``-k`` option of ``initdb`` command )
+
+Version PosgreSQL 9.3 or higher
+
+The script invoke ``verifychecksum`` command inside. If it run on other OS than Hat Enterprise Linux 6/CentOS 6, you must separately build to ``src/verifychecksum.c`` to be located in the following ``bin`` directory.
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-verify-checksum [option...] [segment file | directory]
+
+If specified the file name as an argument, verify the checksum.
+
+If specified a directory name as an argument, of the files in that directory, to verify the checksum as the subject of the following files.
+
+* The files found in the ``global`` and ``base`` directories, and
+* The file name is composed of a number (ex. ``1234``,``1234.1``) , and it contained that ``_vm`` or ``_fsm`` are appended at the end of files.
+
+If errors were found in one or more files in the checksum verification, return exit code ``1``.
+If any other error occured, return exit code ``2``.
+If there are no problems in any files, return ``0``.
+
+Options
+----------
+
+.. code-block:: none
+
+ -r, --recursive
+ -v, --verbose
+ --help
+
+`-r``, ``--recursive`` If it options specified directory, recursively scan the sub-directory below, to verify the checksum of the applicable file.
+
+``-v``, ``--verbose`` During the checksum verification, it will show output the more messages.
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``blkno``, Block number that found the checksum error
+ ``expected``, Checksum calculated from the data of the block
+ ``found``, Checksum which had been recorded in the page header
+ ``Verified N files``, Number of files that the checksum verification
+ ``N files corrupted``, Number of files that found the checksum error
+
+Examples
+--------
+
+Verifies the checksum of single file.
+
+.. code-block:: none
+
+ $ pt-verify-checksum /var/lib/pgsql/9.4/data/base/16386/16399
+ [2015-03-28 15:50:03] INFO: Verified 1 files. 0 files corrupted.
+ $
+
+Verifies the checksum of all of files in the database.
+
+.. code-block:: none
+
+ $ pt-verify-checksum /var/lib/pgsql/9.4/data/base/16386
+ [2015-03-28 15:51:00] INFO: Verified 311 files. 0 files corrupted.
+ $
+
+It recursively searched in the database cluster, and to verify the checksum of all of files.
+
+.. code-block:: none
+
+ $ pt-verify-checksum -r /var/lib/pgsql/9.4/data
+ [2015-03-28 15:55:16] INFO: /var/lib/pgsql/9.4/data/base/12144/11905: blkno 7, expected 2cf, found da97
+ [2015-03-28 15:55:16] INFO: 1 blocks corrupted in /var/lib/pgsql/9.4/data/base/12144/11905.
+ [2015-03-28 15:55:16] INFO: Verified 1046 files. 1 files corrupted.
+ $
diff --git a/docs/en/pt-xact-stat.rst b/docs/en/pt-xact-stat.rst
new file mode 100644
index 0000000..a140986
--- /dev/null
+++ b/docs/en/pt-xact-stat.rst
@@ -0,0 +1,97 @@
+
+pt-xact-stat
+============
+
+Summary
+-------
+
+Displays transaction statistical information of multiple node. It is possible to display each specified interval continuously.
+
+Usage
+-----
+
+.. code-block:: none
+
+ pt-xact-stat [option...] [delay [count]]
+
+Options
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -H, --host-list=HOSTLIST
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -H, --host-list=HOSTNAME:PORT,HOSTNAME:PORT[,...]
+
+``-h``, ``--host`` Specifies the connecting PostgreSQL database server name or its IP address. If not specified, to set the value of PGHOST. ``localhost`` will be used if no value has been defined in PGHOST.
+
+``-p``, ``--port`` Specifies the port number of the connecting PostgreSQL database. If not specified, to set the value of PGPORT. ``5432`` will be used if no value has been defined in PGPORT.
+
+``-H``, ``--host-list`` If it is connected to multiple database servers, it specifies database server name or several combination IP address or port number. The form is ``192.168.1.101:5432,192.168.1.102:5433`` . A set of server and port number is linked with a colon. A set of several server is linked with a comma. This port number is optional and will be set for the default port number.
+
+``-U``, ``--username`` Specifies the user name of the PosgtgreSQL database. If not specified, to set the value of PGUSER. The value of USER will be used if no value has been defined in PGUSER.
+
+``-d``, ``--dbname`` Specifies the connecting database name. If not specified, to set the value of PGDATABASE. It connect to the database same as the database username if no value has been defined in PGDATABASE.
+
+
+Output Items
+------------
+
+.. csv-table::
+
+ ``HOST``, Host name of PostgreSQL server
+ ``PORT``, Port number of PstgreSQL server
+ ``DBNAME``, Database name
+ ``CONN``, Number of sessions that are connected to the database
+ ``COMMITS``, Total number of transactions that are committed
+ ``ROLLBACKS``, Total number of transactions that are rollback
+ ``B_READ``, Number of blocks in the table that read from disk
+ ``B_HIT``, Number of pages that read from shared buffer
+
+
+Examples
+--------
+
+It connects to the two PostgreSQL instance running on the port 5432 and port 5433 of the localhost, and to display the statistical information of transaction for each instance every 5 seconds twice.
+
+.. code-block:: none
+
+ $ pt-xact-stat --host-list 127.0.0.1:5432,127.0.0.1:5433,127.0.0.1:5434 -d postgres 5 2
+ Sat Mar 28 20:47:50 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 137 | 1 | 104 | 10273 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 8 | 0 | 104 | 1350 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 76 | 0 | 104 | 7708 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ Sat Mar 28 20:47:55 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 139 | 1 | 104 | 10460 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 10 | 0 | 104 | 1537 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 78 | 0 | 104 | 7895 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ $
+
+Connects to the three PostgreSQL instance running on the port 5432, port 5433 and port 5434 of the localhost, and to display once the statistical information of transaction for each instance, and exit.
+
+.. code-block:: none
+
+ $ pt-xact-stat --host-list 127.0.0.1:5432,127.0.0.1:5433,127.0.0.1:5434 -d postgres
+ Sat Mar 28 21:05:48 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 12 | 0 | 104 | 1400 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 4 | 0 | 104 | 976 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 4 | 0 | 104 | 976 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ $
diff --git a/docs/en/releasenote.rst b/docs/en/releasenote.rst
new file mode 100644
index 0000000..c2aa515
--- /dev/null
+++ b/docs/en/releasenote.rst
@@ -0,0 +1,22 @@
+
+Release Note
+==============
+
+Version 0.2.2 (2015-08-08)
+--------------------------
+
+* Fix pt-proc-stat to improve error handling.
+* Fix pt-config to parse quoted values correctly.
+* Minor doc fixes.
+
+Version 0.2.1 (2015-05-31)
+--------------------------
+
+* Add support for Red Hat Enterprise Linux 7, CentOS 7 and Ubuntu 14.04 LTS with Python 2.7.
+* Fix to correct help messages. (pt-index-usage, pt-set-tablespace, pt-table-usage)
+
+Version 0.2.0 (2015-05-09)
+--------------------------
+
+* First Release Version
+
diff --git a/docs/ja/Makefile b/docs/ja/Makefile
new file mode 100644
index 0000000..f084230
--- /dev/null
+++ b/docs/ja/Makefile
@@ -0,0 +1,187 @@
+include ../../Makefile.global
+
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PostgresToolkit.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PostgresToolkit.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/PostgresToolkit"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PostgresToolkit"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+all: html
+
+install:
+ mkdir -p $(PREFIX)/docs
+ rm -rf $(PREFIX)/docs/ja
+ rm -rf _build/html/_sources
+ cp -rv _build/html $(PREFIX)/docs/ja
diff --git a/docs/ja/conf.py b/docs/ja/conf.py
new file mode 100644
index 0000000..a7e03b3
--- /dev/null
+++ b/docs/ja/conf.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+#
+# Postgres Toolkit documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 07 14:44:31 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Postgres Toolkit'
+copyright = u'2015, Uptime Technologies, LLC'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.2.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'PostgresToolkitdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'PostgresToolkit.tex', u'Postgres Toolkit Documentation',
+ u'Uptime Technologies, LLC', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'postgrestoolkit', u'Postgres Toolkit Documentation',
+ [u'Uptime Technologies, LLC'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'PostgresToolkit', u'Postgres Toolkit Documentation',
+ u'Uptime Technologies, LLC', 'PostgresToolkit', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs/ja/index.rst b/docs/ja/index.rst
new file mode 100644
index 0000000..3461ccf
--- /dev/null
+++ b/docs/ja/index.rst
@@ -0,0 +1,41 @@
+.. Postgres Toolkit documentation master file, created by
+ sphinx-quickstart on Sat Feb 07 14:44:31 2015.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Postgres Toolkit
+================
+
+Postgres Toolkitは、PostgreSQLサーバの運用管理やパフォーマンスチューニング、トラブルシューティングなどを行う際に、複雑なDBA作業をより容易に行うことができるスクリプトやコマンドのコレクションです。Postgres Toolkitを使うことによって、作業品質の向上、作業時間の短縮などを実現することができ、DBA業務の生産性を大幅に向上させることができます。
+
+Postgres Toolkitについての質問、要望、バグレポートなどは ``postgres-toolkit at uptime dot jp`` までメールで送るか、Postgres ToolkitのGithubレポジトリ http://www.github.com/uptimejp/postgres-toolkit/ のIssueを使ってご連絡ください。
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ install
+ pt-config
+ pt-index-usage
+ pt-kill
+ pt-proc-stat
+ pt-replication-stat
+ pt-session-profiler
+ pt-set-tablespace
+ pt-snap-statements
+ pt-stat-snapshot
+ pt-table-usage
+ pt-tablespace-usage
+ pt-verify-checksum
+ pt-xact-stat
+ releasenote
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/docs/ja/install.rst b/docs/ja/install.rst
new file mode 100644
index 0000000..30a3da5
--- /dev/null
+++ b/docs/ja/install.rst
@@ -0,0 +1,51 @@
+
+ツールキットの導入
+==================
+
+
+サポートOS
+----------
+
+サポートされているOSとそのバージョンは以下の通りです。
+
+* Red Hat Enterprise Linux 6 / CentOS 6
+* Red Hat Enterprise Linux 7 / CentOS 7
+* Ubuntu 14.04 LTS
+
+なお、Python2.6またはPython2.7がインストールされている必要があります。
+
+
+PostgreSQLバージョン
+--------------------
+
+サポートされているPostgreSQLのバージョンは以下の通りです。
+
+* PostgreSQL 9.0
+* PostgreSQL 9.1
+* PostgreSQL 9.2
+* PostgreSQL 9.3
+* PostgreSQL 9.4
+
+
+インストール方法
+----------------
+
+以下のコマンドを実行することで、インターネット経由でインストールすることができます。
+
+.. code-block:: none
+
+ curl -L http://dl.uptimeforce.com/postgres-toolkit/install.sh | sh
+
+または
+
+.. code-block:: none
+
+ wget http://dl.uptimeforce.com/postgres-toolkit/install.sh
+ sh install.sh
+
+を実行してください。
+
+インストールが完了すると、 ``/opt/uptime/postgres-toolkit-<VERSION>`` 以下に関連するファイルがインストールされます。
+
+
+
diff --git a/docs/ja/make.bat b/docs/ja/make.bat
new file mode 100644
index 0000000..bc9cc6b
--- /dev/null
+++ b/docs/ja/make.bat
@@ -0,0 +1,242 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PostgresToolkit.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PostgresToolkit.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %BUILDDIR%/..
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs/ja/pgperf_functions.rst b/docs/ja/pgperf_functions.rst
new file mode 100644
index 0000000..e8a5dcd
--- /dev/null
+++ b/docs/ja/pgperf_functions.rst
@@ -0,0 +1,162 @@
+スナップショット関数
+====================
+
+この章ではパフォーマンス統計情報のスナップショットを管理・利用するためのSQL関数について解説します。
+
+スナップショット関数一覧
+------------------------
+
+===================================== ======================================
+関数名 内容
+===================================== ======================================
+pgperf.create_snapshot(level) 新規のスナップショットを作成します。
+pgperf.delete_snapshot(snapid) 指定したスナップショットを削除します。
+pgperf.purge_snapshots(interval) 指定した期間以前のスナップショットを削除します。
+pgperf.get_interval(snapid1, snapid2) 指定した2つのスナップショットの間隔を秒数で取得します。
+===================================== ======================================
+
+
+pgperf.create_snapshot()関数
+----------------------------
+
+概要
+^^^^
+
+PostgreSQL内のパフォーマンス統計情報のスナップショットを取得します。
+
+定義
+^^^^
+::
+
+ integer pgperf.create_snapshot(integer level)
+
+引数
+^^^^
+
+======== ======== ================================
+引数名 引数型 内容
+======== ======== ================================
+level integer 取得するスナップショットレベル
+======== ======== ================================
+
+データベース内のパフォーマンス統計情報は、その種別によってはデータベースへの小さくない負荷が発生する場合があります。
+
+``pgperf.create_snapshot()`` 関数では、スナップショット取得レベルを指定することで、負荷の小さいパフォーマンス統計情報は頻繁に取得し、データベースへの負荷のかかるパフォーマンス統計情報は頻度を下げて取得することが可能です。
+
+========== =====================================================================
+取得レベル 取得する内容
+========== =====================================================================
+1 基本的なアクセス統計情報およびセッション情報のスナップショットを取得します。
+
+ pg_stat_database, pg_database_size()
+
+ pg_stat_user_tables, pg_statio_user_tables
+
+ pg_stat_user_indexes, pg_statio_user_indexes
+
+ pg_relation_size(), pg_total_relation_size()
+
+ pg_current_xlog_location(), pg_current_xlog_insert_location()
+
+ pg_stat_bgwriter
+
+ pg_stat_activity, pg_locks, pg_stat_statements
+
+
+2 上記に加え、オプティマイザ統計情報のスナップショットを取得します。
+
+ pg_statistic
+
+3 未使用
+4 上記に加え、テーブル/インデックスのフラグメンテーション情報のスナップショットを取得します。
+
+ pgstattuple(), pgstatindex()
+
+5 未使用
+========== =====================================================================
+
+
+
+pgperf.delete_snapshot()関数
+----------------------------
+
+概要
+^^^^
+
+指定したパフォーマンス統計情報のスナップショットのデータを削除します。
+
+定義
+^^^^
+
+::
+
+ integer pgperf.delete_snapshot(integer snapid);
+
+引数
+^^^^
+
+======== ======== ================================
+引数名 引数型 内容
+======== ======== ================================
+snapid integer 削除するスナップショットのスナップショットID
+======== ======== ================================
+
+
+pgperf.purge_snapshots()関数
+----------------------------
+
+概要
+^^^^
+
+パフォーマンス統計情報の古いスナップショットを一括して削除します。
+
+定義
+^^^^
+
+::
+
+ integer pgperf.purge_snapshots(interval period);
+
+引数
+^^^^
+
+======== ======== ================================
+引数名 引数型 内容
+======== ======== ================================
+period interval 削除するスナップショットの期間
+======== ======== ================================
+
+``period`` で指定された期間(インターバル)より前に作成されたスナップショットを削除します。
+
+``interval`` 型の記述方法の詳細については、PostgreSQLのマニュアルを参照してください。
+
+* `日付/時刻データ型 8.5.4. 時間間隔入力 <http://www.postgresql.jp/document/current/html/datatype-datetime.html#DATATYPE-INTERVAL-INPUT>`_
+
+
+pgperf.get_interval()関数
+-------------------------
+
+概要
+^^^^
+
+指定した2つのスナップショットの間隔を秒数で取得します。
+
+パフォーマンス分析を行うスクリプト等を作成する際、この値を使用することによって容易に単位秒あたりの数値に変換することができます。
+
+
+定義
+^^^^
+::
+
+ integer pgperf.get_interval(integer snapid1, integer snapid2)
+
+引数
+^^^^
+
+======== ======== ================================
+引数名 引数型 内容
+======== ======== ================================
+snapid1 integer スナップショットID
+snapid2 integer スナップショットID
+======== ======== ================================
+
diff --git a/docs/ja/pgperf_intro.rst b/docs/ja/pgperf_intro.rst
new file mode 100644
index 0000000..e2610e8
--- /dev/null
+++ b/docs/ja/pgperf_intro.rst
@@ -0,0 +1,147 @@
+PgPerfパッケージの使い方
+========================
+
+この章ではPgPerfパッケージの導入から基本的な使い方を解説します。
+
+PgPerfパッケージのインストール
+------------------------------
+
+PgPerfをインストールするには、スナップショットを取得するデータベースに対して ``pgperf_snapshot_install.sql`` スクリプトを実行して、該当データベース内に ``pgperf`` スキーマ、スナップショット関数、およびスナップショットテーブルを作成します。
+
+.. code-block:: none
+
+ psql -f pgperf_snapshot_install<VERSION>.sql <DBNAME>
+
+PgPerfパッケージのアンインストール
+----------------------------------
+
+PgPerfパッケージをアンインストールする場合には、インストールしてあったデータベースに対して ``pgperf_snapshot_uninstall.sql`` スクリプトを実行します。
+
+.. code-block:: none
+
+ psql -f pgperf_snapshot_uninstall.sql <DBNAME>
+
+``pgperf_snapshot_uninstall.sql`` を実行すると、該当データベース内の ``pgperf`` スキーマ、およびスキーマに含まれるスナップショット関数、スナップショットテーブルを一括して削除します。
+
+スナップショットの取得
+----------------------
+
+スナップショットを取得するには、SQL関数 ``pgperf.create_snapshot()`` を実行します。
+
+スナップショット取得レベルを指定して ``pgperf.create_snapshot()`` 関数を実行すると、各統計情報のスナップショットが取得され、取得されたスナップショットのスナップショットIDが返却されます。
+
+以下の例では、スナップショット取得レベルを ``4`` としてスナップショットを取得し、そのスナップショットIDとして ``1005`` が返却されています。
+
+::
+
+ postgres=# SELECT pgperf.create_snapshot(4);
+ create_snapshot
+ -----------------
+ 1005
+ (1 row)
+
+ postgres=#
+
+
+スナップショット一覧の取得
+--------------------------
+
+スナップショットの一覧を取得するには、 ``pgperf.snapshot`` テーブルの内容を表示します。
+
+以下のように、スナップショットIDとスナップショットを取得した日時を取得することができます。
+
+::
+
+ postgres=# SELECT * FROM pgperf.snapshot;
+ sid | ts | level
+ -----+----------------------------+-------
+ 0 | 2015-04-11 19:11:24.04428 | 1
+ 1 | 2015-04-11 19:11:24.060965 | 2
+ 2 | 2015-04-11 19:11:24.110034 | 4
+ (3 rows)
+
+ postgres=#
+
+
+スナップショットの削除
+----------------------
+
+スナップショットを削除するには、スナップショットIDを指定してSQL関数 ``pgperf.delete_snapshot()`` を実行します。
+
+以下の例では、スナップショットIDが ``2`` のスナップショットを削除しています。
+
+::
+
+ postgres=# SELECT pgperf.delete_snapshot(2);
+ delete_snapshot
+ -----------------
+ t
+ (1 row)
+
+ postgres=# SELECT * FROM pgperf.snapshot;
+ sid | ts | level
+ -----+----------------------------+-------
+ 0 | 2015-04-11 19:11:24.04428 | 1
+ 1 | 2015-04-11 19:11:24.060965 | 2
+ (2 rows)
+
+ postgres=#
+
+
+定期的なスナップショットの取得
+------------------------------
+
+スナップショットを定期的に取得するには、cronなどを用いて定期的に ``pgperf.create_snapshot()`` を実行します。
+
+パッケージに同梱されているシェルスクリプト ``get_snapshot.sh`` は指定したデータベース、または(テンプレート以外の接続が許可されている)すべてのデータベースに対して ``pgperf.create_snapshot()`` を実行します。
+
+以下は ``postgres`` データベースのパフォーマンス統計情報のスナップショットを取得しています。
+
+::
+
+$ ./get_snapshot.sh postgres
+
+以下はすべてのデータベースのパフォーマンス統計情報のスナップショットを取得しています。
+
+::
+
+$ ./get_snapshot.sh
+
+crontabを設定することで、 ``get_snapshot.sh`` スクリプトを定期的に実行してスナップショットを取得することができます。
+
+以下のcrontabの設定では10分おきに ``get_snapshot.sh`` スクリプトを実行して全データベースのパフォーマンス統計情報のスナップショットを取得しています。
+
+::
+
+ 0-59/10 * * * * /path/to/get_snapshot.sh > /dev/null 2>&1
+
+
+古いスナップショットの消し込み
+------------------------------
+
+保存されている古いスナップショットを一括して削除するためには、SQL関数 ``pgperf.purge_snapshots()`` を実行します。
+
+以下の例では、一週間以上前に作成されたスナップショットを削除しています。
+
+::
+
+ postgres=# SELECT sid,ts FROM pgperf.snapshot ORDER BY ts LIMIT 1;
+ sid | ts
+ -----+----------------------------
+ 2 | 2012-10-21 18:20:01.238885
+ (1 row)
+
+ postgres=# SELECT now(),pgperf.purge_snapshots('1 weeks');
+ now | purge_snapshots
+ -------------------------------+-----------------
+ 2012-10-29 14:57:04.092243+09 | 121
+ (1 row)
+
+ postgres=# SELECT sid,ts FROM pgperf.snapshot ORDER BY ts LIMIT 1;
+ sid | ts
+ -----+--------------------------
+ 123 | 2012-10-22 15:00:01.8397
+ (1 row)
+
+ postgres=#
+
diff --git a/docs/ja/pgperf_overview.rst b/docs/ja/pgperf_overview.rst
new file mode 100644
index 0000000..ca7bae0
--- /dev/null
+++ b/docs/ja/pgperf_overview.rst
@@ -0,0 +1,49 @@
+PgPerfパッケージの概要
+======================
+
+PgPerfパッケージとは
+--------------------
+
+PgPerfパッケージは、PostgreSQLの内部で取得できるパフォーマンス統計情報のスナップショットを取得し、専用のスナップショットテーブルに保存するパッケージです。
+
+ここで言う「PostgreSQLのパフォーマンス統計情報」とは、主に以下の統計情報です。
+
+ * pg_stat_databaseなどのシステムビューから取得できるアクセス統計情報
+ * pg_statisticシステムテーブルから取得できるオプティマイザ統計情報
+ * pg_current_xlog_location()などのシステム管理関数から取得できる統計情報
+ * pgstattuple()などの関数で取得できるフラグメンテーション情報
+
+これらのパフォーマンス統計情報を取得・保存しておくことで、運用管理やパフォーマンス管理に不可欠な現状把握や予測に役立てることができます。
+
+
+PgPerfパッケージの特徴
+----------------------
+
+PgPerfパッケージは、主に以下のような特徴を持っています。
+
+ * スクリプト(SQL、PL/pgSQL)のみで動作するため、PostgreSQLの稼働しているプラットフォームに依存しない。
+ * 各種性能情報を容易に取得・保存することができ、蓄積したデータを自由に分析・活用することができる。
+ * インストールおよびアンインストールが容易で、稼働しているPostgreSQLの設定を変更する必要がない。
+
+
+PgPerfパッケージの動作するプラットフォーム
+------------------------------------------
+
+PgPerfパッケージは以下のプラットフォームで動作します。
+
+ * PostgreSQL 9.0, 9,1, 9.2, 9.3, 9.4
+
+動作するOSは問いません。
+
+
+PgPerfパッケージの提供するもの
+------------------------------
+
+PgPerfパッケージは以下を提供します。
+
+ * パッケージで使用する専用のスキーマ
+ * スナップショットを取得するためのSQL関数
+ * スナップショットを保存するためのテーブル
+ * 関連するシェルスクリプト
+
+
diff --git a/docs/ja/pgperf_tables.rst b/docs/ja/pgperf_tables.rst
new file mode 100644
index 0000000..3111923
--- /dev/null
+++ b/docs/ja/pgperf_tables.rst
@@ -0,0 +1,441 @@
+スナップショットテーブル
+========================
+
+この章ではパフォーマンス統計情報のスナップショットを保存するテーブルについて解説します。
+
+スナップショットテーブル一覧
+----------------------------
+
+パフォーマンス統計情報のスナップショットは、PostgreSQL内部の各種統計情報と対応する以下のテーブルに保存されます。
+
+.. csv-table::
+ :header-rows: 1
+
+ テーブル名, 概要, 備考
+ pgperf.snapshot, 取得したスナップショットのIDと取得時間を保存します
+ pgperf.snapshot_pg_stat_database, pg_stat_databaseシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_database_size, データベースのサイズを保存します
+ pgperf.snapshot_pg_stat_user_tables, pg_stat_user_tablesシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_statio_user_tables, pg_statio_user_tablesシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_stat_user_indexes, pg_stat_user_indexesシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_statio_user_indexes, pg_statio_user_indexesシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_statio_user_sequences, pg_statio_user_sequencesシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_stat_user_functions, pg_stat_user_functionsシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_relation_size, テーブルおよびインデックスのサイズを保存します
+ pgperf.snapshot_pg_current_xlog, トランザクションログの挿入位置/書き込み位置を保存します
+ pgperf.snapshot_pg_stat_bgwriter, pg_stat_bgwriterシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_stat_activity, pg_stat_activityシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_locks, pg_locksシステムビューのスナップショットを保存します
+ pgperf.snapshot_pg_statistic, pg_statisticsシステムテーブルのスナップショットを保存します
+ pgperf.snapshot_pg_stat_statements, pg_stat_statementsビューのスナップショットを保存します, 8.4以降のみ
+ pgperf.snapshot_pgstattuple, pgstattuple関数の実行結果のスナップショットを保存します
+ pgperf.snapshot_pgstatindex, pgstatindex関数の実行結果をスナップショットを保存します
+
+pgperf.snapshotテーブル
+-----------------------
+
+スナップショット関数で取得したスナップショットのスナップショットIDおよび取得時間を保持するテーブルです。
+
+===================== ================ ======================== ============
+カラム名 データ型 取得元 備考
+===================== ================ ======================== ============
+sid integer スナップショットID 単調増加
+ts timestamp スナップショット取得時刻
+level integer スナップショットレベル
+===================== ================ ======================== ============
+
+
+
+pgperf.snapshot_pg_stat_databaseテーブル
+----------------------------------------
+
+アクセス統計情報を取得する ``pg_stat_database`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+datid oid pg_stat_database.datid
+datname name pg_stat_database.datname
+numbackends integer pg_stat_database.numbackends
+xact_commit bigint pg_stat_database.xact_commit
+xact_rollback bigint pg_stat_database.xact_rollback
+blks_read bigint pg_stat_database.blks_read
+blks_hit bigint pg_stat_database.blks_hit
+tup_returned bigint pg_stat_database.tup_returned
+tup_fetched bigint pg_stat_database.tup_fetched
+tup_inserted bigint pg_stat_database.tup_inserted
+tup_updated bigint pg_stat_database.tup_updated
+tup_deleted bigint pg_stat_database.tup_deleted
+conflicts bigint pg_stat_database.conflicts 9.1以降のみ
+stats_reset timestampz pg_stat_database.stats_reset 9.1以降のみ
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_database_sizeテーブル
+----------------------------------------
+
+データベースのサイズを取得する ``pg_database_size()`` 関数のスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+datname name pg_database.datname
+pg_database_size bigint pg_database_size()
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_tablesテーブル
+-------------------------------------------
+
+アクセス統計情報を取得する ``pg_stat_user_tables`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+relid oid pg_stat_user_tables.relid
+schemaname name pg_stat_user_tables.schemaname
+relname name pg_stat_user_tables.relname
+seq_scan bigint pg_stat_user_tables.seq_scan
+seq_tup_read bigint pg_stat_user_tables.seq_tup_read
+idx_scan bigint pg_stat_user_tables.idx_scan
+idx_tup_fetch bigint pg_stat_user_tables.idx_tup_fetch
+n_tup_ins bigint pg_stat_user_tables.n_tup_ins
+n_tup_upd bigint pg_stat_user_tables.n_tup_upd
+n_tup_del bigint pg_stat_user_tables.n_tup_del
+n_tup_hot_upd bigint pg_stat_user_tables.n_tup_hot_upd
+n_live_tup bigint pg_stat_user_tables.n_live_tup
+n_dead_tup bigint pg_stat_user_tables.n_dead_tup
+last_vacuum timestampz pg_stat_user_tables.last_vacuum
+last_autovacuum timestampz pg_stat_user_tables.last_autovacuum
+last_analyze timestampz pg_stat_user_tables.last_analyze
+last_autoanalyze timestampz pg_stat_user_tables.last_autoanalyze
+vacuum_count bigint pg_stat_user_tables.vacuum_count 9.1以降のみ
+autovacuum_count bigint pg_stat_user_tables.autovacuum_count 9.1以降のみ
+analyze_count bigint pg_stat_user_tables.analyze_count 9.1以降のみ
+autoanalyze_count bigint pg_stat_user_tables.autoanalyze_count 9.1以降のみ
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_statio_user_tablesテーブル
+---------------------------------------------
+
+アクセス統計情報を取得する ``pg_statio_user_tables`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+relid oid pg_statio_user_tables.relid
+schemaname name pg_statio_user_tables.schemaname
+relname name pg_statio_user_tables.relname
+heap_blks_read bigint pg_statio_user_tables.heap_blks_read
+heap_blks_hit bigint pg_statio_user_tables.heap_blks_hit
+idx_blks_read bigint pg_statio_user_tables.idx_blks_read
+idx_blks_hit bigint pg_statio_user_tables.idx_blks_hit
+toast_blks_read bigint pg_statio_user_tables.toast_blks_read
+toast_blks_hit bigint pg_statio_user_tables.toast_blks_hit
+tidx_blks_read bigint pg_statio_user_tables.tidx_blks_read
+tidx_blks_hit bigint pg_statio_user_tables.tidx_blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_indexesテーブル
+--------------------------------------------
+
+アクセス統計情報を取得する ``pg_stat_user_indexes`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+relid oid pg_stat_user_indexes.relid
+indexrelid oid pg_stat_user_indexes.indexrelid
+schemaname name pg_stat_user_indexes.schemaname
+relname name pg_stat_user_indexes.relname
+indexrelname name pg_stat_user_indexes.indexrelname
+idx_scan bigint pg_stat_user_indexes.idx_scan
+idx_tup_read bigint pg_stat_user_indexes.idx_tup_read
+idx_tup_fetch bigint pg_stat_user_indexes.idx_tup_fetch
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_statio_user_indexesテーブル
+----------------------------------------------
+
+アクセス統計情報を取得する ``pg_statio_user_indexes`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+relid oid pg_statio_user_indexes.relid
+indexrelid oid pg_statio_user_indexes.indexrelid
+schemaname name pg_statio_user_indexes.schemaname
+relname name pg_statio_user_indexes.relname
+indexrelname name pg_statio_user_indexes.indexrelname
+idx_blks_read bigint pg_statio_user_indexes.idx_blks_read
+idx_blks_hit bigint pg_statio_user_indexes.idx_blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_statio_user_sequencesテーブル
+------------------------------------------------
+
+アクセス統計情報を取得する ``pg_statio_user_sequences`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+relid oid pg_statio_user_sequences.relid
+schemaname name pg_statio_user_sequences.schemaname
+relname name pg_statio_user_sequences.relname
+blks_read int8 pg_statio_user_sequences.blks_read
+blks_hit int8 pg_statio_user_sequences.blks_hit
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_user_functionsテーブル
+----------------------------------------------
+
+アクセス統計情報を取得する ``pg_stat_user_functions`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+funcid oid pg_stat_user_functions.funcid
+schemaname name pg_stat_user_functions.schemaname
+funcname name pg_stat_user_functions.funcname
+calls int8 pg_stat_user_functions.calls
+total_time int8 pg_stat_user_functions.total_time
+self_time int8 pg_stat_user_functions.self_time
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_relation_sizeテーブル
+----------------------------------------
+
+テーブルおよびインデックスのサイズを取得する ``pg_relation_size()``, ``pg_total_relation_size()`` 関数のスナップショットを保存するテーブルです。
+
+====================== ================ ====================================== =============================
+カラム名 データ型 取得元 備考
+====================== ================ ====================================== =============================
+sid integer スナップショットID
+schemaname name pg_stat_user_tables.schemaname,
+ pg_stat_user_indexes.schemaname
+relid oid pg_stat_user_tables.relid,
+ pg_stat_user_indexes.indexrelid
+relname name pg_class.relname
+pg_relation_size bigint pg_relaion_size()
+pg_total_relation_size bigint pg_total_relaion_size() 対象がテーブルの場合のみ有効
+====================== ================ ====================================== =============================
+
+
+pgperf.snapshot_pg_current_xlogテーブル
+---------------------------------------
+
+トランザクションログの位置を取得する ``pg_current_xlog_location()``, ``pg_current_xlog_insert_location()`` 関数のスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+location text pg_current_xlog_location()
+insert_location text pg_current_xlog_insert_location()
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_stat_bgwriterテーブル
+----------------------------------------
+
+バックグラウンドライタ統計情報を取得する ``pg_stat_bgwriter`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+checkpoints_timed bigint pg_stat_bgwriter.checkpoints_timed
+checkpoints_req bigint pg_stat_bgwriter.checkpoints_req
+checkpoint_write_time double precision pg_stat_bgwriter.checkpoint_write_time 9.2以降のみ
+checkpoint_sync_time double precision pg_stat_bgwriter.checkpoint_sync_time 9.2以降のみ
+buffers_checkpoint bigint pg_stat_bgwriter.buffers_checkpoint
+buffers_clean bigint pg_stat_bgwriter.buffers_clean
+maxwritten_clean bigint pg_stat_bgwriter.maxwritten_clean
+buffers_backend bigint pg_stat_bgwriter.buffers_backend
+buffers_backend_fsync bigint pg_stat_bgwriter.buffers_backend_fsync 9.1以降のみ
+buffers_alloc bigint pg_stat_bgwriter.buffers_alloc
+stats_reset timestampz pg_stat_bgwriter.stats_reset 9.1以降のみ
+===================== ================ ====================================== ===========
+
+
+pgperf.snapshot_pg_stat_activityテーブル
+----------------------------------------
+
+セッション情報を取得する ``pg_stat_activity`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+datid oid pg_stat_activity.datid
+datname name pg_stat_activity.datname
+procpid int4 pg_stat_activity.procpid 9.1以前
+pid int4 pg_stat_activity.pid 9.2以降
+usesysid oid pg_stat_activity.usesysid
+usename name pg_stat_activity.usename
+application_name text pg_stat_activity.application_name 9.0以降
+client_addr inet pg_stat_activity.client_addr
+client_hostname text pg_stat_activity.client_hostname 9.1以降
+client_port int4 pg_stat_activity.client_port
+backend_start timestamptz pg_stat_activity.backend_start
+xact_start timestamptz pg_stat_activity.xact_start
+query_start timestamptz pg_stat_activity.query_start
+state_change timestamptz pg_stat_activity.state_change 9.2以降
+waiting bool pg_stat_activity.waiting
+state text pg_stat_activity.state 9.2以降
+current_query text pg_stat_activity.current_query 9.1以前
+query text pg_stat_activity.query 9.2以降
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_locksテーブル
+--------------------------------
+
+ロック情報を取得する ``pg_locks`` システムビューのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+locktype text pg_locks.locktype
+database oid pg_locks.database
+relation oid pg_locks.relation
+page int4 pg_locks.page
+tuple int2 pg_locks.tuple
+virtualxid text pg_locks.virtualxid
+transactionid xid pg_locks.transactionid
+classid oid pg_locks.classid
+objid oid pg_locks.objid
+objsubid int2 pg_locks.objsubid
+virtualtransaction text pg_locks.virtualtransaction
+pid int4 pg_locks.pid
+mode text pg_locks.mode
+granted bool pg_locks.granted
+fastpath bool pg_locks.fastpath 9.2以降
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_statisticテーブル
+------------------------------------
+
+オプティマイザ統計情報を保持する ``pg_statistic`` システムテーブルのスナップショットを保存するテーブルです。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+starelid oid pg_statistic.starelid
+starelname name pg_class.relname
+staattnum smallint pg_statistic.staattnum
+staattname name pg_attribute.attname
+stainherit boolean pg_statistic.stainherit 9.0以降
+stanullfrac real pg_statistic.stanullfrac
+stawidth integer pg_statistic.stawidth
+stadistinct real pg_statistic.stadistinct
+stakind1 smallint pg_statistic.stakind1
+stakind2 smallint pg_statistic.stakind2
+stakind3 smallint pg_statistic.stakind3
+stakind4 smallint pg_statistic.stakind4
+stakind5 smallint pg_statistic.stakind5 9.2以降
+staop1 oid pg_statistic.staop1
+staop2 oid pg_statistic.staop2
+staop3 oid pg_statistic.staop3
+staop4 oid pg_statistic.staop4
+staop5 oid pg_statistic.staop5 9.2以降
+stanumbers1 real[] pg_statistic.stanumbers1
+stanumbers2 real[] pg_statistic.stanumbers2
+stanumbers3 real[] pg_statistic.stanumbers3
+stanumbers4 real[] pg_statistic.stanumbers4
+stanumbers5 real[] pg_statistic.stanumbers5 9.2以降
+stavalues1 text pg_statistic.stavalues1
+stavalues2 text pg_statistic.stavalues2
+stavalues3 text pg_statistic.stavalues3
+stavalues4 text pg_statistic.stavalues4
+stavalues5 text pg_statistic.stavalues5 9.2以降
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pg_stat_statementsテーブル
+------------------------------------------
+
+セッション統計情報を取得する ``pg_stat_statements`` システムビューのスナップショットを保存するテーブルです。アドオンモジュール ``pg_stat_statements`` をインストール、設定している場合のみ有効です。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+userid oid pg_stat_statements.userid
+dbid oid pg_stat_statements.dbid
+query text pg_stat_statements.query
+calls bigint pg_stat_statements.calls
+total_time double precision pg_stat_statements.total_time
+rows bigint pg_stat_statements.rows
+shared_blks_hit bigint pg_stat_statements.shared_blks_hit
+shared_blks_read bigint pg_stat_statements.shared_blks_read
+shared_blks_dirtied bigint pg_stat_statements.shared_blks_dirtied 9.2以降
+shared_blks_written bigint pg_stat_statements.shared_blks_written
+local_blks_hit bigint pg_stat_statements.local_blks_hit
+local_blks_read bigint pg_stat_statements.local_blks_read
+local_blks_dirtied bigint pg_stat_statements.local_blks_dirtied 9.2以降
+local_blks_written bigint pg_stat_statements.local_blks_written
+temp_blks_read bigint pg_stat_statements.temp_blks_read
+temp_blks_written bigint pg_stat_statements.temp_blks_written
+blk_read_time double precision pg_stat_statements.blk_read_time 9.2以降
+blk_write_time double precision pg_stat_statements.blk_write_time 9.2以降
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pgstattupleテーブル
+-----------------------------------
+
+テーブルのフラグメンテーション情報を取得する ``pgstattuple()`` 関数のスナップショットを保存するテーブルです。アドオンモジュール ``pgstattuple`` をインストール、設定している場合のみ有効です。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+schemaname name pg_stat_user_tables.schemaname
+relname name pg_stat_user_tables.relname
+table_len int8 pgstattuple().table_len
+tuple_count int8 pgstattuple().tuple_count
+tuple_len int8 pgstattuple().tuple_len
+tuple_percent float8 pgstattuple().tuple_percent
+dead_tuple_count int8 pgstattuple().dead_tuple_count
+dead_tuple_len int8 pgstattuple().dead_tuple_len
+dead_tuple_percent float8 pgstattuple().dead_tuple_percent
+free_space int8 pgstattuple().free_space
+free_percent float8 pgstattuple().free_percent
+===================== ================ ====================================== ===========
+
+pgperf.snapshot_pgstatindexテーブル
+-----------------------------------
+
+インデックスのフラグメンテーション情報を取得する ``pgstatindex()`` 関数のスナップショットを保存するテーブルです。アドオンモジュール ``pgstattuple`` をインストール、設定している場合のみ有効です。
+
+===================== ================ ====================================== ===========
+カラム名 データ型 取得元 備考
+===================== ================ ====================================== ===========
+sid integer スナップショットID
+schemaname name pg_stat_user_indexes.schemaname
+relname name pg_stat_user_indexes.relname
+indexrelname name pg_stat_user_indexes.indexrelname
+version int4 pgstatindex().version
+tree_level int4 pgstatindex().tree_level
+index_size int8 pgstatindex().index_size
+root_block_no int8 pgstatindex().root_block_no
+internal_pages int8 pgstatindex().internal_pages
+leaf_pages int8 pgstatindex().leaf_pages
+empty_pages int8 pgstatindex().empty_pages
+deleted_pages int8 pgstatindex().deleted_pages
+avg_leaf_density float8 pgstatindex().avg_leaf_density
+leaf_fragmentation float8 pgstatindex().leaf_fragmentation
+===================== ================ ====================================== ===========
+
diff --git a/docs/ja/pt-config.rst b/docs/ja/pt-config.rst
new file mode 100644
index 0000000..3c79af4
--- /dev/null
+++ b/docs/ja/pt-config.rst
@@ -0,0 +1,107 @@
+pt-config
+=========
+
+概要
+----
+
+PostgreSQLの設定ファイル ``postgresql.conf`` の設定値を参照、変更します。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-config [options...] get [PARAM]
+ pt-config [options...] set [PARAM] [VALUE]
+ pt-config [options...] disable [PARAM]
+
+コマンド
+--------
+
+.. csv-table::
+
+ ``get [PARAM]``, 現在の値を表示します。無効(コメントアウト)の場合、 ``(disabled)`` と表示されます。
+ ``set [PARAM] [VALUE]``, 新しい値を設定します。無効(コメントアウト)な場合は、有効にします。
+ ``disable [PARAM]``, 設定値を無効にします(コメントアウトします)。
+
+オプション
+----------
+
+.. code-block:: none
+
+ -D, --pgdata=PGDATA Specify a PostgreSQL database cluster.
+ --apply Apply change(s).
+ --help Print this help.
+
+``-D``, ``--pgdata`` オプションは、PostgreSQLデータベースクラスタを指定します。オプションが指定されない場合は、PGDATA環境変数に設定された値が使われます。
+
+``--apply`` オプションは、 ``set`` / ``disable`` コマンドを実行する際に、実際に変更内容を postgresql.conf ファイルに適用します。
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``Reading:``, 読み込んでいる ``postgresql.conf`` ファイルをフルパスで表示します。
+ ``Dry-run mode:``, 設定する前後の値を表示するのみで、実際の設定変更は行いません。
+ ``Applying:``, 実際の設定変更を行っています。
+ ``Old``, 変更する前の値を表します。
+ ``New``, 変更する前の値を表します。
+ ``Updating:``, 変更を行っている ``postgresql.conf`` ファイルをフルパスで表示します。
+
+実行例
+------
+
+現在の ``shared_buffers`` の値を表示します。
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data get shared_buffers
+ [2015-04-16 17:08:12] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ 256MB
+ $
+
+``shared_buffers`` の値を ``512MB`` に設定します(実際の設定ファイルの変更は行いません)。
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data set shared_buffers 512MB
+ [2015-04-16 17:08:44] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:08:44] INFO: Dry-run mode:
+ [2015-04-16 17:08:44] INFO: Old: shared_buffers = 256MB # min 128kB
+ [2015-04-16 17:08:44] INFO: New: shared_buffers = 512MB # min 128kB
+ $
+
+``shared_buffers`` の値を ``512MB`` に設定します(実際に設定ファイルの変更を行います)。
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data --apply set shared_buffers 512MB
+ [2015-04-16 17:09:11] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:09:11] INFO: Applying:
+ [2015-04-16 17:09:11] INFO: Old: shared_buffers = 256MB # min 128kB
+ [2015-04-16 17:09:11] INFO: New: shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:11] INFO: Updated: /var/lib/pgsql/9.4/data/postgresql.conf
+ $
+
+``shared_buffers`` の設定を無効化(コメントアウト)します。
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data --apply disable shared_buffers
+ [2015-04-16 17:09:52] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ [2015-04-16 17:09:52] INFO: Applying:
+ [2015-04-16 17:09:52] INFO: Old: shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:52] INFO: New: #shared_buffers = 512MB # min 128kB
+ [2015-04-16 17:09:52] INFO: Updated: /var/lib/pgsql/9.4/data/postgresql.conf
+ $
+
+(無効化されている) ``shared_buffers`` の値を表示します。
+
+.. code-block:: none
+
+ $ pt-config -D /var/lib/pgsql/9.4/data get shared_buffers
+ [2015-04-16 17:10:00] INFO: Reading: /var/lib/pgsql/9.4/data/postgresql.conf
+ 512MB (disabled)
+ $
diff --git a/docs/ja/pt-index-usage.rst b/docs/ja/pt-index-usage.rst
new file mode 100644
index 0000000..2fc605f
--- /dev/null
+++ b/docs/ja/pt-index-usage.rst
@@ -0,0 +1,113 @@
+
+pt-index-usage
+==============
+
+概要
+----
+
+インデックスの使用状況を表示します。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-index-usage [option...]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ -i, --index=STRING
+ -u, --unused
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+``-o``, ``--owner`` オプションは、指定した文字列に合致する名前のユーザが所有者となっているインデックスの情報のみを表示します。
+
+``-n``, ``--schema`` オプションは、指定した文字列に合致する名前のスキーマにあるインデックスの情報のみを表示します。
+
+``-t``, ``--table`` オプションは、指定した文字列に合致する名前のテーブルに作成されたインデックスの情報のみを表示します。
+
+``-i``, ``--index`` オプションは、指定した文字列に合致する名前のインデックスの情報のみを表示します。
+
+``-u``, ``--unused`` オプションは、使われていないインデックスの情報のみを表示します。
+
+``-d`` (または ``--dbname`` ), ``-o`` (または ``--owner`` ), ``-n`` (または ``--schema`` ), ``-t`` (または ``--table`` ), ``-i`` (または ``--index`` ), ``-u`` (または ``--unused`` )を同時に指定した場合には、すべての条件に合致するインデックスのみが表示対象となります。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``OID``, インデックスのオブジェクトID
+ ``OWNER``, インデックスの所有者のユーザ名
+ ``SCHEMA``, インデックスの存在しているスキーマ名
+ ``TABLE``, インデックスが作成されているテーブル名
+ ``INDEX``, インデックス名
+ ``BLKS``, インデックスのブロック数(8kB単位)
+ ``SCAN``, インデックススキャンの実行回数
+ ``T_READ``, インデックススキャンによって取得されたインデックスエントリ数
+ ``T_FTCH``, インデックススキャンによってテーブルから読まれたタプル数
+ ``B_READ``, ディスクから読み込まれたインデックスのブロック数
+ ``B_HIT``, 共有バッファから読み込まれたインデックスのページ数
+ ``STATUS``, インデックスのステータス。このステータスは ``pg_index`` システムテーブルから取得します。
+ ``TABLESPACE``, インデックスの配置されているテーブルスペース名。
+
+実行例
+------
+
+``public`` スキーマにあるテーブルのインデックスについて、利用状況を表示します。
+
+.. code-block:: none
+
+ $ pt-index-usage -n public -d postgres
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26793 | snaga | public | pgbench_accounts | pgbench_accounts_pkey | 276 | 1 | 1 | 1 | 4 | 0 | | spc1 |
+ | 26789 | snaga | public | pgbench_branches | pgbench_branches_pkey | 2 | 1 | 1 | 0 | 2 | 0 | | pg_default |
+ | 26791 | snaga | public | pgbench_tellers | pgbench_tellers_pkey | 2 | 0 | 0 | 0 | 0 | 0 | | pg_default |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
+
+``public`` スキーマ内の ``pgbench_accounts`` テーブルにあるインデックスについて、利用状況を表示します。
+
+.. code-block:: none
+
+ $ pt-index-usage -n public -d postgres -t pgbench_accounts
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26793 | snaga | public | pgbench_accounts | pgbench_accounts_pkey | 276 | 1 | 1 | 1 | 4 | 0 | | spc1 |
+ +-------+-------+--------+------------------+-----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
+
+``public`` スキーマ内の一度も使われていないインデックスについて、利用状況を表示します。
+
+.. code-block:: none
+
+ $ pt-index-usage -d postgres -n public -u
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | OID | OWNER | SCHEMA | TABLE | INDEX | BLKS | SCAN | T_READ | T_FTCH | B_READ | B_HIT | STATUS | TABLESPACE |
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ | 26791 | snaga | public | pgbench_tellers | pgbench_tellers_pkey | 2 | 0 | 0 | 0 | 0 | 0 | | pg_default |
+ +-------+-------+--------+-----------------+----------------------+------+------+--------+--------+--------+-------+--------+------------+
+ $
diff --git a/docs/ja/pt-kill.rst b/docs/ja/pt-kill.rst
new file mode 100644
index 0000000..8597aa6
--- /dev/null
+++ b/docs/ja/pt-kill.rst
@@ -0,0 +1,53 @@
+pt-kill
+=======
+
+概要
+----
+
+PostgreSQLバックエンドの処理を中断、またはバックエンドを終了させます。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-kill [options...] [command] [pid]
+
+
+コマンド
+--------
+
+.. code-block:: none
+
+ cancel Cancel a running query.
+ terminate Terminate a backend with canceling query.
+
+オプション
+----------
+
+.. code-block:: none
+
+ --help Print this help.
+
+出力項目
+--------
+
+特になし。
+
+実行例
+------
+
+プロセスID 3289 で実行中のSQLをキャンセルします。
+
+.. code-block:: none
+
+ $ pt-kill cancel 3289
+
+
+プロセスID 3291 で実行中のバックエンドを終了します。
+
+.. code-block:: none
+
+ $ pt-kill terminate 3291
+
diff --git a/docs/ja/pt-proc-stat.rst b/docs/ja/pt-proc-stat.rst
new file mode 100644
index 0000000..b705301
--- /dev/null
+++ b/docs/ja/pt-proc-stat.rst
@@ -0,0 +1,92 @@
+
+pt-proc-stat
+============
+
+概要
+----
+
+各プロセスごとのI/O統計量を表示します。
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-proc-stat [option...] [delay [count]]
+
+
+``pt-proc-stat`` コマンドは、procファイルシステムを参照します。PostgreSQLの実行ユーザ、またはrootユーザで実行する必要があります。
+
+オプション
+----------
+
+.. code-block:: none
+
+ -D, --pgdata=DATADIR
+ -P, --pid=PID
+ --help
+
+``-D``, ``--pgdata`` オプションは、データベースクラスタのディレクトリを指定します。
+
+``-P``, ``--pid`` オプションは、PostmasterプロセスのプロセスIDを指定します。
+
+``-D`` (または ``--pgdata`` ), ``-P`` (または ``--pid`` )のいずれも指定しなかった場合は、全プロセスの中からPostmasterプロセスを探して、そのプロセスIDを対象とします。
+
+複数のPostmasterプロセスが見つかった場合には、(プロセスIDの大小とは無関係に)そのうちの1つのみを対象とします。
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``PROCESS NAME``, プロセス名
+ ``PID``, プロセスID
+ ``STAT``, プロセスのステータス
+ ``USR``, ユーザCPU使用時間(差分)
+ ``SYS``, システムCPU使用時間(差分)
+ ``VSZ``, 仮想メモリサイズ(MB単位)
+ ``RSS``, 物理メモリ使用量(MB単位)_
+ ``READ``, 読み込みのディスクI/O量(KB単位、差分)
+ ``WRITE``, 書き込みのディスクI/O量(KB単位、差分)
+ ``READ2``, ``READ`` を除いた読み込みI/O量(KB単位、差分)
+ ``WRITE2``, ``WRITE`` を除いた書き込みI/O量(KB単位、差分)
+
+実行例
+------
+
+Postmasterプロセスを自動的に探し、Postmasterおよび子プロセスの統計を5秒間隔で2回表示して終了します。
+
+.. code-block:: none
+
+ $ sudo ./pt-proc-stat 5 2
+ Fri May 1 22:23:39 JST 2015
+ PROCESS NAME[ PID] STAT USR SYS VSZ RSS READ WRITE READ2 WRITE2
+ postmaster[24026] S 4 13 100 9 23752 1290092 1090800 155357
+ logger[24028] S 0 1 85 1 4 76 30 -45
+ checkpointer[24030] S 4 117 100 8 176 56768 -176 -46965
+ writer[24031] S 104 66 100 9 0 291080 0 130560
+ wal writer[24032] S 8 19 100 1 0 2928 0 0
+ autovacuum launcher[24033] S 3 3 101 2 8 8 288 0
+ stats collector[24034] S 13 32 85 1 0 2140 34 -649
+ snaga postgres 127.0[25473] R 32 9 101 7 296 1472 1264 0
+ snaga postgres 127.0[25474] R 33 9 101 7 424 1384 1120 0
+ snaga postgres 127.0[25475] R 33 9 101 7 424 1448 1016 0
+ snaga postgres 127.0[25476] S 32 9 101 7 580 1400 780 0
+ snaga postgres 127.0[25477] R 32 9 101 7 908 1368 492 0
+
+ Fri May 1 22:23:44 JST 2015
+ PROCESS NAME[ PID] STAT USR SYS VSZ RSS READ WRITE READ2 WRITE2
+ postmaster[24026] S 0 0 100 9 0 0 0 0
+ logger[24028] S 0 0 85 1 0 0 0 0
+ checkpointer[24030] S 0 0 100 8 0 0 0 0
+ writer[24031] S 4 1 100 9 0 11928 0 392
+ wal writer[24032] S 0 0 100 1 0 0 0 0
+ autovacuum launcher[24033] S 0 0 101 2 0 0 0 0
+ stats collector[24034] S 0 0 85 1 0 0 0 0
+ snaga postgres 127.0[25473] R 72 18 101 10 1772 3608 1740 0
+ snaga postgres 127.0[25474] R 68 20 101 10 1436 3920 2020 0
+ snaga postgres 127.0[25475] D 70 18 101 10 1304 4216 2368 0
+ snaga postgres 127.0[25476] R 70 20 101 10 1252 3384 2212 0
+ snaga postgres 127.0[25477] R 73 16 101 10 1464 3224 2080 0
+
+ $
diff --git a/docs/ja/pt-replication-stat.rst b/docs/ja/pt-replication-stat.rst
new file mode 100644
index 0000000..decb563
--- /dev/null
+++ b/docs/ja/pt-replication-stat.rst
@@ -0,0 +1,91 @@
+
+pt-replication-stat
+===================
+
+概要
+----
+
+レプリケーションのマスターのノードの統計情報を参照してレプリケーションの実行状況を表示します。指定したインターバルごとに連続的に表示することも可能です。
+
+PostgreSQL 9.1またはそれ以降のバージョンで動作します。PostgreSQL 9.0は ``pg_stat_replication`` システムビューが無いため、動作しません。
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-replication-stat [option...] [delay [count]]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``PID``, マスターノードで動作しているWAL送信プロセスのプロセスID
+ ``NAME``, レプリケーション先として登録されているスレーブノードの名称
+ ``HOST``, スレーブノードのホスト名またはIPアドレス
+ ``PORT``, スレーブノードに接続しているマスターノードのポート番号
+ ``STATE``, スレーブノードの状態です。 ``startup`` 、 ``backup`` 、 ``catchup`` 、 ``streaming`` のいずれかを取る。
+ ``SENT``, スレーブへ送信されたWAL上の位置
+ ``WRITTTEN``, スレーブ上でのWALバッファへの書き込みされたWAL上の位置
+ ``FLUSHED``, スレーブ上でのWALファイルへの同期書き込みされたWAL上の位置
+ ``REPLAYED``, スレーブ上でのデータファイルへの適用されたWAL上の位置
+ ``PRI``, スレーブノードが同期レプリケーションの場合の、ノードの優先度を表示します。
+ ``MODE``, 動作しているモードを表示します。``sync`` は同期モード、``async`` は非同期モード、``potential`` は非同期モードで動作中ではあるが同期モードに昇格する可能性がある。
+
+
+実行例
+------
+
+ホスト ``127.0.0.1`` のポート ``5433`` にユーザ ``postgres`` で接続し、5秒ごとに統計情報を2回表示して終了します。
+
+.. code-block:: none
+
+ $ pt-replication-stat -h 127.0.0.1 -p 5433 -U postgres 5 2
+ Sat Mar 28 21:45:23 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/5F30398 | 0/5F300B0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0/5F2FE48 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ Sat Mar 28 21:45:28 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/608CD68 | 0/608CAC0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/608CAC0 | 0/608CAC0 | 0/608C7D8 | 0/608C7D8 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/608CAC0 | 0/608CAC0 | 0/608C7D8 | 0/608C7D8 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ $
+
+``localhost`` のデフォルトポート(``5432``)に接続して、5秒ごとに統計情報を表示し続けます。CTRL-Cで終了します。
+
+.. code-block:: none
+
+ $ pt-replication-stat -h localhost 5
+ Sat Mar 28 21:45:23 JST 2015
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | PID | NAME | HOST | PORT | STATE | SENT | WRITTTEN | FLUSHED | REPLAYED | PRI | MODE |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+ | | | | | local | 0/5F30398 | 0/5F300B0 | | | | master |
+ | 3323 | replica1 | 127.0.0.1 | 55580 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0 | async |
+ | 3367 | replica2 | 127.0.0.1 | 55589 | streaming | 0/5F300B0 | 0/5F300B0 | 0/5F2FE48 | 0/5F2FE48 | 0 | async |
+ +------+----------+-----------+-------+-----------+-----------+-----------+-----------+-----------+-----+--------+
+
+ ^C[2015-03-28 21:45:25] INFO: Terminated.
+ $
diff --git a/docs/ja/pt-session-profiler.rst b/docs/ja/pt-session-profiler.rst
new file mode 100644
index 0000000..9d2b3f8
--- /dev/null
+++ b/docs/ja/pt-session-profiler.rst
@@ -0,0 +1,66 @@
+
+pt-session-profiler
+===================
+
+概要
+----
+
+ネットワークトラフィックをキャプチャし、PostgreSQLのセッションを検出してクエリおよびパフォーマンス情報を表示します。
+
+tcpdumpを実行するため、 ``root`` 権限が必要です。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-session-profiler [option...]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -i [INTERFACE]
+ -T, --threshold=MILLISECONDS
+ --help
+
+``-h``, ``--host`` オプションは、解析するPostgreSQLセッションのPostgreSQLサーバのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、すべてのサーバ名またはIPアドレスのパケットを解析対象とします。
+
+``-p``, ``--port`` オプションは、解析するPostgreSQLセッションのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-i`` オプションは、キャプチャするネットワークインターフェースを指定します。省略した場合にはすべてのネットワークインターフェース(``any``)が対象となります。
+
+``-T``, ``--threshold`` オプションは、表示するクエリの実行時間の閾値を指定します。単位はミリ秒です。デフォルトは ``1000`` です。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``sess``, セッションを表す一意な文字列(セッションごとに異なる値。送信元のIPアドレスとポート番号、および宛先のIPアドレスとポート番号を、MD5でハッシュ化した文字列の先頭12文字を使用)
+ ``time``, クエリの実行時間
+ ``query``, 実行したクエリ文字列
+
+実行例
+------
+
+すべてのネットワークインターフェースを通過するTCPパケットのうち、ポート 5432 宛のものを監視して、実行に500ミリ秒以上かかったクエリを表示します。CTRL-Cで終了します。
+
+.. code-block:: none
+
+ $ sudo pt-session-profiler -T 500
+ [2015-03-29 15:07:22] INFO: Threshold: 500 ms
+ [2015-03-29 15:07:22] INFO: tcpdump -l -i any -s 0 -X -p tcp port 5432
+ [2015-03-29 15:07:36] INFO: sess:e27f20dae08f, time:0:00:00.557728, query:UPDATE pgbench_tellers SET tbalance = tbalance + 2084 WHERE tid = 23;
+ [2015-03-29 15:07:36] INFO: sess:b3674d7bbea0, time:0:00:00.980950, query:INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (32, 5, 255511, 2695, CURRENT_TIMESTAMP);
+ [2015-03-29 15:07:45] INFO: sess:1c32286cab7a, time:0:00:01.115904, query:SELECT abalance FROM pgbench_accounts WHERE aid = 161999;
+ [2015-03-29 15:07:45] INFO: sess:33f8c268624c, time:0:00:00.526850, query:UPDATE pgbench_accounts SET abalance = abalance + 3877 WHERE aid = 326415;
+ [2015-03-29 15:07:46] INFO: sess:b370afd07dcf, time:0:00:00.719780, query:SELECT abalance FROM pgbench_accounts WHERE aid = 852680;
+ [2015-03-29 15:07:46] INFO: sess:0f04724051ad, time:0:00:00.543609, query:BEGIN;
+ ^C[2015-03-29 15:07:51] INFO: Terminated.
+ $
diff --git a/docs/ja/pt-set-tablespace.rst b/docs/ja/pt-set-tablespace.rst
new file mode 100644
index 0000000..dbece10
--- /dev/null
+++ b/docs/ja/pt-set-tablespace.rst
@@ -0,0 +1,126 @@
+
+pt-set-tablespace
+=================
+
+概要
+----
+
+指定したテーブルと関連するインデックスのテーブルスペースを一括して変更します。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-set-tablespace [option...] [tablespace]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ -l, --list
+ --apply
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+``-o``, ``--owner`` オプションは、指定した文字列に合致する名前のユーザが所有者となっているテーブルのみを対象とします。
+
+``-n``, ``--schema`` オプションは、指定した文字列に合致する名前のスキーマにあるテーブルのみを対象とします。
+
+``-t``, ``--table`` オプションは、指定した文字列に合致する名前のテーブルのみを対象とします。
+
+``--apply`` オプションは、テーブルスペースの変更を実際にデータベースに反映します。
+
+``-l``, ``--list`` オプションは、テーブルスペースの情報を表示します。
+
+``-o`` (または ``--owner`` ), ``-n`` (または ``--schema`` ), ``-t`` (または ``--table`` ) を同時に指定した場合には、すべての条件に合致するテーブルのみが対象となります。
+
+1つ以上のテーブルの移動に失敗した場合には終了コード ``1`` を返します。すべてのファイルの移動に成功した場合には ``0`` を返します。
+
+出力項目
+--------
+
+``-l``, ``--list`` オプションで表示される項目は以下の通りです。
+
+.. csv-table::
+
+ ``OID``, テーブルスペースのオブジェクトID
+ ``OWNER``, テーブルスペースの所有者のユーザ名
+ ``TABLESPACE``, テーブルスペース名
+ ``LOCATION``, テーブルスペースの配置されているディレクトリのパス
+ ``USE%``, テーブルスペースの配置されているパーティションのディスク使用率
+ ``AVAIL``, テーブルスペースの配置されているパーティションの空き容量
+
+その他の表示項目は以下の通りです。
+
+.. csv-table::
+
+ ``Dry-run mode``, 発行するALTER TABLE/INDEX文を表示しますが、実際の移動は行いません。
+ ``Applying ALTER TABLE/INDEX``, ALTER TABLE/INDEX文を実際に実行して、テーブル/インデックスのテーブルスペースを変更します。
+ ``X tables/indexes moved. Y failed.``, ``X`` 個のテーブル/インデックスを移動に成功し、 ``Y`` 個の移動に失敗しました。
+
+
+実行例
+------
+
+PostgreSQLインスタンスに存在するテーブルスペースの一覧を表示します。 各パーティションの使用領域を取得し、併せて一覧として表示します。
+
+.. code-block:: none
+
+ $ pt-set-tablespace --list
+ +--------+----------+------------+---------------------------+------+-------+
+ | OID | OWNER | TABLESPACE | LOCATION | USE% | AVAIL |
+ +--------+----------+------------+---------------------------+------+-------+
+ | 1663 | postgres | pg_default | | | |
+ | 1664 | postgres | pg_global | | | |
+ | 121263 | postgres | hddspc2 | /disk/disk2/pgsql | 85% | 80G |
+ | 16818 | postgres | ssdspc1 | /disk/disk1/tblspc1 | 67% | 127G |
+ | 305242 | postgres | ssdspc2 | /disk/disk3/pgsql/ssdspc2 | 98% | 13G |
+ +--------+----------+------------+---------------------------+------+-------+
+ $
+
+``dbt3`` データベースにある ``orders`` テーブル、および ``orders`` テーブルに作成されたインデックスのすべてを、 ``ssdspc1`` テーブルスペースに移動するための ``ALTER TABLE`` 文および ``ALTER INDEX`` 文を表示します。(実際の移動は行いません)
+
+.. code-block:: none
+
+ $ pt-set-tablespace -d dbt3 --table orders ssdspc1
+ [2015-04-29 17:35:24] INFO: Dry-run mode:
+ [2015-04-29 17:35:24] INFO: ALTER TABLE "public"."orders" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."pk_orders" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."i_o_orderdate" SET TABLESPACE "ssdspc1";
+ [2015-04-29 17:35:24] INFO: ALTER INDEX "public"."i_o_custkey" SET TABLESPACE "ssdspc1";
+ $
+
+``dbt3`` データベースにある ``orders`` テーブル、および ``orders`` テーブルに作成されたインデックスのすべてを、 ``ssdspc1`` テーブルスペースに移動します。
+
+.. code-block:: none
+
+ $ pt-set-tablespace -d dbt3 --table orders --apply ssdspc1
+ [2015-04-29 17:37:06] INFO: Applying ALTER TABLE/INDEX...
+ [2015-04-29 17:37:08] INFO: 4 tables/indexes moved. 0 failed.
+ $
+
+``dbt3`` スキーマ内にあるすべてのテーブル、およびすべてのインデックスを、 ``ssdspc1`` テーブルスペースに移動します。
+
+.. code-block:: none
+
+ $ pt-set-tablespace --schema dbt3 --apply ssdspc1
+ [2015-04-29 17:38:39] INFO: Applying ALTER TABLE/INDEX...
+ [2015-04-29 17:38:57] INFO: 31 tables/indexes moved. 0 failed.
+ $
diff --git a/docs/ja/pt-snap-statements.rst b/docs/ja/pt-snap-statements.rst
new file mode 100644
index 0000000..9ac4c06
--- /dev/null
+++ b/docs/ja/pt-snap-statements.rst
@@ -0,0 +1,113 @@
+
+pt-snap-statements
+==================
+
+概要
+----
+
+2つの時刻のSQL文の統計情報の差分を計算して表示します。
+
+オプションを指定することにより、特定の項目ごとにソートして表示することができます。
+
+contribモジュールの ``pg_stat_statements`` が導入されている必要があります。
+
+また、 ``track_io_timing`` オプションが有効にされている必要があります。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-snap-statements [option...] [interval]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -s, --sort=KEY
+ -l
+ -t, --top=NUMBER
+ -R, --reset
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+``-s`` オプションは、ソートする項目を指定します(未実装)。``KEY`` は、次のいずれかの値を取ることができます: ``CALLS``, ``T_TIME``, ``ROWS``, ``B_HIT``, ``B_READ``, ``B_DIRT``, ``B_WRTN``, ``R_TIME``, ``W_TIME``
+
+``-l`` オプションは、ブロックの種別(共有バッファ、ローカルバッファ、一時バッファ)ごとに詳細な内訳を表示します(未実装)。``-l`` オプションを指定しない場合は、共有バッファ、ローカルバッファ、一時バッファの数値を合算した値が表示されます。
+
+``-t``, ``--top`` オプションは、表示するクエリの数を指定します。指定しない場合はすべてのクエリが表示されます。
+
+``-R``, ``--reset`` オプションは、``pg_stat_statements`` ビューの統計情報を初期化します。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``USER``, クエリを実行したユーザ名
+ ``DBNAME``, クエリを実行したデータベース名
+ ``QUERYID``, 実行されたクエリのクエリID(16進数表記)
+ ``QUERY``, 実行されたクエリ(最大30文字で切り詰め)
+ ``CALLS``, クエリの実行回数
+ ``T_TIME``, クエリの総実行時間(ミリ秒)
+ ``ROWS``, クエリによって取得または影響を受けた行の総数
+ ``B_HIT``, ブロック読み込みの際にバッファから読み込んだブロック総数
+ ``B_READ``, ブロック読み込みの際にディスクから読み込んだブロック総数
+ ``B_DIRT``, クエリによってページが更新されたページ総数
+ ``B_WRTN``, クエリによってディスクに書き込まれたブロック総数
+ ``R_TIME``, ディスクからのブロック読み込みにかかった総時間(ミリ秒) (``track_io_timing`` パラメータが有効になっている必要がある)
+ ``W_TIME``, ディスクへのブロック書き込みにかかった総時間(ミリ秒) (``track_io_timing`` パラメータが有効になっている必要がある)
+
+
+実行例
+------
+
+``postgres`` データベースに接続し、で5秒間に実行されたSQL文を総実行時間(``T_TIME``)の長い順にソートしてすべて表示します。
+
+.. code-block:: none
+
+ $ pt-snap-statements -d postgres 5
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | USER | DBNAME | QUERYID | QUERY | CALLS | T_TIME | ROWS | B_HIT | B_READ | B_DIRT | B_WRTN | R_TIME | W_TIME |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | snaga | postgres | 80053daf | UPDATE pgbench_branches SET bb | 677 | 12007 | 677 | 9160 | 1 | 1 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | 1675159e | UPDATE pgbench_tellers SET tba | 681 | 7648 | 681 | 3403 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | ec088219 | UPDATE pgbench_accounts SET ab | 684 | 530 | 684 | 2289 | 585 | 568 | 0 | 125.9 | 0.0 |
+ | snaga | postgres | 198383d | SELECT abalance FROM pgbench_a | 682 | 73 | 682 | 2080 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | da8cc6f | INSERT INTO pgbench_history (t | 676 | 34 | 676 | 704 | 12 | 10 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | d4e6bf94 | BEGIN; | 684 | 4 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | a81672e | END; | 671 | 3 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | 8caa574 | select count(*) from pgbench_b | 1 | 0 | 1 | 4 | 0 | 0 | 0 | 0.0 | 0.0 |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ $
+
+ホスト ``192.168.1.101`` のポート ``5433`` で稼働しているPostgreSQLサーバの データベース ``postgres`` にユーザ ``snaga`` で接続し、5秒間に実行されたSQL文を総実行時間(``T_TIME``)の長い順にソートしてトップ5件を表示します。
+
+.. code-block:: none
+
+ $ pt-snap-statements --host 192.168.1.101 -p 5433 -U snaga -d postgres -t 5 5
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | USER | DBNAME | QUERYID | QUERY | CALLS | T_TIME | ROWS | B_HIT | B_READ | B_DIRT | B_WRTN | R_TIME | W_TIME |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ | snaga | postgres | 80053daf | UPDATE pgbench_branches SET bb | 503 | 9953 | 503 | 8430 | 14 | 7 | 0 | 0.6 | 0.0 |
+ | snaga | postgres | 1675159e | UPDATE pgbench_tellers SET tba | 508 | 6483 | 508 | 2551 | 10 | 9 | 0 | 0.3 | 0.0 |
+ | snaga | postgres | ec088219 | UPDATE pgbench_accounts SET ab | 511 | 560 | 511 | 1424 | 698 | 477 | 7 | 91.0 | 12.1 |
+ | snaga | postgres | 198383d | SELECT abalance FROM pgbench_a | 511 | 93 | 511 | 1550 | 0 | 0 | 0 | 0.0 | 0.0 |
+ | snaga | postgres | da8cc6f | INSERT INTO pgbench_history (t | 503 | 20 | 503 | 530 | 13 | 11 | 0 | 0.1 | 0.0 |
+ +-------+----------+----------+--------------------------------+-------+--------+------+-------+--------+--------+--------+--------+--------+
+ $
+
diff --git a/docs/ja/pt-stat-snapshot.rst b/docs/ja/pt-stat-snapshot.rst
new file mode 100644
index 0000000..c44fbb3
--- /dev/null
+++ b/docs/ja/pt-stat-snapshot.rst
@@ -0,0 +1,121 @@
+
+pt-stat-snapshot
+================
+
+概要
+----
+
+統計情報のスナップショットを取得、保存、管理します。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-stat-snapshot [option...] install
+ pt-stat-snapshot [option...] uninstall
+ pt-stat-snapshot [option...] create [level]
+ pt-stat-snapshot [option...] list
+ pt-stat-snapshot [option...] delete [sid]
+ pt-stat-snapshot [option...] export [file]
+ pt-stat-snapshot [option...] import [file]
+
+コマンド
+--------
+
+.. csv-table::
+
+ ``install``, 動作に必要なスキーマ、テーブルや関数の作成などを行います。
+ ``uninstall``, 関連するスキーマ、テーブルや関数などを削除します。
+ ``create [level]``, スナップショットを取得します。レベルには ``1``, ``2``, ``4`` のいずれかを指定できます。
+ ``list``, 保存されているスナップショットの一覧を表示します。
+ ``delete [sid]``, 指定したスナップショットIDのスナップショットを削除します。 ``M:N`` のように記述することで範囲指定することが可能です。
+ ``export [file]``, 指定したファイルにスナップショットデータをエクスポートします。
+ ``import [file]``, 指定したファイルからスナップショットデータをインポートします。
+
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+
+出力項目
+--------
+
+``list`` コマンドで表示される項目は以下の通りです。
+
+.. csv-table::
+
+ ``SID``, スナップショットID(スナップショットを取得するごとに単調増加する整数値)
+ ``TIMESTAMP``, スナップショットを取得したタイムスタンプ
+ ``LEVEL``, スナップショットレベル
+
+実行例
+------
+
+動作に必要なスキーマ、テーブル、関数を ``testdb`` データベースにインストールします。
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb install
+ [2015-03-31 17:21:37] INFO: Succeeded to install pgperf snapshot.
+ $
+
+スナップショットレベル4でスナップショットを取得します。
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb create 4
+ [2015-03-31 17:21:47] INFO: Succeeded to take a snapshot.
+ $
+
+スナップショットの一覧を表示します。
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb list
+ +-----+---------------------+-------+
+ | SID | TIMESTAMP | LEVEL |
+ +-----+---------------------+-------+
+ | 0 | 2015-03-31 17:21:47 | 1 |
+ +-----+---------------------+-------+
+ $
+
+関連するスキーマ、テーブル、関数を ``testdb`` データベースからアンインストールします。
+
+.. code-block:: none
+
+ $ pt-stat-snapshot -h 127.0.0.1 -U postgres -d testdb uninstall
+ [2015-03-31 17:21:59] INFO: Succeeded to uninstall pgperf snapshot.
+ $
+
+pgperf-snapshotモジュールについて
+---------------------------------
+
+``pt-stat-snapshot`` コマンドは、その内部で旧 pgperf-snapshot モジュールを使っています。
+
+pgperf-snapshotモジュールについての詳細は、以下のドキュメントを参照してください。
+
+.. toctree::
+ :maxdepth: 2
+
+ pgperf_overview.rst
+ pgperf_intro.rst
+ pgperf_functions.rst
+ pgperf_tables.rst
diff --git a/docs/ja/pt-table-usage.rst b/docs/ja/pt-table-usage.rst
new file mode 100644
index 0000000..bb07fca
--- /dev/null
+++ b/docs/ja/pt-table-usage.rst
@@ -0,0 +1,90 @@
+
+pt-table-usage
+==============
+
+概要
+----
+
+テーブルの使用状況を表示します。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-table-usage [option...]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -o, --owner=STRING
+ -n, --schema=STRING
+ -t, --table=STRING
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+``-o``, ``--owner`` オプションは、指定した文字列に合致する名前のユーザが所有者となっているテーブルの情報のみを表示します。
+
+``-n``, ``--schema`` オプションは、指定した文字列に合致する名前のスキーマにあるテーブルの情報のみを表示します。
+
+``-t``, ``--table`` オプションは、指定した文字列に合致する名前のテーブルの情報のみを表示します。
+
+``-d`` (または ``--dbname`` ), ``-o`` (または ``--owner`` ), ``-n`` (または ``--schema`` ), ``-t`` (または ``--table`` ) を同時に指定した場合には、すべての条件に合致するテーブルのみが表示対象となります。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``OID``, テーブルのオブジェクトID
+ ``OWNER``, テーブルの所有者のユーザ名
+ ``SCHEMA``, テーブルの存在しているスキーマ名
+ ``TABLE``, テーブル名
+ ``BLKS``, テーブルのブロック数(8kB単位)
+ ``SCAN``, シーケンシャルスキャンの実行回数
+ ``T_READ``, シーケンシャルスキャンによって取得されたタプル数
+ ``T_INS``, 挿入されたタプル数
+ ``T_UPD``, 更新されたタプル数(HOT UPDATEを含む)
+ ``T_DEL``, 削除されたタプル数
+ ``B_READ``, ディスクから読み込まれたテーブルのブロック数
+ ``B_HIT``, 共有バッファから読み込まれたテーブルのページ数
+ ``VACUUMED``, 最後にVACUUMされた日時(VACUUMコマンドおよび自動VACUUMのいずれか)
+ ``ANALYZED``, 最後にANALYZEされた日時(ANALYZEコマンドおよび自動ANALYZEのいずれか)
+ ``TABLESPACE``, テーブルの配置されているテーブルスペース名。
+
+実行例
+------
+
+``localhost`` のポート ``5432`` で動作しているPostgreSQLインスタンスに接続し、``dbt3`` データベースの全テーブルの利用状況を表示します。
+
+.. code-block:: none
+
+ $ pt-table-usage -d dbt3
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ | OID | OWNER | SCHEMA | TABLE | BLKS | SCAN | T_READ | T_INS | T_UPD | T_DEL | B_READ | B_HIT | VACUUMED | ANALYZED | TABLESPACE |
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ | 1273410 | snaga | public | customer | 3531 | 5 | 750000 | 150000 | 0 | 0 | 6499 | 29943 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273416 | snaga | public | lineitem | 106583 | 12 | 66656465 | 6001215 | 0 | 0 | 240547 | 1340871 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273419 | snaga | public | nation | 1 | 4 | 100 | 25 | 0 | 0 | 1 | 5 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273413 | snaga | public | orders | 25326 | 5 | 7500000 | 1500000 | 0 | 0 | 48612 | 208386 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273404 | snaga | public | part | 4064 | 3 | 600000 | 200000 | 0 | 0 | 6082 | 26558 | | 2015-03-08 18:31:40 | ssdspc1 |
+ | 1273407 | snaga | public | partsupp | 17087 | 5 | 4000000 | 800000 | 0 | 0 | 32200 | 148518 | | 2015-03-08 18:31:41 | ssdspc1 |
+ | 1273422 | snaga | public | region | 1 | 3 | 15 | 5 | 0 | 0 | 1 | 4 | | 2015-03-08 18:31:42 | ssdspc1 |
+ | 1273401 | snaga | public | supplier | 218 | 4 | 40000 | 10000 | 0 | 0 | 220 | 1802 | | 2015-03-08 18:31:40 | ssdspc1 |
+ +---------+-------+--------+----------+--------+------+----------+---------+-------+-------+--------+---------+----------+---------------------+------------+
+ $
diff --git a/docs/ja/pt-tablespace-usage.rst b/docs/ja/pt-tablespace-usage.rst
new file mode 100644
index 0000000..e462f4e
--- /dev/null
+++ b/docs/ja/pt-tablespace-usage.rst
@@ -0,0 +1,65 @@
+
+pt-tablespace-usage
+===================
+
+概要
+----
+
+各データベースごとのテーブルスペースの使用状況を表示します。
+
+各テーブルスペースについて、どのデータベースがどれくらい容量を使っているかを確認することができます。
+
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-tablespace-usage [option...]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ --help
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``TABLESPACE``, テーブルスペース名
+ ``DBNAME``, データベース名(pg_globalテーブルスペースの場合は空欄)
+ ``SIZE (MB)``, テーブルスペース内でデータベースのオブジェクトが使っている容量(MB単位)
+
+実行例
+------
+
+ローカルホストのポート ``5432`` で動作するPostgreSQLインスタンスの ``postgres`` データベースに接続し、各テーブルスペースのうち、各データベースの使用している容量を表示します。
+
+.. code-block:: none
+
+ $ pt-tablespace-usage -d postgres
+ +------------+-----------+-----------+
+ | TABLESPACE | DBNAME | SIZE (MB) |
+ +------------+-----------+-----------+
+ | pg_default | postgres | 8 |
+ | pg_default | template1 | 6 |
+ | pg_default | testdb | 8 |
+ | pg_global | | 1 |
+ | spc1 | postgres | 16 |
+ +------------+-----------+-----------+
diff --git a/docs/ja/pt-verify-checksum.rst b/docs/ja/pt-verify-checksum.rst
new file mode 100644
index 0000000..4394fa6
--- /dev/null
+++ b/docs/ja/pt-verify-checksum.rst
@@ -0,0 +1,84 @@
+
+pt-verify-checksum
+==================
+
+概要
+----
+
+指定したPostgreSQLのファイルのチェックサムを検証します。
+
+チェックサムを有効にしてデータベースクラスタを作成している必要があります。 (``initdb`` コマンドの ``-k`` オプション)
+
+PostgreSQL 9.3以降のバージョンが対象です。
+
+このスクリプトは内部で ``verifychecksum`` コマンドを呼び出します。Red Hat Enterprise Linux 6/CentOS 6以外のOSで動作させる場合は、``src/verifychecksum.c`` を別途ビルドして ``bin`` ディレクトリ以下に配置する必要があります。
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-verify-checksum [option...] [segment file | directory]
+
+引数としてファイル名を指定した場合には、そのファイルのチェックサムを検証します。
+
+引数としてディレクトリ名を指定した場合には、そのディレクトリにあるファイルのうち、以下のファイルを対象としてチェックサムを検証します。
+
+* ``global`` または ``base`` ディレクトリ以下にあり、かつ、
+* ファイル名が数字で構成されるもの(例: ``1234``、``1234.1``)、および末尾に ``_vm`` または ``_fsm`` が付与されているファイル(例: ``1234_vm``、``1234_fsm``)
+
+チェックサム検証で1つ以上のファイルにエラーが見つかった場合には終了コード ``1`` を返します。それ以外のエラーが発生した場合には終了コード ``2`` を返します。どのファイルにも問題がなかった場合には ``0`` を返します。
+
+オプション
+----------
+
+.. code-block:: none
+
+ -r, --recursive
+ -v, --verbose
+ --help
+
+``-r``, ``--recursive`` オプションは、ディレクトリを指定した場合に、サブディレクトリ以下を再帰的にスキャンし、該当するファイルのチェックサムを検証します。
+
+``-v``, ``--verbose`` オプションは、チェックサム検証中により多くのメッセージを出力します。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``blkno``, チェックサムエラーの見つかったブロック番号
+ ``expected``, ブロックのデータから計算されたチェックサム
+ ``found``, ページヘッダに記録されていたチェックサム
+ ``Verified N files``, チェックサムの検証をしたファイル数
+ ``N files corrupted``, チェックサムのエラーの見つかったファイル数
+
+実行例
+------
+
+単一のファイルのチェックサムを検証します。
+
+.. code-block:: none
+
+ $ pt-verify-checksum /var/lib/pgsql/9.4/data/base/16386/16399
+ [2015-03-28 15:50:03] INFO: Verified 1 files. 0 files corrupted.
+ $
+
+データベース内のすべてのファイルのチェックサムを検証します。
+
+.. code-block:: none
+
+ $ pt-verify-checksum /var/lib/pgsql/9.4/data/base/16386
+ [2015-03-28 15:51:00] INFO: Verified 311 files. 0 files corrupted.
+ $
+
+データベースクラスタ内を再帰的に探索し、すべてのファイルのチェックサムを検証します。
+
+.. code-block:: none
+
+ $ pt-verify-checksum -r /var/lib/pgsql/9.4/data
+ [2015-03-28 15:55:16] INFO: /var/lib/pgsql/9.4/data/base/12144/11905: blkno 7, expected 2cf, found da97
+ [2015-03-28 15:55:16] INFO: 1 blocks corrupted in /var/lib/pgsql/9.4/data/base/12144/11905.
+ [2015-03-28 15:55:16] INFO: Verified 1046 files. 1 files corrupted.
+ $
diff --git a/docs/ja/pt-xact-stat.rst b/docs/ja/pt-xact-stat.rst
new file mode 100644
index 0000000..97aea22
--- /dev/null
+++ b/docs/ja/pt-xact-stat.rst
@@ -0,0 +1,97 @@
+
+pt-xact-stat
+============
+
+概要
+----
+
+複数ノードのトランザクションの統計状況を表示します。指定したインターバルごとに連続的に表示することも可能です。
+
+実行方法
+--------
+
+.. code-block:: none
+
+ pt-xact-stat [option...] [delay [count]]
+
+オプション
+----------
+
+.. code-block:: none
+
+ -h, --host=HOSTNAME
+ -p, --port=PORT
+ -H, --host-list=HOSTLIST
+ -U, --username=USERNAME
+ -d, --dbname=DBNAME
+ -H, --host-list=HOSTNAME:PORT,HOSTNAME:PORT[,...]
+
+``-h``, ``--host`` オプションは、接続するPostgreSQLデータベースのサーバ名またはIPアドレスを指定します。オプションが指定されない場合は、PGHOST環境変数に設定された値が使われます。PGHOST環境変数が設定されていない場合には、デフォルトの値として ``localhost`` が使われます。
+
+``-p``, ``--port`` オプションは、接続するPostgreSQLデータベースのポート番号を指定します。オプションが指定されない場合は、PGPORT環境変数に設定された値が使われます。PGPORT環境変数が設定されていない場合には、デフォルトの値として ``5432`` が使われます。
+
+``-H``, ``--host-list`` オプションは、接続するPostgreSQLサーバが複数ある場合にデータベースのサーバ名またはIPアドレスとポート番号の組み合わせを複数指定します。書式は ``192.168.1.101:5432,192.168.1.102:5433`` のように、一組のサーバ名とポート番号をコロン ``:`` で連結し、複数のサーバをカンマ ``,`` で連結します。ポート番号は省略可能で、省略した場合はデフォルトのポート番号が使われます。
+
+``-U``, ``--username`` オプションは、PostgreSQLデータベースに接続するユーザ名を指定します。オプションが指定されない場合は、PGUSER環境変数に設定された値が使われます。PGUSER環境変数が設定されていない場合には、USER環境変数に設定された値が使われます。
+
+``-d``, ``--dbname`` オプションは、接続するデータベース名を指定します。オプションが指定されない場合は、PGDATABASE環境変数に設定された値が使われます。PGDATABASE環境変数が設定されていない場合には、データベースに接続するユーザ名と同じ名前のデータベースに接続します。
+
+
+出力項目
+--------
+
+.. csv-table::
+
+ ``HOST``, PostgreSQLサーバのホスト名
+ ``PORT``, PostgreSQLサーバのポート番号
+ ``DBNAME``, データベース名
+ ``CONN``, データベースに接続しているセッション数
+ ``COMMITS``, コミットされたトランザクション総数
+ ``ROLLBACKS``, ロールバックされたトランザクション総数
+ ``B_READ``, ディスクから読み込まれたテーブルのブロック数
+ ``B_HIT``, 共有バッファから読み込まれたテーブルのページ数
+
+
+実行例
+------
+
+ローカルホストのポート5432とポート5433で動作している2つのPostgreSQLインスタンスに接続して、各インスタンスのトランザクションの統計情報を5秒おきに2回表示します。
+
+.. code-block:: none
+
+ $ pt-xact-stat --host-list 127.0.0.1:5432,127.0.0.1:5433,127.0.0.1:5434 -d postgres 5 2
+ Sat Mar 28 20:47:50 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 137 | 1 | 104 | 10273 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 8 | 0 | 104 | 1350 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 76 | 0 | 104 | 7708 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ Sat Mar 28 20:47:55 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 139 | 1 | 104 | 10460 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 10 | 0 | 104 | 1537 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 78 | 0 | 104 | 7895 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ $
+
+ローカルホストのポート5432, ポート5433, ポート5434で動作している3つのPostgreSQLインスタンスに接続して、各インスタンスのトランザクションの統計情報を1回表示して終了します。
+
+.. code-block:: none
+
+ $ pt-xact-stat --host-list 127.0.0.1:5432,127.0.0.1:5433,127.0.0.1:5434 -d postgres
+ Sat Mar 28 21:05:48 JST 2015
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | HOST | PORT | DBNAME | CONN | COMMITS | ROLLBACKS | B_READ | B_HIT |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+ | 127.0.0.1 | 5432 | postgres | 1 | 12 | 0 | 104 | 1400 |
+ | 127.0.0.1 | 5433 | postgres | 1 | 4 | 0 | 104 | 976 |
+ | 127.0.0.1 | 5434 | postgres | 1 | 4 | 0 | 104 | 976 |
+ +-----------+------+----------+------+---------+-----------+--------+-------+
+
+ $
diff --git a/docs/ja/releasenote.rst b/docs/ja/releasenote.rst
new file mode 100644
index 0000000..a9db4c8
--- /dev/null
+++ b/docs/ja/releasenote.rst
@@ -0,0 +1,21 @@
+
+リリースノート
+==============
+
+Version 0.2.2 (2015-08-08)
+--------------------------
+
+* pt-proc-statのエラーチェックの改善。
+* pt-configでクォートされた値を正しく読めるように修正。
+* その他、ドキュメントの警備な修正。
+
+バージョン0.2.1 (2015-05-31)
+----------------------------
+
+* Python 2.7がデフォルトのRed Hat Enterprise Linux 7、CentOS 7およびUbuntu 14.04 LTSをサポート対象として追加。
+* ヘルプメッセージの修正。 (pt-index-usage, pt-set-tablespace, pt-table-usage)
+
+バージョン0.2.0 (2015-05-09)
+----------------------------
+
+* 最初のリリースバージョン
diff --git a/install.sh.in b/install.sh.in
new file mode 100644
index 0000000..c9a758e
--- /dev/null
+++ b/install.sh.in
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# install.sh
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+VERSION="__VERSION__"
+
+cd /tmp
+
+curl -o postgres-toolkit-${VERSION}.tar.gz \
+ http://dl.uptimeforce.com/postgres-toolkit/files/postgres-toolkit-${VERSION}.tar.gz
+
+tar zxvf postgres-toolkit-${VERSION}.tar.gz -C /
+
+cat<<EOF
+
+Succeeded to install Postgres Toolkit!
+
+Subscribe now to stay informed of the latest Postgres Toolkit news.
+ - http://postgres-toolkit.launchrock.com/
+
+The official manual is available online.
+ - https://postgres-toolkit.readthedocs.org/
+ - https://postgres-toolkit-ja.readthedocs.org/
+
+Thanks for choosing Postgres Toolkit! Enjoy!
+
+EOF
diff --git a/installcheck.expected b/installcheck.expected
new file mode 100644
index 0000000..4bade5b
--- /dev/null
+++ b/installcheck.expected
@@ -0,0 +1,169 @@
+
+Usage: pt-config [option...] [command] [param [value]]
+
+Commands:
+ get [PARAM] Get a current value of a parameter.
+ set [PARAM] [VALUE] Set a new value for a parameter.
+ disable [PARAM] Comment a parameter out.
+
+Options:
+ -D, --pgdata=PGDATA Specify a PostgreSQL database cluster.
+ --apply Apply change(s).
+
+ --help Print this help.
+
+
+Usage: pt-index-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+ -i, --index=STRING Index name
+
+ --help Print this help.
+
+
+Usage: pt-kill [option...] [command] [pid]
+
+Commands:
+ cancel Cancel a running query.
+ terminate Terminate a backend with canceling query.
+
+Options:
+ --help Print this help.
+
+
+Usage: pt-proc-stat [option...] [delay [count]]
+
+Options:
+ -D, --pgdata=DATADIR Location of the database storage area
+ -P, --pid=PID Process ID of the postmaster
+
+ --help Print this help.
+
+
+Usage: pt-replication-stat [option...] [delay [count]]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+
+Usage: pt-session-profiler [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -i STRING Interface name to listen
+ -T, --threshold=NUMBER Threashold of query elapsed time (in millisecond)
+
+ --help Print this help.
+
+
+Usage: pt-set-tablespace [option...] [tablespace]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+
+ -l, --list List table spaces
+ --apply Apply change(s)
+
+ --help Print this help.
+
+
+Usage: pt-snap-statements [option...] [interval]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -t, --top=NUMBER Number of queries to be listed
+ -R, --reset Reset statistics
+
+ --help Print this help.
+
+
+Usage: pt-stat-snapshot [option...] install
+ pt-stat-snapshot [option...] create [level]
+ pt-stat-snapshot [option...] list
+ pt-stat-snapshot [option...] delete [sid]
+ pt-stat-snapshot [option...] export [file]
+ pt-stat-snapshot [option...] import [file]
+ pt-stat-snapshot [option...] uninstall
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+
+Usage: pt-table-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ -o, --owner=STRING Owner name
+ -n, --schema=STRING Schema name
+ -t, --table=STRING Table name
+
+ --help Print this help.
+
+
+Usage: pt-tablespace-usage [option...]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
+
+Usage: pt-verify-checksum [option...] [file]
+ pt-verify-checksum [option...] [directory]
+
+Options:
+ -r, --recursive Find files recursively.
+ -v, --verbose Enable verbose output.
+
+ --help Print this help.
+
+
+Usage: pt-xact-stat [option...] [delay [count]]
+
+Options:
+ -h, --host=HOSTNAME Host name of the postgres server
+ -p, --port=PORT Port number of the postgres server
+ -H, --host-list=HOSTLIST List of pairs of hostname and port number
+ (c.f. host1:port1,host2:port2)
+ -U, --username=USERNAME User name to connect
+ -d, --dbname=DBNAME Database name to connect
+
+ --help Print this help.
+
diff --git a/lib/DirectoryTree.py b/lib/DirectoryTree.py
new file mode 100644
index 0000000..e16fd6a
--- /dev/null
+++ b/lib/DirectoryTree.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# DirectoryTree
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import os
+from stat import *
+
+import log
+
+class DirectoryTree():
+ path = None
+ filelist = []
+
+ def __init__(self, path, recursive=False, debug=False):
+ self.path = path
+ self.recursive = recursive
+ self.filelist = []
+
+ if debug is True:
+ log.setLevel(log.DEBUG)
+
+ log.debug("path: %s" % (self.path))
+ log.debug("recursive: %s" % (str(self.recursive)))
+
+ def get_file_list(self):
+ mode = os.stat(self.path)[ST_MODE]
+
+ if S_ISDIR(mode):
+ log.debug("%s is directory." % self.path)
+ if self.recursive is True:
+ log.debug(" going to recursive")
+ self.walktree(self.path, self.visitfile)
+ else:
+ for f in os.listdir(self.path):
+ log.debug(" found %s" % f)
+ pathname = os.path.join(self.path, f)
+ mode = os.stat(pathname)[ST_MODE]
+
+ # found a regular file.
+ if S_ISREG(mode):
+ log.debug(" %s is a regular file" % f)
+ self.visitfile(pathname, f)
+
+ elif S_ISREG(mode):
+ self.filelist.append(self.path)
+
+ return self.filelist
+
+ def walktree(self, directory, callback):
+ log.debug("walktree: %s" % directory)
+ for f in os.listdir(directory):
+ pathname = os.path.join(directory, f)
+ mode = os.stat(pathname)[ST_MODE]
+ if S_ISDIR(mode):
+ self.walktree(pathname, callback)
+ elif S_ISREG(mode):
+ callback(pathname, f)
+
+ def visitfile(self, filepath, filename):
+ # filepath must be a regular file.
+ log.debug("append %s to the list" % filepath)
+ self.filelist.append(filepath)
+
+ # Is the file in 'base' or 'global' directories?
+# if re.search('/base/', filepath) is None and re.search('/global/', filepath) is None:
+# return
+#
+# if re.search('^\d+$', filename) is not None:
+# self.filelist.append(filepath)
+# elif re.search('^\d+\.\d+$', filename) is not None:
+# self.filelist.append(filepath)
+# elif re.search('^\d+_fsm$', filename) is not None:
+# self.filelist.append(filepath)
+# elif re.search('^\d+_vm$', filename) is not None:
+# self.filelist.append(filepath)
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..35513b0
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,21 @@
+include ../Makefile.global
+
+all:
+
+check:
+ ./testDirectoryTree.py
+ ./testLog.py
+ ./testPsqlWrapper.py
+ ./testTcpdumpWrapper.py
+
+install:
+ mkdir -p $(PREFIX)/lib
+ install -m 644 PsqlWrapper.py $(PREFIX)/lib
+ install -m 644 log.py $(PREFIX)/lib
+ install -m 644 DirectoryTree.py $(PREFIX)/lib
+ install -m 644 TcpdumpWrapper.py $(PREFIX)/lib
+
+clean:
+ rm -f *~ *.pyc
+ rm -rf t1
+
diff --git a/lib/PsqlWrapper.py b/lib/PsqlWrapper.py
new file mode 100644
index 0000000..77e8b7d
--- /dev/null
+++ b/lib/PsqlWrapper.py
@@ -0,0 +1,176 @@
+#!/bin/env python
+# coding: UTF-8
+
+# PsqlWrapper
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import log
+import os
+import re
+import subprocess
+import sys
+
+def get_host(host=None):
+ if host is None:
+ if os.environ.get("PGHOST") is not None:
+ h = os.environ.get("PGHOST")
+ else:
+ h = 'localhost'
+ else:
+ h = host
+
+ return h
+
+def get_port(port=None):
+ if port is None:
+ if os.environ.get("PGPORT") is not None:
+ p = int(os.environ.get("PGPORT"))
+ else:
+ p = 5432
+ else:
+ p = int(port)
+
+ return p
+
+def get_username(username=None):
+ if username is None:
+ if os.environ.get("PGUSER") is not None:
+ u = os.environ.get("PGUSER")
+ else:
+ u = os.environ.get("USER")
+ else:
+ u = username
+
+ return u
+
+def get_dbname(dbname=None, username=None):
+ if dbname is None:
+ if os.environ.get("PGDATABASE") is not None:
+ d = os.environ.get("PGDATABASE")
+ else:
+ d = username
+ else:
+ d = dbname
+
+ return d
+
+class PsqlWrapper:
+ version = None
+
+ def __init__(self, host, port, username, dbname, on_error_stop=False, debug=False):
+ if debug is True:
+ log.setLevel(log.DEBUG)
+
+ self.host = get_host(host)
+ self.port = get_port(port)
+ self.username = get_username(username)
+ self.dbname = get_dbname(dbname, self.username)
+
+ log.debug("host: " + self.host)
+ log.debug("port: " + str(self.port))
+ log.debug("user: " + self.username)
+ log.debug("dbname: " + self.dbname)
+
+ self.on_error_stop = on_error_stop
+
+ def get_version(self):
+ if self.version is None:
+ rs = self.execute_query('select version()')
+# print(rs[1])
+ m = re.search('PostgreSQL (\d+\.\d+)', rs[1][0])
+
+ self.version = float(m.group(1))
+
+ return self.version
+
+ def execute_query(self, query, ignore_error=False):
+ cmd = "psql -A"
+ cmd = cmd + " -h " + self.host
+ cmd = cmd + " -p " + str(self.port)
+ cmd = cmd + " -U " + self.username
+ cmd = cmd + " -d " + self.dbname
+ if self.on_error_stop is True:
+ cmd = cmd + " --set=ON_ERROR_STOP=on "
+
+ log.debug(cmd)
+
+ p = subprocess.Popen(cmd, shell=True,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout_data, stderr_data = p.communicate(input=query)
+ p.returncode, stdout_data, stderr_data
+
+ self.stderr_data = stderr_data
+
+ if p.returncode != 0:
+ if ignore_error is True:
+ log.debug("return code: " + str(p.returncode))
+ log.debug(stderr_data)
+ return None
+ else:
+ log.debug("return code: " + str(p.returncode))
+ log.error("Failed to execute psql. (" + cmd + ")")
+ if stderr_data is not None:
+ log.error(stderr_data.replace('\n',''))
+ sys.exit(1)
+
+ rs = []
+ lines = re.split('\n', stdout_data)[:-1]
+ for line in lines:
+ rs.append(re.split('\|', line))
+
+ return rs
+
+ def print_result(self, rs):
+ cols = len(rs[0])
+ size = []
+
+ header = True
+ for r in rs:
+ if len(r) != cols:
+ continue
+
+ i = 0
+ for c in r:
+ if header is True:
+ size.append(len(c))
+ elif len(c) > size[i]:
+ size[i] = len(c)
+
+# print '[' + str(i) + ']' + c
+ i = i + 1
+ header = False
+
+# print size
+
+ header = True
+ sep2 = None
+ for r in rs:
+ if len(r) != cols:
+ continue
+
+ i = 0
+ out = '|'
+ sep = '+'
+ for c in r:
+ if header is True:
+ out = out + ' ' + str(c).center(size[i]) + ' |'
+ sep = sep + '-' + '-' * size[i] + '-+'
+ elif re.match('^\d+$', str(c)):
+ out = out + ' ' + str(c).rjust(size[i]) + ' |'
+ else:
+ out = out + ' ' + str(c).ljust(size[i]) + ' |'
+ i = i + 1
+
+ if header is True:
+ sep2 = sep
+ print sep2
+
+ print out
+
+ if header is True:
+ print sep2
+ header = False
+
+ print sep2
diff --git a/lib/TcpdumpWrapper.py b/lib/TcpdumpWrapper.py
new file mode 100644
index 0000000..754646c
--- /dev/null
+++ b/lib/TcpdumpWrapper.py
@@ -0,0 +1,399 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# TcpdumpWrapper
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+from datetime import datetime, timedelta, date, time
+import hashlib
+import os
+import re
+import subprocess
+import sys
+
+import log
+
+class TcpdumpPacket:
+ def __init__(self, ts, src, dst, bytes, debug=None):
+ self.ts = self.string2timestamp(ts)
+ self.src = src
+ self.dst = dst
+ self.debug = debug
+
+ self.bytes = bytes
+
+ self.messages = []
+
+ log.debug("+ " + ts + " " + src + " " + dst)
+
+ self.end()
+
+ self.session_id = self.get_session_id()
+
+ def string2timestamp(self, ts):
+ t = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
+ log.debug("ts = " + str(t))
+ return t
+
+ def get_session_id(self):
+ src_dst = [ self.iph_src + ":" + str(self.tcph_src), self.iph_dst + ":" + str(self.tcph_dst) ]
+ ss = ""
+ for s in sorted(src_dst):
+ ss = ss + s + " "
+
+ return hashlib.md5(ss).hexdigest()[0:12]
+
+ def get_timestamp(self):
+ log.debug("get_timestamp: %s" % str(self.ts))
+ return self.ts
+
+ def get_messages(self):
+ return self.messages
+
+ def parse_ip_header(self, data):
+ self.iph_version = (data[0] >> 4) & 0b1111
+ self.iph_header_len = data[0] & 0b1111
+ self.iph_tos = data[1]
+ self.iph_dgram_len = (data[2] <<8) + data[3]
+ self.iph_id = (data[4] <<8) + data[5]
+ self.iph_dst = "%d.%d.%d.%d" % (data[12], data[13], data[14], data[15])
+ self.iph_src = "%d.%d.%d.%d" % (data[16], data[17], data[18], data[19])
+
+ if self.debug is True:
+ print("version : %d" % self.iph_version)
+ print("hd len : %d (%d)" % (self.iph_header_len, self.iph_header_len * 4))
+ print("tos : %d" % self.iph_tos)
+ print("dgram len: %d" % self.iph_dgram_len)
+ print("data len: %d" % (self.iph_dgram_len - self.iph_header_len*4))
+ print("id : %d" % self.iph_id)
+ print("dst : %s" % (self.iph_dst))
+ print("src : %s" % (self.iph_src))
+
+ return self.iph_header_len * 4
+
+ def parse_tcp_header(self, data):
+ self.tcph_src = (data[0] << 8) + data[1]
+ self.tcph_dst = (data[2] << 8) + data[3]
+ self.tcph_seq = (data[4] << 24) + (data[5] << 16) + (data[6] << 8) + data[7]
+ self.tcph_offset = (data[12] >> 4) & 0b1111
+
+ if self.debug is True:
+ print("src port : %d" % (self.tcph_src))
+ print("dst port : %d" % (self.tcph_dst))
+ print("seq : %d" % (self.tcph_seq))
+ print("offset : %d (%d)" % (self.tcph_offset, self.tcph_offset * 4))
+
+ return self.tcph_offset * 4
+
+ def end(self):
+ cur = 0
+ iph_len = self.parse_ip_header(self.bytes[cur:])
+ cur = cur + iph_len
+
+ tcph_len = self.parse_tcp_header(self.bytes[cur:])
+ cur = cur + tcph_len
+
+ self.payload = self.bytes[cur:]
+
+ s = ""
+ for d in self.payload:
+ s = s + "%02x " % (d)
+ log.debug("payload: " + s)
+
+ if len(self.payload) >= 5:
+ pos = 0
+ cont = True
+ while cont:
+ if len(self.payload[pos:]) < 5:
+ cont = False
+ break
+
+ ch = self.read_char(self.payload[pos:])
+# if not(ch >= 48 and ch <= 122):
+# break
+
+ pos = pos + 1
+
+ i = self.read_int32(self.payload[pos:])
+ pos = pos + 4
+
+ log.debug("sess: " + self.get_session_id() + ": " + str(self.ts) + ": %c[%x] len=%d" % (ch, ch, i))
+
+ # client to server
+ if ch == ord('S'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ self.messages.append( [chr(ch), s] )
+ log.debug(s)
+
+ elif ch == ord('Q'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ log.debug(s)
+ self.messages.append( [chr(ch), s] )
+
+ elif ch == ord('P'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ s1 = s.split('\0')
+ log.debug("> " + s1[0] + "," + s1[1])
+ self.messages.append( [chr(ch), s1[0], s1[1]] )
+
+ elif ch == ord('E'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ self.messages.append( [chr(ch), s] )
+
+ elif ch == ord('B'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ s1 = s.split('\0')
+ log.debug("> " + s1[0] + "," + s1[1])
+ self.messages.append( [chr(ch), s1[0], s1[1]] )
+
+ elif ch == ord('X'):
+ self.messages.append( [chr(ch), None] )
+ cont = False
+
+ # server to client
+ elif ch == ord('T'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ self.messages.append( [chr(ch), s] )
+ log.debug(s)
+
+ elif ch == ord('D'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ self.messages.append( [chr(ch), s] )
+ log.debug(s)
+
+ elif ch == ord('C'):
+ s = self.read_string(self.payload[pos:], i - 4)
+ self.messages.append( [chr(ch), s] )
+ log.debug(s)
+
+ elif ch == ord('1'):
+ self.messages.append( [chr(ch), None] )
+
+ elif ch == ord('2'):
+ self.messages.append( [chr(ch), None] )
+
+ elif ch == ord('n'):
+ self.messages.append( [chr(ch), None] )
+
+ elif ch == ord('Z'):
+ self.messages.append( [chr(ch), None] )
+ cont = False
+
+ pos = pos + (i - 4)
+
+ def parse(self):
+ self.pos = 12
+ while len(self.payload) > self.pos + 5:
+ c = self.read_char()
+ log.debug("%02x(%c)" % (c, c))
+ l = self.read_int32()
+ log.debug(l)
+ self.pos = self.pos + l
+
+ def read_char(self, data):
+ ch = data[0]
+ return ch
+
+ def read_int32(self, data):
+ i = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + (data[3])
+ return i
+
+ def read_string(self, data, size):
+ s = ""
+ i = 0
+ while i < size:
+ s = s + "%c" % data[i]
+ i = i + 1
+ return s
+
+class TcpdumpWrapper:
+ pkt = None
+ tcpdump = None
+ process = None
+ line = None
+
+ def __init__(self, host=None, port=None, interface=None, inputfile=None, debug=None):
+ if debug is True:
+ log.setLevel(log.DEBUG)
+
+ self.host = host
+ self.port = port
+ self.iface = interface
+ self.inputfile = inputfile
+ self.debug = debug
+
+ if self.port is None:
+ self.port = "5432"
+
+ if self.iface is None:
+ self.iface = "any"
+
+ self.tcpdump = "tcpdump -tttt"
+
+ if self.inputfile is not None:
+ self.tcpdump = self.tcpdump + " -r " + self.inputfile
+
+ self.tcpdump = self.tcpdump + " -l -i " + self.iface + " -s 0 -X -p tcp port " + str(self.port)
+
+ if self.host is not None:
+ self.tcpdump = self.tcpdump + " and host " + self.host
+
+ log.info(self.tcpdump)
+
+ self.process = subprocess.Popen([self.tcpdump],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=True,
+ bufsize=0)
+
+ # header
+ self.p1 = re.compile('^([\d-]+) ([\d:\.]+) IP (.*) > (.*): Flags')
+ # data
+ self.p2 = re.compile('^\t+0x(.+): (.+) ')
+
+ def get_packet(self):
+ self.data = ""
+ self.hdr = None
+
+ while True:
+ if self.line is None:
+ self.line = self.readline()
+
+ if self.line is None:
+ if self.hdr is not None and self.data is not None:
+ # EOF found.
+ pkt = TcpdumpPacket(self.hdr[0], self.hdr[1], self.hdr[2], self.parse_data(self.data), self.debug)
+
+ self.data = ""
+ self.hdr = None
+
+ return pkt
+ else:
+ return None
+
+ if self.line[0] != '\t':
+ if len(self.data) > 0:
+ pkt = TcpdumpPacket(self.hdr[0], self.hdr[1], self.hdr[2], self.parse_data(self.data), self.debug)
+ self.data = ""
+ self.hdr = None
+ return pkt
+
+ self.hdr = self.parse_header(self.line)
+ # d: ts, src, dest
+ log.debug("Header: ts=" + self.hdr[0] + ", src=" + self.hdr[1] + ", dest=" + self.hdr[2])
+
+ else:
+ #log.debug(">" + self.line[10:50] + "<")
+ self.data = self.data + self.line[10:50]
+
+ self.line = None
+
+ def get_last_packet(self):
+ pkt = None
+
+ if self.hdr is not None and self.data is not None:
+ pkt = TcpdumpPacket(self.hdr[0], self.hdr[1], self.hdr[2], self.parse_data(self.data), self.debug)
+
+ self.data = ""
+ self.hdr = None
+
+ return pkt
+
+ def readline(self):
+ self.process.stdout.flush()
+ line = self.process.stdout.readline()
+
+ if len(line) == 0:
+ # EOF
+ self.process.poll()
+ if self.process.returncode != 0:
+ log.error("%s" % self.process.stderr.readline())
+ sys.exit(1)
+
+ log.debug("readline: EOF")
+ return None
+
+ return line.rstrip()
+
+ def parse_header(self, line):
+ r1 = self.p1.match(line)
+
+ if r1 is not None:
+ # Header line may look like this:
+ # 2015-04-30 13:33:27.579311 IP localhost.55641 > localhost.postgres: Flags [.], ack 66, win 664, options [nop,nop,TS val 265008484 ecr 265008484], length 0
+ ts = r1.group(1) + " " + r1.group(2)
+ src = r1.group(3)
+ dest = r1.group(4)
+
+ return [ts, src, dest]
+
+ return None
+
+ def parse_data(self, line):
+ bytes = []
+ # line:
+ # 4500 0039 e080 4000 4006 5c3c 7f00 0001
+ offset = 0
+ length = len(line)
+
+ log.debug("! " + line)
+ cur = 0
+ while cur < length:
+ if line[cur] == ' ':
+ cur = cur + 1
+ continue
+ # chr to hex
+ h = int(line[cur:cur+2], 16)
+ bytes.append(h)
+# print(payload[cur:cur+2] + ", " + str(h))
+ cur = cur + 2
+
+ return bytes
+
+ def parse(self, line):
+ # return true when messages avalable.
+ msg_avail = False
+
+ log.debug("parse: " + line)
+
+ # packet header info
+ r1 = self.p1.match(line)
+ if r1 is not None:
+ # Header line may look like this:
+ # 13:33:27.579311 IP localhost.55641 > localhost.postgres: Flags [.], ack 66, win 664, options [nop,nop,TS val 265008484 ecr 265008484], length 0
+ log.debug("Header: " + line)
+ ts = r1.group(1)
+ src = r1.group(2)
+ dest = r1.group(3)
+
+ # close the previous packet
+ if self.pkt is not None:
+ self.pkt.end()
+
+ # retreive all info/messages in the previous packet.
+ self.session_id = self.pkt.get_session_id()
+ self.timestamp = self.pkt.get_timestamp()
+ self.messages = copy.deepcopy(self.pkt.get_messages())
+ msg_avail = True
+
+ for m in self.pkt.get_messages():
+ log.debug("ts:%s cmd:%c msg:%s" % (str(self.pkt.get_timestamp()), m[0], m[1]))
+
+ # new packet coming
+ self.pkt = TcpdumpPacket(ts, src, dest, self.debug)
+ self.packets.append(self.pkt)
+
+ # packet bytes
+ r2 = self.p2.match(line)
+ if r2 is not None:
+ log.debug("matched r2: " + line)
+
+ if self.pkt is not None:
+ log.debug("append_bytes: " + line)
+ self.pkt.append_bytes(r2.group(2))
+
+ return msg_avail
+
+ def print_packets(self):
+ print(self.packets)
diff --git a/lib/log.py b/lib/log.py
new file mode 100644
index 0000000..727d25a
--- /dev/null
+++ b/lib/log.py
@@ -0,0 +1,31 @@
+#!/bin/env python
+# coding: UTF-8
+
+# log
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import logging
+
+logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s',
+ datefmt="%Y-%m-%d %H:%M:%S")
+logger = logging.getLogger('PsqlWrapper')
+logger.setLevel(logging.INFO)
+
+INFO = logging.INFO
+DEBUG = logging.DEBUG
+
+def setLevel(level):
+ logger.setLevel(level)
+
+def debug(msg):
+ logger.debug(msg)
+
+def error(msg):
+ logger.error(msg)
+
+def warning(msg):
+ logger.warning(msg)
+
+def info(msg):
+ logger.info(msg)
diff --git a/lib/test.cap b/lib/test.cap
new file mode 100644
index 0000000..d285754
--- /dev/null
+++ b/lib/test.cap
Binary files differ
diff --git a/lib/testDirectoryTree.py b/lib/testDirectoryTree.py
new file mode 100755
index 0000000..0d9d074
--- /dev/null
+++ b/lib/testDirectoryTree.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# testDirectoryTree
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import unittest
+import os
+import DirectoryTree
+
+class TestDirectoryTree(unittest.TestCase):
+ def setUp(self):
+ os.system("rm -rf t1")
+ os.system("mkdir -p t1/t2/t3")
+ os.system("touch t1/a t1/t2/b t1/t2/t3/c")
+
+ def testDirectoryTree001(self):
+ d = DirectoryTree.DirectoryTree('t1', debug=False)
+
+ l = d.get_file_list()
+# print(l)
+
+ self.assertTrue(d is not None)
+ self.assertTrue(len(l) == 1)
+ self.assertEqual(l[0], 't1/a')
+
+ def testDirectoryTree002(self):
+ d = DirectoryTree.DirectoryTree('t1', recursive=True, debug=False)
+
+ l = d.get_file_list()
+# print(sorted(l))
+
+ self.assertTrue(d is not None)
+ self.assertTrue(len(l) == 3)
+ self.assertEqual(sorted(l)[0], 't1/a')
+ self.assertEqual(sorted(l)[1], 't1/t2/b')
+ self.assertEqual(sorted(l)[2], 't1/t2/t3/c')
+
+ def testDirectoryTree003(self):
+ d = DirectoryTree.DirectoryTree('t1/a', recursive=False, debug=True)
+
+ l = d.get_file_list()
+# print(sorted(l))
+
+ self.assertTrue(d is not None)
+ self.assertTrue(len(l) == 1)
+ self.assertEqual(sorted(l)[0], 't1/a')
+
+ def _tearDown(self):
+ print("teardown")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/testLog.py b/lib/testLog.py
new file mode 100755
index 0000000..bc347e1
--- /dev/null
+++ b/lib/testLog.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# testLog
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import unittest
+import os
+import log
+
+class TestLog(unittest.TestCase):
+ def _setUp(self):
+ print("setup")
+
+ def testSetLevel001(self):
+ log.setLevel(log.INFO)
+
+ def testDebug001(self):
+ log.debug("debug")
+
+ def testError001(self):
+ log.error("error")
+
+ def _tearDown(self):
+ print("teardown")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/testPsqlWrapper.py b/lib/testPsqlWrapper.py
new file mode 100755
index 0000000..675bb98
--- /dev/null
+++ b/lib/testPsqlWrapper.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# testPsqlWrapper
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import unittest
+import os
+import PsqlWrapper
+
+class TestPsqlWrapper(unittest.TestCase):
+ def _setUp(self):
+ print("setup")
+
+ def testPsqlWrapper001(self):
+ p = PsqlWrapper.PsqlWrapper(None, None, None, None)
+
+ self.assertTrue(p is not None)
+ self.assertEqual(p.host, 'localhost')
+ self.assertEqual(p.port, 5432)
+ self.assertEqual(p.username, os.environ['USER'])
+ self.assertEqual(p.dbname, p.username)
+
+ def testPsqlWrapper002(self):
+ p = PsqlWrapper.PsqlWrapper('localhost', '5432', 'postgres', 'postgres')
+
+ self.assertTrue(p is not None)
+ self.assertEqual(p.host, 'localhost')
+ self.assertEqual(p.port, 5432)
+ self.assertEqual(p.username, 'postgres')
+ self.assertEqual(p.dbname, 'postgres')
+
+ def testPsqlWrapper003(self):
+ os.environ['PGHOST'] = '127.0.0.1'
+ os.environ['PGPORT'] = '5433'
+ os.environ['PGUSER'] = 'username'
+ os.environ['PGDATABASE'] = 'dbname'
+
+ p = PsqlWrapper.PsqlWrapper(None, None, None, None)
+
+ self.assertTrue(p is not None)
+ self.assertEqual(p.host, '127.0.0.1')
+ self.assertEqual(p.port, 5433)
+ self.assertEqual(p.username, 'username')
+ self.assertEqual(p.dbname, 'dbname')
+
+ def testPsqlWrapper004(self):
+ os.environ['PGHOST'] = '127.0.0.1'
+ os.environ['PGPORT'] = '5433'
+ os.environ['PGUSER'] = 'username'
+ os.environ['PGDATABASE'] = 'dbname'
+
+ p = PsqlWrapper.PsqlWrapper('localhost', '5432', 'postgres', 'postgres')
+
+ self.assertTrue(p is not None)
+ self.assertEqual(p.host, 'localhost')
+ self.assertEqual(p.port, 5432)
+ self.assertEqual(p.username, 'postgres')
+ self.assertEqual(p.dbname, 'postgres')
+
+ def _tearDown(self):
+ print("teardown")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/testTcpdumpWrapper.py b/lib/testTcpdumpWrapper.py
new file mode 100755
index 0000000..900c583
--- /dev/null
+++ b/lib/testTcpdumpWrapper.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# coding: UTF-8
+
+# testTcpdumpWrapper
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+
+import unittest
+import os
+
+import TcpdumpWrapper
+import log
+
+class TestTcpdmpWrapper(unittest.TestCase):
+
+ def testTcpdumpWrapper001(self):
+ dump = TcpdumpWrapper.TcpdumpWrapper('127.0.0.1', '5432', 'lo', 'test.cap')
+ self.assertTrue(dump is not None)
+
+ dump = TcpdumpWrapper.TcpdumpWrapper(None, '5432', 'lo', 'test.cap')
+ self.assertTrue(dump is not None)
+
+ dump = TcpdumpWrapper.TcpdumpWrapper(None, None, 'lo', 'test.cap')
+ self.assertTrue(dump is not None)
+
+ dump = TcpdumpWrapper.TcpdumpWrapper(None, None, None, 'test.cap')
+ self.assertTrue(dump is not None)
+
+ def testTcpdumpWrapper002(self):
+ dump = TcpdumpWrapper.TcpdumpWrapper('127.0.0.1', '5432', 'lo', 'test.cap', debug=False)
+ self.assertTrue(dump is not None)
+
+ p = dump.get_packet()
+
+ self.assertTrue(str(p.ts) == '2015-04-30 21:48:12.366446')
+ self.assertTrue(p.src == 'localhost.55060')
+ self.assertTrue(p.dst == 'localhost.postgres' or p.dst == 'localhost.postgresql')
+
+ def testTcpdumpWrapper003(self):
+ dump = TcpdumpWrapper.TcpdumpWrapper('127.0.0.1', '5432', 'lo', 'test.cap', debug=False)
+ self.assertTrue(dump is not None)
+
+ prev = None
+ while True:
+ p = dump.get_packet()
+ if p is None:
+ break
+ log.debug(p)
+ prev = p
+
+ self.assertTrue(str(prev.ts) == '2015-04-30 21:48:12.413238')
+ self.assertTrue(prev.src == 'localhost.55061')
+ self.assertTrue(prev.dst == 'localhost.postgres' or prev.dst == 'localhost.postgresql')
+
+ def testTcpdumpWrapper004(self):
+ dump = TcpdumpWrapper.TcpdumpWrapper('127.0.0.1', '5432', 'lo', 'test.cap', debug=False)
+ self.assertTrue(dump is not None)
+
+ p = dump.get_packet()
+
+ self.assertTrue(p.get_session_id() == '74fbc116e01b')
+ self.assertTrue(str(p.get_timestamp()) == '2015-04-30 21:48:12.366446')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..f971a10
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,20 @@
+TOPDIR=..
+
+include $(TOPDIR)/Makefile.global
+
+all:
+ make -C verifychecksum all
+ make -C pgperf all
+
+check:
+ make -C pgperf check
+
+install:
+ make -C verifychecksum install
+ make -C pgperf install
+
+clean:
+ make -C verifychecksum clean
+ make -C pgperf clean
+
+
diff --git a/src/pgperf/90/pg_current_xlog.sql b/src/pgperf/90/pg_current_xlog.sql
new file mode 100644
index 0000000..11ff12f
--- /dev/null
+++ b/src/pgperf/90/pg_current_xlog.sql
@@ -0,0 +1,3 @@
+SELECT 0::int as sid,
+ pg_current_xlog_location() AS location,
+ pg_current_xlog_insert_location() AS insert_location;
diff --git a/src/pgperf/90/pg_database_size.sql b/src/pgperf/90/pg_database_size.sql
new file mode 100644
index 0000000..d0798c0
--- /dev/null
+++ b/src/pgperf/90/pg_database_size.sql
@@ -0,0 +1,5 @@
+SELECT 0::int as sid,
+ datname,
+ pg_database_size(datname)
+ FROM pg_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/90/pg_locks.sql b/src/pgperf/90/pg_locks.sql
new file mode 100644
index 0000000..822b0ce
--- /dev/null
+++ b/src/pgperf/90/pg_locks.sql
@@ -0,0 +1,16 @@
+SELECT 0::int as sid,
+ locktype,
+ database,
+ relation,
+ page,
+ tuple,
+ virtualxid,
+ transactionid,
+ classid,
+ objid,
+ objsubid,
+ virtualtransaction,
+ pid,
+ mode,
+ granted
+ FROM pg_locks;
diff --git a/src/pgperf/90/pg_relation_size.sql b/src/pgperf/90/pg_relation_size.sql
new file mode 100644
index 0000000..8aee182
--- /dev/null
+++ b/src/pgperf/90/pg_relation_size.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ schemaname,
+ relid,
+ relname,
+ pg_relation_size(relid),
+ pg_total_relation_size(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_stat_activity.sql b/src/pgperf/90/pg_stat_activity.sql
new file mode 100644
index 0000000..c305dd1
--- /dev/null
+++ b/src/pgperf/90/pg_stat_activity.sql
@@ -0,0 +1,15 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ procpid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_port,
+ backend_start,
+ xact_start,
+ query_start,
+ waiting,
+ current_query
+ FROM pg_stat_activity;
diff --git a/src/pgperf/90/pg_stat_bgwriter.sql b/src/pgperf/90/pg_stat_bgwriter.sql
new file mode 100644
index 0000000..aa9c636
--- /dev/null
+++ b/src/pgperf/90/pg_stat_bgwriter.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ checkpoints_timed,
+ checkpoints_req,
+ buffers_checkpoint,
+ buffers_clean,
+ maxwritten_clean,
+ buffers_backend,
+ buffers_alloc
+ FROM pg_stat_bgwriter;
diff --git a/src/pgperf/90/pg_stat_database.sql b/src/pgperf/90/pg_stat_database.sql
new file mode 100644
index 0000000..3175b93
--- /dev/null
+++ b/src/pgperf/90/pg_stat_database.sql
@@ -0,0 +1,15 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ numbackends,
+ xact_commit,
+ xact_rollback,
+ blks_read,
+ blks_hit,
+ tup_returned,
+ tup_fetched,
+ tup_inserted,
+ tup_updated,
+ tup_deleted
+ FROM pg_stat_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/90/pg_stat_statements.sql b/src/pgperf/90/pg_stat_statements.sql
new file mode 100644
index 0000000..a457fd5
--- /dev/null
+++ b/src/pgperf/90/pg_stat_statements.sql
@@ -0,0 +1,16 @@
+SELECT 0::int as sid,
+ userid,
+ dbid,
+ query,
+ calls,
+ total_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written
+ FROM pg_stat_statements;
diff --git a/src/pgperf/90/pg_stat_user_functions.sql b/src/pgperf/90/pg_stat_user_functions.sql
new file mode 100644
index 0000000..9b137b4
--- /dev/null
+++ b/src/pgperf/90/pg_stat_user_functions.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ funcid,
+ schemaname,
+ funcname,
+ calls,
+ total_time,
+ self_time
+ FROM pg_stat_user_functions
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_stat_user_indexes.sql b/src/pgperf/90/pg_stat_user_indexes.sql
new file mode 100644
index 0000000..80babf5
--- /dev/null
+++ b/src/pgperf/90/pg_stat_user_indexes.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_scan,
+ idx_tup_read,
+ idx_tup_fetch
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_stat_user_tables.sql b/src/pgperf/90/pg_stat_user_tables.sql
new file mode 100644
index 0000000..5b0c5f4
--- /dev/null
+++ b/src/pgperf/90/pg_stat_user_tables.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_tup_hot_upd,
+ n_live_tup,
+ n_dead_tup,
+ last_vacuum,
+ last_autovacuum,
+ last_analyze,
+ last_autoanalyze
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_statio_user_indexes.sql b/src/pgperf/90/pg_statio_user_indexes.sql
new file mode 100644
index 0000000..7f8bd33
--- /dev/null
+++ b/src/pgperf/90/pg_statio_user_indexes.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_blks_read,
+ idx_blks_hit
+ FROM pg_statio_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_statio_user_sequences.sql b/src/pgperf/90/pg_statio_user_sequences.sql
new file mode 100644
index 0000000..9cb8d27
--- /dev/null
+++ b/src/pgperf/90/pg_statio_user_sequences.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ blks_read,
+ blks_hit
+ FROM pg_statio_user_sequences
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_statio_user_tables.sql b/src/pgperf/90/pg_statio_user_tables.sql
new file mode 100644
index 0000000..ffc6adf
--- /dev/null
+++ b/src/pgperf/90/pg_statio_user_tables.sql
@@ -0,0 +1,14 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ heap_blks_read,
+ heap_blks_hit,
+ idx_blks_read,
+ idx_blks_hit,
+ toast_blks_read,
+ toast_blks_hit,
+ tidx_blks_read,
+ tidx_blks_hit
+ FROM pg_statio_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/90/pg_statistic.sql b/src/pgperf/90/pg_statistic.sql
new file mode 100644
index 0000000..e9c2b69
--- /dev/null
+++ b/src/pgperf/90/pg_statistic.sql
@@ -0,0 +1,31 @@
+SELECT 0::int as sid,
+ s.starelid,
+ c.relname as starelname,
+ s.staattnum,
+ a.attname as staattname,
+ s.stainherit,
+ s.stanullfrac,
+ s.stawidth,
+ s.stadistinct,
+ s.stakind1,
+ s.stakind2,
+ s.stakind3,
+ s.stakind4,
+ s.staop1,
+ s.staop2,
+ s.staop3,
+ s.staop4,
+ s.stanumbers1,
+ s.stanumbers2,
+ s.stanumbers3,
+ s.stanumbers4,
+ s.stavalues1::TEXT,
+ s.stavalues2::TEXT,
+ s.stavalues3::TEXT,
+ s.stavalues4::TEXT
+ FROM pg_statistic s, pg_class c, pg_namespace n, pg_attribute a
+ WHERE n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
+ AND n.oid = c.relnamespace
+ AND c.oid = s.starelid
+ AND a.attnum = s.staattnum
+ AND a.attrelid = s.starelid;
diff --git a/src/pgperf/90/pg_stats.sql b/src/pgperf/90/pg_stats.sql
new file mode 100644
index 0000000..1b198d8
--- /dev/null
+++ b/src/pgperf/90/pg_stats.sql
@@ -0,0 +1,14 @@
+SELECT 0::int AS sid,
+ schemaname,
+ tablename,
+ attname,
+ inherited,
+ null_frac,
+ avg_width,
+ n_distinct,
+ most_common_vals::text,
+ most_common_freqs,
+ histogram_bounds::text,
+ correlation
+ FROM pg_stats;
+
diff --git a/src/pgperf/90/pgstatindex.sql b/src/pgperf/90/pgstatindex.sql
new file mode 100644
index 0000000..021d0c2
--- /dev/null
+++ b/src/pgperf/90/pgstatindex.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ indexrelname,
+ (pgstatindex).version,
+ (pgstatindex).tree_level,
+ (pgstatindex).index_size,
+ (pgstatindex).root_block_no,
+ (pgstatindex).internal_pages,
+ (pgstatindex).leaf_pages,
+ (pgstatindex).empty_pages,
+ (pgstatindex).deleted_pages,
+ (pgstatindex).avg_leaf_density,
+ (pgstatindex).leaf_fragmentation
+ FROM ( SELECT schemaname,
+ relname,
+ indexrelname,
+ pgstatindex(schemaname || '.' || indexrelname)
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/90/pgstattuple.sql b/src/pgperf/90/pgstattuple.sql
new file mode 100644
index 0000000..196132c
--- /dev/null
+++ b/src/pgperf/90/pgstattuple.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ (pgstattuple).table_len,
+ (pgstattuple).tuple_count,
+ (pgstattuple).tuple_len,
+ (pgstattuple).tuple_percent,
+ (pgstattuple).dead_tuple_count,
+ (pgstattuple).dead_tuple_len,
+ (pgstattuple).dead_tuple_percent,
+ (pgstattuple).free_space,
+ (pgstattuple).free_percent
+ FROM ( SELECT schemaname,
+ relname,
+ pgstattuple(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/91/pg_current_xlog.sql b/src/pgperf/91/pg_current_xlog.sql
new file mode 100644
index 0000000..11ff12f
--- /dev/null
+++ b/src/pgperf/91/pg_current_xlog.sql
@@ -0,0 +1,3 @@
+SELECT 0::int as sid,
+ pg_current_xlog_location() AS location,
+ pg_current_xlog_insert_location() AS insert_location;
diff --git a/src/pgperf/91/pg_database_size.sql b/src/pgperf/91/pg_database_size.sql
new file mode 100644
index 0000000..d0798c0
--- /dev/null
+++ b/src/pgperf/91/pg_database_size.sql
@@ -0,0 +1,5 @@
+SELECT 0::int as sid,
+ datname,
+ pg_database_size(datname)
+ FROM pg_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/91/pg_locks.sql b/src/pgperf/91/pg_locks.sql
new file mode 100644
index 0000000..822b0ce
--- /dev/null
+++ b/src/pgperf/91/pg_locks.sql
@@ -0,0 +1,16 @@
+SELECT 0::int as sid,
+ locktype,
+ database,
+ relation,
+ page,
+ tuple,
+ virtualxid,
+ transactionid,
+ classid,
+ objid,
+ objsubid,
+ virtualtransaction,
+ pid,
+ mode,
+ granted
+ FROM pg_locks;
diff --git a/src/pgperf/91/pg_relation_size.sql b/src/pgperf/91/pg_relation_size.sql
new file mode 100644
index 0000000..8aee182
--- /dev/null
+++ b/src/pgperf/91/pg_relation_size.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ schemaname,
+ relid,
+ relname,
+ pg_relation_size(relid),
+ pg_total_relation_size(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_stat_activity.sql b/src/pgperf/91/pg_stat_activity.sql
new file mode 100644
index 0000000..eaf84bc
--- /dev/null
+++ b/src/pgperf/91/pg_stat_activity.sql
@@ -0,0 +1,16 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ procpid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ xact_start,
+ query_start,
+ waiting,
+ current_query
+ FROM pg_stat_activity;
diff --git a/src/pgperf/91/pg_stat_bgwriter.sql b/src/pgperf/91/pg_stat_bgwriter.sql
new file mode 100644
index 0000000..5b3596b
--- /dev/null
+++ b/src/pgperf/91/pg_stat_bgwriter.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ checkpoints_timed,
+ checkpoints_req,
+ buffers_checkpoint,
+ buffers_clean,
+ maxwritten_clean,
+ buffers_backend,
+ buffers_backend_fsync,
+ buffers_alloc,
+ stats_reset
+ FROM pg_stat_bgwriter;
diff --git a/src/pgperf/91/pg_stat_database.sql b/src/pgperf/91/pg_stat_database.sql
new file mode 100644
index 0000000..48c7a8e
--- /dev/null
+++ b/src/pgperf/91/pg_stat_database.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ numbackends,
+ xact_commit,
+ xact_rollback,
+ blks_read,
+ blks_hit,
+ tup_returned,
+ tup_fetched,
+ tup_inserted,
+ tup_updated,
+ tup_deleted,
+ conflicts,
+ stats_reset
+ FROM pg_stat_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/91/pg_stat_database_conflicts.sql b/src/pgperf/91/pg_stat_database_conflicts.sql
new file mode 100644
index 0000000..ad511ce
--- /dev/null
+++ b/src/pgperf/91/pg_stat_database_conflicts.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ confl_tablespace,
+ confl_lock,
+ confl_snapshot,
+ confl_bufferpin,
+ confl_deadlock
+ FROM pg_stat_database_conflicts
+ WHERE datname = current_database();
diff --git a/src/pgperf/91/pg_stat_replication.sql b/src/pgperf/91/pg_stat_replication.sql
new file mode 100644
index 0000000..6345185
--- /dev/null
+++ b/src/pgperf/91/pg_stat_replication.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ procpid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ state,
+ sent_location,
+ write_location,
+ flush_location,
+ replay_location,
+ sync_priority,
+ sync_state
+ FROM pg_stat_replication;
diff --git a/src/pgperf/91/pg_stat_statements.sql b/src/pgperf/91/pg_stat_statements.sql
new file mode 100644
index 0000000..a457fd5
--- /dev/null
+++ b/src/pgperf/91/pg_stat_statements.sql
@@ -0,0 +1,16 @@
+SELECT 0::int as sid,
+ userid,
+ dbid,
+ query,
+ calls,
+ total_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written
+ FROM pg_stat_statements;
diff --git a/src/pgperf/91/pg_stat_user_functions.sql b/src/pgperf/91/pg_stat_user_functions.sql
new file mode 100644
index 0000000..9b137b4
--- /dev/null
+++ b/src/pgperf/91/pg_stat_user_functions.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ funcid,
+ schemaname,
+ funcname,
+ calls,
+ total_time,
+ self_time
+ FROM pg_stat_user_functions
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_stat_user_indexes.sql b/src/pgperf/91/pg_stat_user_indexes.sql
new file mode 100644
index 0000000..80babf5
--- /dev/null
+++ b/src/pgperf/91/pg_stat_user_indexes.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_scan,
+ idx_tup_read,
+ idx_tup_fetch
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_stat_user_tables.sql b/src/pgperf/91/pg_stat_user_tables.sql
new file mode 100644
index 0000000..6985f30
--- /dev/null
+++ b/src/pgperf/91/pg_stat_user_tables.sql
@@ -0,0 +1,24 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_tup_hot_upd,
+ n_live_tup,
+ n_dead_tup,
+ last_vacuum,
+ last_autovacuum,
+ last_analyze,
+ last_autoanalyze,
+ vacuum_count,
+ autovacuum_count,
+ analyze_count,
+ autoanalyze_count
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_statio_user_indexes.sql b/src/pgperf/91/pg_statio_user_indexes.sql
new file mode 100644
index 0000000..7f8bd33
--- /dev/null
+++ b/src/pgperf/91/pg_statio_user_indexes.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_blks_read,
+ idx_blks_hit
+ FROM pg_statio_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_statio_user_sequences.sql b/src/pgperf/91/pg_statio_user_sequences.sql
new file mode 100644
index 0000000..9cb8d27
--- /dev/null
+++ b/src/pgperf/91/pg_statio_user_sequences.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ blks_read,
+ blks_hit
+ FROM pg_statio_user_sequences
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_statio_user_tables.sql b/src/pgperf/91/pg_statio_user_tables.sql
new file mode 100644
index 0000000..ffc6adf
--- /dev/null
+++ b/src/pgperf/91/pg_statio_user_tables.sql
@@ -0,0 +1,14 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ heap_blks_read,
+ heap_blks_hit,
+ idx_blks_read,
+ idx_blks_hit,
+ toast_blks_read,
+ toast_blks_hit,
+ tidx_blks_read,
+ tidx_blks_hit
+ FROM pg_statio_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/91/pg_statistic.sql b/src/pgperf/91/pg_statistic.sql
new file mode 100644
index 0000000..e9c2b69
--- /dev/null
+++ b/src/pgperf/91/pg_statistic.sql
@@ -0,0 +1,31 @@
+SELECT 0::int as sid,
+ s.starelid,
+ c.relname as starelname,
+ s.staattnum,
+ a.attname as staattname,
+ s.stainherit,
+ s.stanullfrac,
+ s.stawidth,
+ s.stadistinct,
+ s.stakind1,
+ s.stakind2,
+ s.stakind3,
+ s.stakind4,
+ s.staop1,
+ s.staop2,
+ s.staop3,
+ s.staop4,
+ s.stanumbers1,
+ s.stanumbers2,
+ s.stanumbers3,
+ s.stanumbers4,
+ s.stavalues1::TEXT,
+ s.stavalues2::TEXT,
+ s.stavalues3::TEXT,
+ s.stavalues4::TEXT
+ FROM pg_statistic s, pg_class c, pg_namespace n, pg_attribute a
+ WHERE n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
+ AND n.oid = c.relnamespace
+ AND c.oid = s.starelid
+ AND a.attnum = s.staattnum
+ AND a.attrelid = s.starelid;
diff --git a/src/pgperf/91/pg_stats.sql b/src/pgperf/91/pg_stats.sql
new file mode 100644
index 0000000..1b198d8
--- /dev/null
+++ b/src/pgperf/91/pg_stats.sql
@@ -0,0 +1,14 @@
+SELECT 0::int AS sid,
+ schemaname,
+ tablename,
+ attname,
+ inherited,
+ null_frac,
+ avg_width,
+ n_distinct,
+ most_common_vals::text,
+ most_common_freqs,
+ histogram_bounds::text,
+ correlation
+ FROM pg_stats;
+
diff --git a/src/pgperf/91/pgstatindex.sql b/src/pgperf/91/pgstatindex.sql
new file mode 100644
index 0000000..021d0c2
--- /dev/null
+++ b/src/pgperf/91/pgstatindex.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ indexrelname,
+ (pgstatindex).version,
+ (pgstatindex).tree_level,
+ (pgstatindex).index_size,
+ (pgstatindex).root_block_no,
+ (pgstatindex).internal_pages,
+ (pgstatindex).leaf_pages,
+ (pgstatindex).empty_pages,
+ (pgstatindex).deleted_pages,
+ (pgstatindex).avg_leaf_density,
+ (pgstatindex).leaf_fragmentation
+ FROM ( SELECT schemaname,
+ relname,
+ indexrelname,
+ pgstatindex(schemaname || '.' || indexrelname)
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/91/pgstattuple.sql b/src/pgperf/91/pgstattuple.sql
new file mode 100644
index 0000000..196132c
--- /dev/null
+++ b/src/pgperf/91/pgstattuple.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ (pgstattuple).table_len,
+ (pgstattuple).tuple_count,
+ (pgstattuple).tuple_len,
+ (pgstattuple).tuple_percent,
+ (pgstattuple).dead_tuple_count,
+ (pgstattuple).dead_tuple_len,
+ (pgstattuple).dead_tuple_percent,
+ (pgstattuple).free_space,
+ (pgstattuple).free_percent
+ FROM ( SELECT schemaname,
+ relname,
+ pgstattuple(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/92/pg_current_xlog.sql b/src/pgperf/92/pg_current_xlog.sql
new file mode 100644
index 0000000..11ff12f
--- /dev/null
+++ b/src/pgperf/92/pg_current_xlog.sql
@@ -0,0 +1,3 @@
+SELECT 0::int as sid,
+ pg_current_xlog_location() AS location,
+ pg_current_xlog_insert_location() AS insert_location;
diff --git a/src/pgperf/92/pg_database_size.sql b/src/pgperf/92/pg_database_size.sql
new file mode 100644
index 0000000..d0798c0
--- /dev/null
+++ b/src/pgperf/92/pg_database_size.sql
@@ -0,0 +1,5 @@
+SELECT 0::int as sid,
+ datname,
+ pg_database_size(datname)
+ FROM pg_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/92/pg_locks.sql b/src/pgperf/92/pg_locks.sql
new file mode 100644
index 0000000..3dd228d
--- /dev/null
+++ b/src/pgperf/92/pg_locks.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ locktype,
+ database,
+ relation,
+ page,
+ tuple,
+ virtualxid,
+ transactionid,
+ classid,
+ objid,
+ objsubid,
+ virtualtransaction,
+ pid,
+ mode,
+ granted,
+ fastpath
+ FROM pg_locks;
diff --git a/src/pgperf/92/pg_relation_size.sql b/src/pgperf/92/pg_relation_size.sql
new file mode 100644
index 0000000..8aee182
--- /dev/null
+++ b/src/pgperf/92/pg_relation_size.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ schemaname,
+ relid,
+ relname,
+ pg_relation_size(relid),
+ pg_total_relation_size(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_stat_activity.sql b/src/pgperf/92/pg_stat_activity.sql
new file mode 100644
index 0000000..5fd8451
--- /dev/null
+++ b/src/pgperf/92/pg_stat_activity.sql
@@ -0,0 +1,18 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ xact_start,
+ query_start,
+ state_change,
+ waiting,
+ state,
+ query
+ FROM pg_stat_activity;
diff --git a/src/pgperf/92/pg_stat_bgwriter.sql b/src/pgperf/92/pg_stat_bgwriter.sql
new file mode 100644
index 0000000..6ed41dd
--- /dev/null
+++ b/src/pgperf/92/pg_stat_bgwriter.sql
@@ -0,0 +1,13 @@
+SELECT 0::int as sid,
+ checkpoints_timed,
+ checkpoints_req,
+ checkpoint_write_time,
+ checkpoint_sync_time,
+ buffers_checkpoint,
+ buffers_clean,
+ maxwritten_clean,
+ buffers_backend,
+ buffers_backend_fsync,
+ buffers_alloc,
+ stats_reset
+ FROM pg_stat_bgwriter;
diff --git a/src/pgperf/92/pg_stat_database.sql b/src/pgperf/92/pg_stat_database.sql
new file mode 100644
index 0000000..896d289
--- /dev/null
+++ b/src/pgperf/92/pg_stat_database.sql
@@ -0,0 +1,22 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ numbackends,
+ xact_commit,
+ xact_rollback,
+ blks_read,
+ blks_hit,
+ tup_returned,
+ tup_fetched,
+ tup_inserted,
+ tup_updated,
+ tup_deleted,
+ conflicts,
+ temp_files,
+ temp_bytes,
+ deadlocks,
+ blk_read_time,
+ blk_write_time,
+ stats_reset
+ FROM pg_stat_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/92/pg_stat_database_conflicts.sql b/src/pgperf/92/pg_stat_database_conflicts.sql
new file mode 100644
index 0000000..ad511ce
--- /dev/null
+++ b/src/pgperf/92/pg_stat_database_conflicts.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ confl_tablespace,
+ confl_lock,
+ confl_snapshot,
+ confl_bufferpin,
+ confl_deadlock
+ FROM pg_stat_database_conflicts
+ WHERE datname = current_database();
diff --git a/src/pgperf/92/pg_stat_replication.sql b/src/pgperf/92/pg_stat_replication.sql
new file mode 100644
index 0000000..967880d
--- /dev/null
+++ b/src/pgperf/92/pg_stat_replication.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ state,
+ sent_location,
+ write_location,
+ flush_location,
+ replay_location,
+ sync_priority,
+ sync_state
+ FROM pg_stat_replication;
diff --git a/src/pgperf/92/pg_stat_statements.sql b/src/pgperf/92/pg_stat_statements.sql
new file mode 100644
index 0000000..245b619
--- /dev/null
+++ b/src/pgperf/92/pg_stat_statements.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ userid,
+ dbid,
+ query,
+ calls,
+ total_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time
+ FROM pg_stat_statements;
diff --git a/src/pgperf/92/pg_stat_user_functions.sql b/src/pgperf/92/pg_stat_user_functions.sql
new file mode 100644
index 0000000..9b137b4
--- /dev/null
+++ b/src/pgperf/92/pg_stat_user_functions.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ funcid,
+ schemaname,
+ funcname,
+ calls,
+ total_time,
+ self_time
+ FROM pg_stat_user_functions
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_stat_user_indexes.sql b/src/pgperf/92/pg_stat_user_indexes.sql
new file mode 100644
index 0000000..80babf5
--- /dev/null
+++ b/src/pgperf/92/pg_stat_user_indexes.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_scan,
+ idx_tup_read,
+ idx_tup_fetch
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_stat_user_tables.sql b/src/pgperf/92/pg_stat_user_tables.sql
new file mode 100644
index 0000000..6985f30
--- /dev/null
+++ b/src/pgperf/92/pg_stat_user_tables.sql
@@ -0,0 +1,24 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_tup_hot_upd,
+ n_live_tup,
+ n_dead_tup,
+ last_vacuum,
+ last_autovacuum,
+ last_analyze,
+ last_autoanalyze,
+ vacuum_count,
+ autovacuum_count,
+ analyze_count,
+ autoanalyze_count
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_statio_user_indexes.sql b/src/pgperf/92/pg_statio_user_indexes.sql
new file mode 100644
index 0000000..7f8bd33
--- /dev/null
+++ b/src/pgperf/92/pg_statio_user_indexes.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_blks_read,
+ idx_blks_hit
+ FROM pg_statio_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_statio_user_sequences.sql b/src/pgperf/92/pg_statio_user_sequences.sql
new file mode 100644
index 0000000..9cb8d27
--- /dev/null
+++ b/src/pgperf/92/pg_statio_user_sequences.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ blks_read,
+ blks_hit
+ FROM pg_statio_user_sequences
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_statio_user_tables.sql b/src/pgperf/92/pg_statio_user_tables.sql
new file mode 100644
index 0000000..ffc6adf
--- /dev/null
+++ b/src/pgperf/92/pg_statio_user_tables.sql
@@ -0,0 +1,14 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ heap_blks_read,
+ heap_blks_hit,
+ idx_blks_read,
+ idx_blks_hit,
+ toast_blks_read,
+ toast_blks_hit,
+ tidx_blks_read,
+ tidx_blks_hit
+ FROM pg_statio_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/92/pg_statistic.sql b/src/pgperf/92/pg_statistic.sql
new file mode 100644
index 0000000..7f8c328
--- /dev/null
+++ b/src/pgperf/92/pg_statistic.sql
@@ -0,0 +1,35 @@
+SELECT 0::int as sid,
+ s.starelid,
+ c.relname as starelname,
+ s.staattnum,
+ a.attname as staattname,
+ s.stainherit,
+ s.stanullfrac,
+ s.stawidth,
+ s.stadistinct,
+ s.stakind1,
+ s.stakind2,
+ s.stakind3,
+ s.stakind4,
+ s.stakind5,
+ s.staop1,
+ s.staop2,
+ s.staop3,
+ s.staop4,
+ s.staop5,
+ s.stanumbers1,
+ s.stanumbers2,
+ s.stanumbers3,
+ s.stanumbers4,
+ s.stanumbers5,
+ s.stavalues1::TEXT,
+ s.stavalues2::TEXT,
+ s.stavalues3::TEXT,
+ s.stavalues4::TEXT,
+ s.stavalues5::TEXT
+ FROM pg_statistic s, pg_class c, pg_namespace n, pg_attribute a
+ WHERE n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
+ AND n.oid = c.relnamespace
+ AND c.oid = s.starelid
+ AND a.attnum = s.staattnum
+ AND a.attrelid = s.starelid;
diff --git a/src/pgperf/92/pg_stats.sql b/src/pgperf/92/pg_stats.sql
new file mode 100644
index 0000000..aae20f9
--- /dev/null
+++ b/src/pgperf/92/pg_stats.sql
@@ -0,0 +1,17 @@
+SELECT 0::int AS sid,
+ schemaname,
+ tablename,
+ attname,
+ inherited,
+ null_frac,
+ avg_width,
+ n_distinct,
+ most_common_vals::text,
+ most_common_freqs,
+ histogram_bounds::text,
+ correlation,
+ most_common_elems::text,
+ most_common_elem_freqs,
+ elem_count_histogram
+ FROM pg_stats;
+
diff --git a/src/pgperf/92/pgstatindex.sql b/src/pgperf/92/pgstatindex.sql
new file mode 100644
index 0000000..021d0c2
--- /dev/null
+++ b/src/pgperf/92/pgstatindex.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ indexrelname,
+ (pgstatindex).version,
+ (pgstatindex).tree_level,
+ (pgstatindex).index_size,
+ (pgstatindex).root_block_no,
+ (pgstatindex).internal_pages,
+ (pgstatindex).leaf_pages,
+ (pgstatindex).empty_pages,
+ (pgstatindex).deleted_pages,
+ (pgstatindex).avg_leaf_density,
+ (pgstatindex).leaf_fragmentation
+ FROM ( SELECT schemaname,
+ relname,
+ indexrelname,
+ pgstatindex(schemaname || '.' || indexrelname)
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/92/pgstattuple.sql b/src/pgperf/92/pgstattuple.sql
new file mode 100644
index 0000000..196132c
--- /dev/null
+++ b/src/pgperf/92/pgstattuple.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ (pgstattuple).table_len,
+ (pgstattuple).tuple_count,
+ (pgstattuple).tuple_len,
+ (pgstattuple).tuple_percent,
+ (pgstattuple).dead_tuple_count,
+ (pgstattuple).dead_tuple_len,
+ (pgstattuple).dead_tuple_percent,
+ (pgstattuple).free_space,
+ (pgstattuple).free_percent
+ FROM ( SELECT schemaname,
+ relname,
+ pgstattuple(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/93/pg_current_xlog.sql b/src/pgperf/93/pg_current_xlog.sql
new file mode 100644
index 0000000..11ff12f
--- /dev/null
+++ b/src/pgperf/93/pg_current_xlog.sql
@@ -0,0 +1,3 @@
+SELECT 0::int as sid,
+ pg_current_xlog_location() AS location,
+ pg_current_xlog_insert_location() AS insert_location;
diff --git a/src/pgperf/93/pg_database_size.sql b/src/pgperf/93/pg_database_size.sql
new file mode 100644
index 0000000..d0798c0
--- /dev/null
+++ b/src/pgperf/93/pg_database_size.sql
@@ -0,0 +1,5 @@
+SELECT 0::int as sid,
+ datname,
+ pg_database_size(datname)
+ FROM pg_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/93/pg_locks.sql b/src/pgperf/93/pg_locks.sql
new file mode 100644
index 0000000..3dd228d
--- /dev/null
+++ b/src/pgperf/93/pg_locks.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ locktype,
+ database,
+ relation,
+ page,
+ tuple,
+ virtualxid,
+ transactionid,
+ classid,
+ objid,
+ objsubid,
+ virtualtransaction,
+ pid,
+ mode,
+ granted,
+ fastpath
+ FROM pg_locks;
diff --git a/src/pgperf/93/pg_relation_size.sql b/src/pgperf/93/pg_relation_size.sql
new file mode 100644
index 0000000..8aee182
--- /dev/null
+++ b/src/pgperf/93/pg_relation_size.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ schemaname,
+ relid,
+ relname,
+ pg_relation_size(relid),
+ pg_total_relation_size(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_stat_activity.sql b/src/pgperf/93/pg_stat_activity.sql
new file mode 100644
index 0000000..5fd8451
--- /dev/null
+++ b/src/pgperf/93/pg_stat_activity.sql
@@ -0,0 +1,18 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ xact_start,
+ query_start,
+ state_change,
+ waiting,
+ state,
+ query
+ FROM pg_stat_activity;
diff --git a/src/pgperf/93/pg_stat_bgwriter.sql b/src/pgperf/93/pg_stat_bgwriter.sql
new file mode 100644
index 0000000..6ed41dd
--- /dev/null
+++ b/src/pgperf/93/pg_stat_bgwriter.sql
@@ -0,0 +1,13 @@
+SELECT 0::int as sid,
+ checkpoints_timed,
+ checkpoints_req,
+ checkpoint_write_time,
+ checkpoint_sync_time,
+ buffers_checkpoint,
+ buffers_clean,
+ maxwritten_clean,
+ buffers_backend,
+ buffers_backend_fsync,
+ buffers_alloc,
+ stats_reset
+ FROM pg_stat_bgwriter;
diff --git a/src/pgperf/93/pg_stat_database.sql b/src/pgperf/93/pg_stat_database.sql
new file mode 100644
index 0000000..896d289
--- /dev/null
+++ b/src/pgperf/93/pg_stat_database.sql
@@ -0,0 +1,22 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ numbackends,
+ xact_commit,
+ xact_rollback,
+ blks_read,
+ blks_hit,
+ tup_returned,
+ tup_fetched,
+ tup_inserted,
+ tup_updated,
+ tup_deleted,
+ conflicts,
+ temp_files,
+ temp_bytes,
+ deadlocks,
+ blk_read_time,
+ blk_write_time,
+ stats_reset
+ FROM pg_stat_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/93/pg_stat_database_conflicts.sql b/src/pgperf/93/pg_stat_database_conflicts.sql
new file mode 100644
index 0000000..ad511ce
--- /dev/null
+++ b/src/pgperf/93/pg_stat_database_conflicts.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ confl_tablespace,
+ confl_lock,
+ confl_snapshot,
+ confl_bufferpin,
+ confl_deadlock
+ FROM pg_stat_database_conflicts
+ WHERE datname = current_database();
diff --git a/src/pgperf/93/pg_stat_replication.sql b/src/pgperf/93/pg_stat_replication.sql
new file mode 100644
index 0000000..967880d
--- /dev/null
+++ b/src/pgperf/93/pg_stat_replication.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ state,
+ sent_location,
+ write_location,
+ flush_location,
+ replay_location,
+ sync_priority,
+ sync_state
+ FROM pg_stat_replication;
diff --git a/src/pgperf/93/pg_stat_statements.sql b/src/pgperf/93/pg_stat_statements.sql
new file mode 100644
index 0000000..245b619
--- /dev/null
+++ b/src/pgperf/93/pg_stat_statements.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ userid,
+ dbid,
+ query,
+ calls,
+ total_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time
+ FROM pg_stat_statements;
diff --git a/src/pgperf/93/pg_stat_user_functions.sql b/src/pgperf/93/pg_stat_user_functions.sql
new file mode 100644
index 0000000..9b137b4
--- /dev/null
+++ b/src/pgperf/93/pg_stat_user_functions.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ funcid,
+ schemaname,
+ funcname,
+ calls,
+ total_time,
+ self_time
+ FROM pg_stat_user_functions
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_stat_user_indexes.sql b/src/pgperf/93/pg_stat_user_indexes.sql
new file mode 100644
index 0000000..80babf5
--- /dev/null
+++ b/src/pgperf/93/pg_stat_user_indexes.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_scan,
+ idx_tup_read,
+ idx_tup_fetch
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_stat_user_tables.sql b/src/pgperf/93/pg_stat_user_tables.sql
new file mode 100644
index 0000000..6985f30
--- /dev/null
+++ b/src/pgperf/93/pg_stat_user_tables.sql
@@ -0,0 +1,24 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_tup_hot_upd,
+ n_live_tup,
+ n_dead_tup,
+ last_vacuum,
+ last_autovacuum,
+ last_analyze,
+ last_autoanalyze,
+ vacuum_count,
+ autovacuum_count,
+ analyze_count,
+ autoanalyze_count
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_statio_user_indexes.sql b/src/pgperf/93/pg_statio_user_indexes.sql
new file mode 100644
index 0000000..7f8bd33
--- /dev/null
+++ b/src/pgperf/93/pg_statio_user_indexes.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_blks_read,
+ idx_blks_hit
+ FROM pg_statio_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_statio_user_sequences.sql b/src/pgperf/93/pg_statio_user_sequences.sql
new file mode 100644
index 0000000..9cb8d27
--- /dev/null
+++ b/src/pgperf/93/pg_statio_user_sequences.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ blks_read,
+ blks_hit
+ FROM pg_statio_user_sequences
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_statio_user_tables.sql b/src/pgperf/93/pg_statio_user_tables.sql
new file mode 100644
index 0000000..ffc6adf
--- /dev/null
+++ b/src/pgperf/93/pg_statio_user_tables.sql
@@ -0,0 +1,14 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ heap_blks_read,
+ heap_blks_hit,
+ idx_blks_read,
+ idx_blks_hit,
+ toast_blks_read,
+ toast_blks_hit,
+ tidx_blks_read,
+ tidx_blks_hit
+ FROM pg_statio_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/93/pg_statistic.sql b/src/pgperf/93/pg_statistic.sql
new file mode 100644
index 0000000..7f8c328
--- /dev/null
+++ b/src/pgperf/93/pg_statistic.sql
@@ -0,0 +1,35 @@
+SELECT 0::int as sid,
+ s.starelid,
+ c.relname as starelname,
+ s.staattnum,
+ a.attname as staattname,
+ s.stainherit,
+ s.stanullfrac,
+ s.stawidth,
+ s.stadistinct,
+ s.stakind1,
+ s.stakind2,
+ s.stakind3,
+ s.stakind4,
+ s.stakind5,
+ s.staop1,
+ s.staop2,
+ s.staop3,
+ s.staop4,
+ s.staop5,
+ s.stanumbers1,
+ s.stanumbers2,
+ s.stanumbers3,
+ s.stanumbers4,
+ s.stanumbers5,
+ s.stavalues1::TEXT,
+ s.stavalues2::TEXT,
+ s.stavalues3::TEXT,
+ s.stavalues4::TEXT,
+ s.stavalues5::TEXT
+ FROM pg_statistic s, pg_class c, pg_namespace n, pg_attribute a
+ WHERE n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
+ AND n.oid = c.relnamespace
+ AND c.oid = s.starelid
+ AND a.attnum = s.staattnum
+ AND a.attrelid = s.starelid;
diff --git a/src/pgperf/93/pg_stats.sql b/src/pgperf/93/pg_stats.sql
new file mode 100644
index 0000000..aae20f9
--- /dev/null
+++ b/src/pgperf/93/pg_stats.sql
@@ -0,0 +1,17 @@
+SELECT 0::int AS sid,
+ schemaname,
+ tablename,
+ attname,
+ inherited,
+ null_frac,
+ avg_width,
+ n_distinct,
+ most_common_vals::text,
+ most_common_freqs,
+ histogram_bounds::text,
+ correlation,
+ most_common_elems::text,
+ most_common_elem_freqs,
+ elem_count_histogram
+ FROM pg_stats;
+
diff --git a/src/pgperf/93/pgstatindex.sql b/src/pgperf/93/pgstatindex.sql
new file mode 100644
index 0000000..021d0c2
--- /dev/null
+++ b/src/pgperf/93/pgstatindex.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ indexrelname,
+ (pgstatindex).version,
+ (pgstatindex).tree_level,
+ (pgstatindex).index_size,
+ (pgstatindex).root_block_no,
+ (pgstatindex).internal_pages,
+ (pgstatindex).leaf_pages,
+ (pgstatindex).empty_pages,
+ (pgstatindex).deleted_pages,
+ (pgstatindex).avg_leaf_density,
+ (pgstatindex).leaf_fragmentation
+ FROM ( SELECT schemaname,
+ relname,
+ indexrelname,
+ pgstatindex(schemaname || '.' || indexrelname)
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/93/pgstattuple.sql b/src/pgperf/93/pgstattuple.sql
new file mode 100644
index 0000000..196132c
--- /dev/null
+++ b/src/pgperf/93/pgstattuple.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ (pgstattuple).table_len,
+ (pgstattuple).tuple_count,
+ (pgstattuple).tuple_len,
+ (pgstattuple).tuple_percent,
+ (pgstattuple).dead_tuple_count,
+ (pgstattuple).dead_tuple_len,
+ (pgstattuple).dead_tuple_percent,
+ (pgstattuple).free_space,
+ (pgstattuple).free_percent
+ FROM ( SELECT schemaname,
+ relname,
+ pgstattuple(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/94/pg_current_xlog.sql b/src/pgperf/94/pg_current_xlog.sql
new file mode 100644
index 0000000..11ff12f
--- /dev/null
+++ b/src/pgperf/94/pg_current_xlog.sql
@@ -0,0 +1,3 @@
+SELECT 0::int as sid,
+ pg_current_xlog_location() AS location,
+ pg_current_xlog_insert_location() AS insert_location;
diff --git a/src/pgperf/94/pg_database_size.sql b/src/pgperf/94/pg_database_size.sql
new file mode 100644
index 0000000..d0798c0
--- /dev/null
+++ b/src/pgperf/94/pg_database_size.sql
@@ -0,0 +1,5 @@
+SELECT 0::int as sid,
+ datname,
+ pg_database_size(datname)
+ FROM pg_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/94/pg_locks.sql b/src/pgperf/94/pg_locks.sql
new file mode 100644
index 0000000..3dd228d
--- /dev/null
+++ b/src/pgperf/94/pg_locks.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ locktype,
+ database,
+ relation,
+ page,
+ tuple,
+ virtualxid,
+ transactionid,
+ classid,
+ objid,
+ objsubid,
+ virtualtransaction,
+ pid,
+ mode,
+ granted,
+ fastpath
+ FROM pg_locks;
diff --git a/src/pgperf/94/pg_relation_size.sql b/src/pgperf/94/pg_relation_size.sql
new file mode 100644
index 0000000..8aee182
--- /dev/null
+++ b/src/pgperf/94/pg_relation_size.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ schemaname,
+ relid,
+ relname,
+ pg_relation_size(relid),
+ pg_total_relation_size(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_stat_activity.sql b/src/pgperf/94/pg_stat_activity.sql
new file mode 100644
index 0000000..842580e
--- /dev/null
+++ b/src/pgperf/94/pg_stat_activity.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ xact_start,
+ query_start,
+ state_change,
+ waiting,
+ state,
+ backend_xid,
+ backend_xmin,
+ query
+ FROM pg_stat_activity;
diff --git a/src/pgperf/94/pg_stat_archiver.sql b/src/pgperf/94/pg_stat_archiver.sql
new file mode 100644
index 0000000..c45ef2e
--- /dev/null
+++ b/src/pgperf/94/pg_stat_archiver.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ archived_count,
+ last_archived_wal,
+ last_archived_time,
+ failed_count,
+ last_failed_wal,
+ last_failed_time,
+ stats_reset
+ FROM pg_stat_archiver;
diff --git a/src/pgperf/94/pg_stat_bgwriter.sql b/src/pgperf/94/pg_stat_bgwriter.sql
new file mode 100644
index 0000000..6ed41dd
--- /dev/null
+++ b/src/pgperf/94/pg_stat_bgwriter.sql
@@ -0,0 +1,13 @@
+SELECT 0::int as sid,
+ checkpoints_timed,
+ checkpoints_req,
+ checkpoint_write_time,
+ checkpoint_sync_time,
+ buffers_checkpoint,
+ buffers_clean,
+ maxwritten_clean,
+ buffers_backend,
+ buffers_backend_fsync,
+ buffers_alloc,
+ stats_reset
+ FROM pg_stat_bgwriter;
diff --git a/src/pgperf/94/pg_stat_database.sql b/src/pgperf/94/pg_stat_database.sql
new file mode 100644
index 0000000..896d289
--- /dev/null
+++ b/src/pgperf/94/pg_stat_database.sql
@@ -0,0 +1,22 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ numbackends,
+ xact_commit,
+ xact_rollback,
+ blks_read,
+ blks_hit,
+ tup_returned,
+ tup_fetched,
+ tup_inserted,
+ tup_updated,
+ tup_deleted,
+ conflicts,
+ temp_files,
+ temp_bytes,
+ deadlocks,
+ blk_read_time,
+ blk_write_time,
+ stats_reset
+ FROM pg_stat_database
+ WHERE datname = current_database();
diff --git a/src/pgperf/94/pg_stat_database_conflicts.sql b/src/pgperf/94/pg_stat_database_conflicts.sql
new file mode 100644
index 0000000..ad511ce
--- /dev/null
+++ b/src/pgperf/94/pg_stat_database_conflicts.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ datid,
+ datname,
+ confl_tablespace,
+ confl_lock,
+ confl_snapshot,
+ confl_bufferpin,
+ confl_deadlock
+ FROM pg_stat_database_conflicts
+ WHERE datname = current_database();
diff --git a/src/pgperf/94/pg_stat_replication.sql b/src/pgperf/94/pg_stat_replication.sql
new file mode 100644
index 0000000..9987d61
--- /dev/null
+++ b/src/pgperf/94/pg_stat_replication.sql
@@ -0,0 +1,18 @@
+SELECT 0::int as sid,
+ pid,
+ usesysid,
+ usename,
+ application_name,
+ client_addr,
+ client_hostname,
+ client_port,
+ backend_start,
+ backend_xmin,
+ state,
+ sent_location,
+ write_location,
+ flush_location,
+ replay_location,
+ sync_priority,
+ sync_state
+ FROM pg_stat_replication;
diff --git a/src/pgperf/94/pg_stat_statements.sql b/src/pgperf/94/pg_stat_statements.sql
new file mode 100644
index 0000000..bfe268a
--- /dev/null
+++ b/src/pgperf/94/pg_stat_statements.sql
@@ -0,0 +1,21 @@
+SELECT 0::int as sid,
+ userid,
+ dbid,
+ queryid,
+ query,
+ calls,
+ total_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time
+ FROM pg_stat_statements;
diff --git a/src/pgperf/94/pg_stat_user_functions.sql b/src/pgperf/94/pg_stat_user_functions.sql
new file mode 100644
index 0000000..9b137b4
--- /dev/null
+++ b/src/pgperf/94/pg_stat_user_functions.sql
@@ -0,0 +1,9 @@
+SELECT 0::int as sid,
+ funcid,
+ schemaname,
+ funcname,
+ calls,
+ total_time,
+ self_time
+ FROM pg_stat_user_functions
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_stat_user_indexes.sql b/src/pgperf/94/pg_stat_user_indexes.sql
new file mode 100644
index 0000000..80babf5
--- /dev/null
+++ b/src/pgperf/94/pg_stat_user_indexes.sql
@@ -0,0 +1,11 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_scan,
+ idx_tup_read,
+ idx_tup_fetch
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_stat_user_tables.sql b/src/pgperf/94/pg_stat_user_tables.sql
new file mode 100644
index 0000000..0f3f5a8
--- /dev/null
+++ b/src/pgperf/94/pg_stat_user_tables.sql
@@ -0,0 +1,25 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_tup_hot_upd,
+ n_live_tup,
+ n_dead_tup,
+ n_mod_since_analyze,
+ last_vacuum,
+ last_autovacuum,
+ last_analyze,
+ last_autoanalyze,
+ vacuum_count,
+ autovacuum_count,
+ analyze_count,
+ autoanalyze_count
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_statio_user_indexes.sql b/src/pgperf/94/pg_statio_user_indexes.sql
new file mode 100644
index 0000000..7f8bd33
--- /dev/null
+++ b/src/pgperf/94/pg_statio_user_indexes.sql
@@ -0,0 +1,10 @@
+SELECT 0::int as sid,
+ relid,
+ indexrelid,
+ schemaname,
+ relname,
+ indexrelname,
+ idx_blks_read,
+ idx_blks_hit
+ FROM pg_statio_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_statio_user_sequences.sql b/src/pgperf/94/pg_statio_user_sequences.sql
new file mode 100644
index 0000000..9cb8d27
--- /dev/null
+++ b/src/pgperf/94/pg_statio_user_sequences.sql
@@ -0,0 +1,8 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ blks_read,
+ blks_hit
+ FROM pg_statio_user_sequences
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_statio_user_tables.sql b/src/pgperf/94/pg_statio_user_tables.sql
new file mode 100644
index 0000000..ffc6adf
--- /dev/null
+++ b/src/pgperf/94/pg_statio_user_tables.sql
@@ -0,0 +1,14 @@
+SELECT 0::int as sid,
+ relid,
+ schemaname,
+ relname,
+ heap_blks_read,
+ heap_blks_hit,
+ idx_blks_read,
+ idx_blks_hit,
+ toast_blks_read,
+ toast_blks_hit,
+ tidx_blks_read,
+ tidx_blks_hit
+ FROM pg_statio_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema');
diff --git a/src/pgperf/94/pg_statistic.sql b/src/pgperf/94/pg_statistic.sql
new file mode 100644
index 0000000..7f8c328
--- /dev/null
+++ b/src/pgperf/94/pg_statistic.sql
@@ -0,0 +1,35 @@
+SELECT 0::int as sid,
+ s.starelid,
+ c.relname as starelname,
+ s.staattnum,
+ a.attname as staattname,
+ s.stainherit,
+ s.stanullfrac,
+ s.stawidth,
+ s.stadistinct,
+ s.stakind1,
+ s.stakind2,
+ s.stakind3,
+ s.stakind4,
+ s.stakind5,
+ s.staop1,
+ s.staop2,
+ s.staop3,
+ s.staop4,
+ s.staop5,
+ s.stanumbers1,
+ s.stanumbers2,
+ s.stanumbers3,
+ s.stanumbers4,
+ s.stanumbers5,
+ s.stavalues1::TEXT,
+ s.stavalues2::TEXT,
+ s.stavalues3::TEXT,
+ s.stavalues4::TEXT,
+ s.stavalues5::TEXT
+ FROM pg_statistic s, pg_class c, pg_namespace n, pg_attribute a
+ WHERE n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
+ AND n.oid = c.relnamespace
+ AND c.oid = s.starelid
+ AND a.attnum = s.staattnum
+ AND a.attrelid = s.starelid;
diff --git a/src/pgperf/94/pg_stats.sql b/src/pgperf/94/pg_stats.sql
new file mode 100644
index 0000000..aae20f9
--- /dev/null
+++ b/src/pgperf/94/pg_stats.sql
@@ -0,0 +1,17 @@
+SELECT 0::int AS sid,
+ schemaname,
+ tablename,
+ attname,
+ inherited,
+ null_frac,
+ avg_width,
+ n_distinct,
+ most_common_vals::text,
+ most_common_freqs,
+ histogram_bounds::text,
+ correlation,
+ most_common_elems::text,
+ most_common_elem_freqs,
+ elem_count_histogram
+ FROM pg_stats;
+
diff --git a/src/pgperf/94/pgstatindex.sql b/src/pgperf/94/pgstatindex.sql
new file mode 100644
index 0000000..021d0c2
--- /dev/null
+++ b/src/pgperf/94/pgstatindex.sql
@@ -0,0 +1,20 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ indexrelname,
+ (pgstatindex).version,
+ (pgstatindex).tree_level,
+ (pgstatindex).index_size,
+ (pgstatindex).root_block_no,
+ (pgstatindex).internal_pages,
+ (pgstatindex).leaf_pages,
+ (pgstatindex).empty_pages,
+ (pgstatindex).deleted_pages,
+ (pgstatindex).avg_leaf_density,
+ (pgstatindex).leaf_fragmentation
+ FROM ( SELECT schemaname,
+ relname,
+ indexrelname,
+ pgstatindex(schemaname || '.' || indexrelname)
+ FROM pg_stat_user_indexes
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/94/pgstattuple.sql b/src/pgperf/94/pgstattuple.sql
new file mode 100644
index 0000000..196132c
--- /dev/null
+++ b/src/pgperf/94/pgstattuple.sql
@@ -0,0 +1,17 @@
+SELECT 0::int as sid,
+ schemaname,
+ relname,
+ (pgstattuple).table_len,
+ (pgstattuple).tuple_count,
+ (pgstattuple).tuple_len,
+ (pgstattuple).tuple_percent,
+ (pgstattuple).dead_tuple_count,
+ (pgstattuple).dead_tuple_len,
+ (pgstattuple).dead_tuple_percent,
+ (pgstattuple).free_space,
+ (pgstattuple).free_percent
+ FROM ( SELECT schemaname,
+ relname,
+ pgstattuple(relid)
+ FROM pg_stat_user_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'pg_toast', 'information_schema') ) AS p;
diff --git a/src/pgperf/Makefile b/src/pgperf/Makefile
new file mode 100644
index 0000000..d6b6efd
--- /dev/null
+++ b/src/pgperf/Makefile
@@ -0,0 +1,26 @@
+TOPDIR=../..
+
+include $(TOPDIR)/Makefile.global
+
+all:
+ ./_build.sh
+
+check:
+ make -C t check
+
+clean:
+ rm -f pgperf_snapshot_install90.sql
+ rm -f pgperf_snapshot_install91.sql
+ rm -f pgperf_snapshot_install92.sql
+ rm -f pgperf_snapshot_install93.sql
+ rm -f pgperf_snapshot_install94.sql
+ rm -f *~
+ make -C t clean
+
+install:
+ mkdir -p $(PREFIX)/share
+ install -m 644 pgperf_snapshot_install90.sql $(PREFIX)/share
+ install -m 644 pgperf_snapshot_install91.sql $(PREFIX)/share
+ install -m 644 pgperf_snapshot_install92.sql $(PREFIX)/share
+ install -m 644 pgperf_snapshot_install93.sql $(PREFIX)/share
+ install -m 644 pgperf_snapshot_install94.sql $(PREFIX)/share
diff --git a/src/pgperf/README_DEV.txt b/src/pgperf/README_DEV.txt
new file mode 100644
index 0000000..3aea382
--- /dev/null
+++ b/src/pgperf/README_DEV.txt
@@ -0,0 +1,15 @@
+
+How to add a new snapshot table
+===============================
+
+ - Determine a SQL statement to retreive a snapshot.
+ - Create a SQL script file in 90~94 directories.
+ - Modify _build.sh to generate snapshot table and functions.
+ - Modify doc, pgperf_tables.rst.
+ - Add a new dedicated unit test, and add it to regress2.sh.
+ - Fix other tests.
+ - Modify create_snapshot(), delete_snapshot() function to call
+ the new functions.
+ - Fix the tests.
+
+EOF
diff --git a/src/pgperf/_build.sh b/src/pgperf/_build.sh
new file mode 100755
index 0000000..9f162dc
--- /dev/null
+++ b/src/pgperf/_build.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+function build90()
+{
+ cat _pgperf_create_role.sql
+
+ echo "BEGIN;"
+
+ cat _pgperf_snapshot_install_common.sql
+
+ cd 90
+ ../_pgperf.pl pg_stat_bgwriter
+ ../_pgperf.pl pg_database_size
+ ../_pgperf.pl pg_stat_database
+ ../_pgperf.pl pg_relation_size
+ ../_pgperf.pl pg_stat_user_tables
+ ../_pgperf.pl pg_statio_user_tables
+ ../_pgperf.pl pg_stat_user_indexes
+ ../_pgperf.pl pg_statio_user_indexes
+ ../_pgperf.pl pg_stat_user_functions
+ ../_pgperf.pl pg_statio_user_sequences
+ ../_pgperf.pl pg_current_xlog
+ ../_pgperf.pl pg_stat_activity
+ ../_pgperf.pl pg_locks
+ ../_pgperf.pl pg_stat_statements
+# ../_pgperf.pl pg_statistic
+ ../_pgperf.pl pg_stats
+ ../_pgperf.pl pgstattuple
+ ../_pgperf.pl pgstatindex
+ cd ..
+
+ echo "COMMIT;"
+}
+
+function build91()
+{
+ cat _pgperf_create_role.sql
+
+ echo "BEGIN;"
+
+ cat _pgperf_snapshot_install_common.sql
+
+ cd 91
+ ../_pgperf.pl pg_stat_bgwriter
+ ../_pgperf.pl pg_database_size
+ ../_pgperf.pl pg_stat_database
+ ../_pgperf.pl pg_relation_size
+ ../_pgperf.pl pg_stat_user_tables
+ ../_pgperf.pl pg_statio_user_tables
+ ../_pgperf.pl pg_stat_user_indexes
+ ../_pgperf.pl pg_statio_user_indexes
+ ../_pgperf.pl pg_stat_user_functions
+ ../_pgperf.pl pg_statio_user_sequences
+ ../_pgperf.pl pg_current_xlog
+ ../_pgperf.pl pg_stat_activity
+ ../_pgperf.pl pg_locks
+ ../_pgperf.pl pg_stat_replication
+ ../_pgperf.pl pg_stat_database_conflicts
+ ../_pgperf.pl pg_stat_statements
+# ../_pgperf.pl pg_statistic
+ ../_pgperf.pl pg_stats
+ ../_pgperf.pl pgstattuple
+ ../_pgperf.pl pgstatindex
+ cd ..
+
+ echo "COMMIT;"
+}
+
+function build92()
+{
+ cat _pgperf_create_role.sql
+
+ echo "BEGIN;"
+
+ cat _pgperf_snapshot_install_common.sql
+
+ cd 92
+ ../_pgperf.pl pg_stat_bgwriter
+ ../_pgperf.pl pg_database_size
+ ../_pgperf.pl pg_stat_database
+ ../_pgperf.pl pg_relation_size
+ ../_pgperf.pl pg_stat_user_tables
+ ../_pgperf.pl pg_statio_user_tables
+ ../_pgperf.pl pg_stat_user_indexes
+ ../_pgperf.pl pg_statio_user_indexes
+ ../_pgperf.pl pg_stat_user_functions
+ ../_pgperf.pl pg_statio_user_sequences
+ ../_pgperf.pl pg_current_xlog
+ ../_pgperf.pl pg_stat_activity
+ ../_pgperf.pl pg_locks
+ ../_pgperf.pl pg_stat_replication
+ ../_pgperf.pl pg_stat_database_conflicts
+ ../_pgperf.pl pg_stat_statements
+# ../_pgperf.pl pg_statistic
+ ../_pgperf.pl pg_stats
+ ../_pgperf.pl pgstattuple
+ ../_pgperf.pl pgstatindex
+ cd ..
+
+ echo "COMMIT;"
+}
+
+function build93()
+{
+ cat _pgperf_create_role.sql
+
+ echo "BEGIN;"
+
+ cat _pgperf_snapshot_install_common.sql
+
+ cd 93
+ ../_pgperf.pl pg_stat_bgwriter
+ ../_pgperf.pl pg_database_size
+ ../_pgperf.pl pg_stat_database
+ ../_pgperf.pl pg_relation_size
+ ../_pgperf.pl pg_stat_user_tables
+ ../_pgperf.pl pg_statio_user_tables
+ ../_pgperf.pl pg_stat_user_indexes
+ ../_pgperf.pl pg_statio_user_indexes
+ ../_pgperf.pl pg_stat_user_functions
+ ../_pgperf.pl pg_statio_user_sequences
+ ../_pgperf.pl pg_current_xlog
+ ../_pgperf.pl pg_stat_activity
+ ../_pgperf.pl pg_locks
+ ../_pgperf.pl pg_stat_replication
+ ../_pgperf.pl pg_stat_database_conflicts
+ ../_pgperf.pl pg_stat_statements
+# ../_pgperf.pl pg_statistic
+ ../_pgperf.pl pg_stats
+ ../_pgperf.pl pgstattuple
+ ../_pgperf.pl pgstatindex
+ cd ..
+
+ echo "COMMIT;"
+}
+
+function build94()
+{
+ cat _pgperf_create_role.sql
+
+ echo "BEGIN;"
+
+ cat _pgperf_snapshot_install_common.sql
+
+ cd 94
+ ../_pgperf.pl pg_stat_archiver
+ ../_pgperf.pl pg_stat_bgwriter
+ ../_pgperf.pl pg_database_size
+ ../_pgperf.pl pg_stat_database
+ ../_pgperf.pl pg_relation_size
+ ../_pgperf.pl pg_stat_user_tables
+ ../_pgperf.pl pg_statio_user_tables
+ ../_pgperf.pl pg_stat_user_indexes
+ ../_pgperf.pl pg_statio_user_indexes
+ ../_pgperf.pl pg_stat_user_functions
+ ../_pgperf.pl pg_statio_user_sequences
+ ../_pgperf.pl pg_current_xlog
+ ../_pgperf.pl pg_stat_activity
+ ../_pgperf.pl pg_locks
+ ../_pgperf.pl pg_stat_replication
+ ../_pgperf.pl pg_stat_database_conflicts
+ ../_pgperf.pl pg_stat_statements
+# ../_pgperf.pl pg_statistic
+ ../_pgperf.pl pg_stats
+ ../_pgperf.pl pgstattuple
+ ../_pgperf.pl pgstatindex
+ cd ..
+
+ echo "COMMIT;"
+}
+
+export LANG=C
+
+echo pgperf_snapshot_install90.sql
+build90 > pgperf_snapshot_install90.sql
+
+echo pgperf_snapshot_install91.sql
+build91 > pgperf_snapshot_install91.sql
+
+echo pgperf_snapshot_install92.sql
+build92 > pgperf_snapshot_install92.sql
+
+echo pgperf_snapshot_install93.sql
+build93 > pgperf_snapshot_install93.sql
+
+echo pgperf_snapshot_install94.sql
+build94 > pgperf_snapshot_install94.sql
+
diff --git a/src/pgperf/_pgperf.pl b/src/pgperf/_pgperf.pl
new file mode 100755
index 0000000..595bf45
--- /dev/null
+++ b/src/pgperf/_pgperf.pl
@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+
+use strict;
+
+my $table = shift;
+
+sub read_sql_file {
+ my $file = $_[0];
+
+ open(F, "$file") || die($!);
+
+ my $sql;
+ while(<F>)
+ {
+ $sql .= $_;
+ }
+
+ close(F);
+
+ $sql;
+}
+
+sub def_create_snapshot_table {
+ my $tab = $_[0];
+ my $sql = $_[1];
+
+ $sql =~ s/;//;
+ my $s =<<EOF;
+--
+-- CREATE TABLE pgperf.snapshot_${tab}
+--
+DROP TABLE IF EXISTS pgperf.snapshot_${tab};
+
+CREATE TABLE pgperf.snapshot_${tab} AS
+$sql
+LIMIT 0;
+
+ALTER TABLE pgperf.snapshot_${tab} ALTER COLUMN sid SET NOT NULL;
+ALTER TABLE pgperf.snapshot_${tab} OWNER TO pgperf;
+
+CREATE INDEX snapshot_${tab}_sid_idx ON pgperf.snapshot_${tab}(sid);
+
+-- \\d pgperf.snapshot_${tab}
+
+EOF
+
+ $s;
+}
+
+
+sub def_create_snapshot_func {
+ my $tab = $_[0];
+ my $sql = $_[1];
+
+ $sql =~ s/0::int as sid,/_sid,/i;
+ $sql =~ s/;//i;
+
+ my $s =<<EOF;
+--
+-- CREATE FUNCTION pgperf.create_snapshot_${tab} (INT)
+--
+CREATE OR REPLACE FUNCTION pgperf.create_snapshot_${tab} (INT)
+ RETURNS boolean AS
+\$\$
+DECLARE
+ _sid ALIAS FOR \$1;
+BEGIN
+ INSERT INTO pgperf.snapshot_${tab} (
+ $sql
+ );
+
+ RETURN true;
+END
+\$\$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.create_snapshot_${tab}(INT) OWNER TO pgperf;
+
+EOF
+
+ $s;
+}
+
+sub def_delete_snapshot_func {
+ my $tab = $_[0];
+ my $sql = $_[1];
+
+ my $s =<<EOF;
+--
+-- CREATE FUNCTION pgperf.delete_snapshot_${tab} (INT)
+--
+CREATE OR REPLACE FUNCTION pgperf.delete_snapshot_${tab} (INT)
+ RETURNS boolean AS
+\$\$
+DECLARE
+ _sid ALIAS FOR \$1;
+BEGIN
+ DELETE FROM pgperf.snapshot_${tab} WHERE sid = _sid;
+ RETURN true;
+END
+\$\$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.delete_snapshot_${tab}(INT) OWNER TO pgperf;
+
+EOF
+
+ $s;
+}
+
+sub def_test_snapshot {
+ my $tab = $_[0];
+ my $sql = $_[1];
+
+ my $s =<<EOF;
+--
+-- TEST pgperf.snapshot_${tab}
+--
+/*
+SELECT pgperf.create_snapshot_${tab}(1);
+SELECT count(*) FROM pgperf.snapshot_${tab};
+SELECT * FROM pgperf.snapshot_${tab};
+SELECT pgperf.delete_snapshot_${tab}(1);
+SELECT count(*) FROM pgperf.snapshot_${tab};
+SELECT * FROM pgperf.snapshot_${tab};
+SELECT pgperf.create_snapshot_${tab}(1);
+*/
+
+EOF
+
+ $s;
+}
+
+my $sql = &read_sql_file($table . ".sql");
+
+my $sql2 = def_create_snapshot_table($table, $sql);
+my $sql3 = def_create_snapshot_func($table, $sql);
+my $sql4 = def_delete_snapshot_func($table, $sql);
+#my $sql5 = def_test_snapshot($table, $sql);
+
+#print $sql;
+
+print $sql2;
+print $sql3;
+print $sql4;
+#print $sql5;
+
diff --git a/src/pgperf/_pgperf_create_role.sql b/src/pgperf/_pgperf_create_role.sql
new file mode 100644
index 0000000..3af0f70
--- /dev/null
+++ b/src/pgperf/_pgperf_create_role.sql
@@ -0,0 +1,2 @@
+DROP USER pgperf;
+CREATE USER pgperf NOSUPERUSER NOINHERIT LOGIN CONNECTION LIMIT 5;
diff --git a/src/pgperf/_pgperf_snapshot_install_common.sql b/src/pgperf/_pgperf_snapshot_install_common.sql
new file mode 100644
index 0000000..a84f8d3
--- /dev/null
+++ b/src/pgperf/_pgperf_snapshot_install_common.sql
@@ -0,0 +1,284 @@
+--
+-- pgperf snapshot package
+--
+-- Copyright(C) 2012-2015 Uptime Technologies, LLC.
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; version 2 of the License.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along
+-- with this program; if not, write to the Free Software Foundation, Inc.,
+-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+--
+CREATE SCHEMA pgperf;
+ALTER SCHEMA pgperf OWNER TO pgperf;
+
+--
+-- pgperf stat tables
+--
+CREATE TABLE pgperf.snapshot (
+ sid INTEGER PRIMARY KEY,
+ ts TIMESTAMP NOT NULL,
+ level INTEGER NOT NULL
+);
+
+CREATE INDEX snapshot_ts_idx on pgperf.snapshot(ts);
+
+ALTER TABLE pgperf.snapshot OWNER TO pgperf;
+
+--
+-- Get a major version of the PostgreSQL server.
+--
+CREATE OR REPLACE FUNCTION pgperf._get_server_version (
+) RETURNS INTEGER AS
+$$
+DECLARE
+ _version INTEGER;
+BEGIN
+ SELECT substr(replace(setting, '.', ''), 1, 2)::integer INTO _version
+ FROM pg_settings
+ WHERE name = 'server_version';
+
+ IF _version < 90 THEN
+ RAISE EXCEPTION 'Unsupported PostgreSQL version: %', _version;
+ END IF;
+
+ RETURN _version;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf._get_server_version() OWNER TO pgperf;
+
+--
+-- Check if a function exists.
+--
+CREATE OR REPLACE FUNCTION pgperf._check_function (
+ NAME
+) RETURNS BOOLEAN AS
+$$
+DECLARE
+ _name ALIAS FOR $1;
+ _found BOOLEAN;
+BEGIN
+ SELECT CASE WHEN count(*)>0 THEN true ELSE false END INTO _found
+ FROM pg_proc
+ WHERE proname = _name;
+
+ RETURN _found;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf._check_function(NAME) OWNER TO pgperf;
+
+--
+-- Check if a table or a view exists.
+--
+CREATE OR REPLACE FUNCTION pgperf._check_table_or_view (
+ NAME
+) RETURNS BOOLEAN AS
+$$
+DECLARE
+ _name ALIAS FOR $1;
+ _found BOOLEAN;
+BEGIN
+ SELECT CASE WHEN count(*)>0 THEN true ELSE false END INTO _found
+ FROM pg_class
+ WHERE relname = _name
+ AND (relkind = 'r' OR relkind = 'v');
+
+ RETURN _found;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf._check_table_or_view(NAME) OWNER TO pgperf;
+
+--
+-- _is_super()
+--
+CREATE OR REPLACE FUNCTION pgperf._is_super (
+) RETURNS boolean AS
+$$
+DECLARE
+ _is_super BOOLEAN;
+BEGIN
+ SELECT usesuper INTO _is_super FROM pg_user WHERE usename = CURRENT_USER;
+ RETURN _is_super;
+END
+$$
+LANGUAGE 'plpgsql';
+
+--
+-- Create a snapshot.
+--
+CREATE OR REPLACE FUNCTION pgperf.create_snapshot (
+ INTEGER
+) RETURNS integer AS
+$$
+DECLARE
+ _level ALIAS FOR $1;
+ _sid INTEGER;
+ _version INTEGER;
+ _is_super BOOLEAN;
+ _has_pg_stat_statements BOOLEAN;
+ _has_pgstattuple BOOLEAN;
+BEGIN
+ SELECT pgperf._is_super() INTO _is_super;
+ SELECT pgperf._check_table_or_view('pg_stat_statements') INTO _has_pg_stat_statements;
+ SELECT pgperf._check_function('pgstattuple') INTO _has_pgstattuple;
+ SELECT pgperf._get_server_version() INTO _version;
+
+ SELECT max(sid) INTO _sid FROM pgperf.snapshot;
+ IF _sid IS NULL THEN
+ _sid := 0;
+ ELSE
+ _sid = _sid + 1;
+ END IF;
+
+ INSERT INTO pgperf.snapshot (sid,ts,level) VALUES (_sid, now(), _level);
+
+ PERFORM pgperf.create_snapshot_pg_database_size(_sid);
+ PERFORM pgperf.create_snapshot_pg_relation_size(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_bgwriter(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_database(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_user_tables(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_user_indexes(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_user_functions(_sid);
+ PERFORM pgperf.create_snapshot_pg_statio_user_tables(_sid);
+ PERFORM pgperf.create_snapshot_pg_statio_user_indexes(_sid);
+ PERFORM pgperf.create_snapshot_pg_statio_user_sequences(_sid);
+ PERFORM pgperf.create_snapshot_pg_current_xlog(_sid);
+ PERFORM pgperf.create_snapshot_pg_stat_activity(_sid);
+ PERFORM pgperf.create_snapshot_pg_locks(_sid);
+ IF _version >= 94 THEN
+ PERFORM pgperf.create_snapshot_pg_stat_archiver(_sid);
+ END IF;
+
+ IF _has_pg_stat_statements = true THEN
+ PERFORM pgperf.create_snapshot_pg_stat_statements(_sid);
+ END IF;
+
+ IF _level >= 2 THEN
+ -- PERFORM pgperf.create_snapshot_pg_statistic(_sid);
+ PERFORM pgperf.create_snapshot_pg_stats(_sid);
+ END IF;
+
+ IF _level >= 4 AND _has_pgstattuple THEN
+ IF _is_super = true THEN
+ PERFORM pgperf.create_snapshot_pgstattuple(_sid);
+ PERFORM pgperf.create_snapshot_pgstatindex(_sid);
+ ELSE
+ RAISE WARNING 'pgperf.create_snapshot: Cannot take pgstattuple/pgstatindex snapshot without super-user permission.'
+ USING HINT = 'Check the permission for user "' || current_user || '".';
+ END IF;
+ END IF;
+
+ RETURN _sid;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.create_snapshot(INTEGER) OWNER TO pgperf;
+
+--
+-- Delete a snapshot.
+--
+CREATE OR REPLACE FUNCTION pgperf.delete_snapshot (
+ INTEGER
+) RETURNS boolean AS
+$$
+DECLARE
+ _sid ALIAS FOR $1;
+ _version INTEGER;
+BEGIN
+ SELECT pgperf._get_server_version() INTO _version;
+
+ PERFORM pgperf.delete_snapshot_pg_database_size(_sid);
+ PERFORM pgperf.delete_snapshot_pg_relation_size(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_bgwriter(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_database(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_user_tables(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_user_indexes(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_user_functions(_sid);
+ PERFORM pgperf.delete_snapshot_pg_statio_user_tables(_sid);
+ PERFORM pgperf.delete_snapshot_pg_statio_user_indexes(_sid);
+ PERFORM pgperf.delete_snapshot_pg_statio_user_sequences(_sid);
+ PERFORM pgperf.delete_snapshot_pg_current_xlog(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stat_activity(_sid);
+ PERFORM pgperf.delete_snapshot_pg_locks(_sid);
+ IF _version >= 94 THEN
+ PERFORM pgperf.delete_snapshot_pg_stat_archiver(_sid);
+ END IF;
+
+ PERFORM pgperf.delete_snapshot_pg_stat_statements(_sid);
+-- PERFORM pgperf.delete_snapshot_pg_statistic(_sid);
+ PERFORM pgperf.delete_snapshot_pg_stats(_sid);
+ PERFORM pgperf.delete_snapshot_pgstattuple(_sid);
+ PERFORM pgperf.delete_snapshot_pgstatindex(_sid);
+
+ DELETE FROM pgperf.snapshot WHERE sid = _sid;
+
+ RETURN true;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.delete_snapshot(INTEGER) OWNER TO pgperf;
+
+--
+-- Purge old snapshots at once.
+--
+CREATE OR REPLACE FUNCTION pgperf.purge_snapshots (
+ INTERVAL
+) RETURNS INTEGER AS
+$$
+DECLARE
+ _interval ALIAS FOR $1;
+ _count INTEGER;
+BEGIN
+ SELECT count(*) INTO _count
+ FROM pgperf.snapshot
+ WHERE ts < now() - _interval::interval;
+
+ PERFORM pgperf.delete_snapshot(sid)
+ FROM pgperf.snapshot
+ WHERE ts < now() - _interval::interval;
+
+ RETURN _count;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.purge_snapshots(INTERVAL) OWNER TO pgperf;
+
+--
+-- Get an interval between snaphsots in seconds.
+--
+CREATE OR REPLACE FUNCTION pgperf.get_interval (
+ INTEGER,
+ INTEGER
+) RETURNS INTEGER AS
+$$
+DECLARE
+ _sid1 ALIAS FOR $1;
+ _sid2 ALIAS FOR $2;
+ _interval INTEGER;
+BEGIN
+ SELECT extract(EPOCH FROM (s2.ts - s1.ts))::INTEGER INTO _interval
+ FROM (SELECT ts FROM pgperf.snapshot WHERE sid=_sid1 ) AS s1,
+ (SELECT ts FROM pgperf.snapshot WHERE sid=_sid2 ) AS s2;
+
+ RETURN _interval;
+END
+$$
+LANGUAGE 'plpgsql';
+
+ALTER FUNCTION pgperf.get_interval(INTEGER,INTEGER) OWNER TO pgperf;
diff --git a/src/pgperf/t/001_schema/expected/test.out b/src/pgperf/t/001_schema/expected/test.out
new file mode 100644
index 0000000..697097c
--- /dev/null
+++ b/src/pgperf/t/001_schema/expected/test.out
@@ -0,0 +1,46 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+ nspname
+---------
+ pgperf
+(1 row)
+
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of relations
+Schema | Name | Type | Owner
+pgperf | snapshot | table | pgperf
+pgperf | snapshot_pg_current_xlog | table | pgperf
+pgperf | snapshot_pg_database_size | table | pgperf
+pgperf | snapshot_pg_locks | table | pgperf
+pgperf | snapshot_pg_relation_size | table | pgperf
+pgperf | snapshot_pg_stat_activity | table | pgperf
+pgperf | snapshot_pg_stat_archiver | table | pgperf
+pgperf | snapshot_pg_stat_bgwriter | table | pgperf
+pgperf | snapshot_pg_stat_database | table | pgperf
+pgperf | snapshot_pg_stat_database_conflicts | table | pgperf
+pgperf | snapshot_pg_stat_replication | table | pgperf
+pgperf | snapshot_pg_stat_statements | table | pgperf
+pgperf | snapshot_pg_stat_user_functions | table | pgperf
+pgperf | snapshot_pg_stat_user_indexes | table | pgperf
+pgperf | snapshot_pg_stat_user_tables | table | pgperf
+pgperf | snapshot_pg_statio_user_indexes | table | pgperf
+pgperf | snapshot_pg_statio_user_sequences | table | pgperf
+pgperf | snapshot_pg_statio_user_tables | table | pgperf
+pgperf | snapshot_pg_stats | table | pgperf
+pgperf | snapshot_pgstatindex | table | pgperf
+pgperf | snapshot_pgstattuple | table | pgperf
+(21 rows)
+Output format is aligned.
+Field separator is " | ".
+ Table "pgperf.snapshot"
+ Column | Type | Modifiers
+--------+-----------------------------+-----------
+ sid | integer | not null
+ ts | timestamp without time zone | not null
+ level | integer | not null
+Indexes:
+ "snapshot_pkey" PRIMARY KEY, btree (sid)
+ "snapshot_ts_idx" btree (ts)
+
diff --git a/src/pgperf/t/001_schema/expected/test90.out b/src/pgperf/t/001_schema/expected/test90.out
new file mode 100644
index 0000000..8bf13a3
--- /dev/null
+++ b/src/pgperf/t/001_schema/expected/test90.out
@@ -0,0 +1,43 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+ nspname
+---------
+ pgperf
+(1 row)
+
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of relations
+Schema | Name | Type | Owner
+pgperf | snapshot | table | pgperf
+pgperf | snapshot_pg_current_xlog | table | pgperf
+pgperf | snapshot_pg_database_size | table | pgperf
+pgperf | snapshot_pg_locks | table | pgperf
+pgperf | snapshot_pg_relation_size | table | pgperf
+pgperf | snapshot_pg_stat_activity | table | pgperf
+pgperf | snapshot_pg_stat_bgwriter | table | pgperf
+pgperf | snapshot_pg_stat_database | table | pgperf
+pgperf | snapshot_pg_stat_statements | table | pgperf
+pgperf | snapshot_pg_stat_user_functions | table | pgperf
+pgperf | snapshot_pg_stat_user_indexes | table | pgperf
+pgperf | snapshot_pg_stat_user_tables | table | pgperf
+pgperf | snapshot_pg_statio_user_indexes | table | pgperf
+pgperf | snapshot_pg_statio_user_sequences | table | pgperf
+pgperf | snapshot_pg_statio_user_tables | table | pgperf
+pgperf | snapshot_pg_stats | table | pgperf
+pgperf | snapshot_pgstatindex | table | pgperf
+pgperf | snapshot_pgstattuple | table | pgperf
+(18 rows)
+Output format is aligned.
+Field separator is " | ".
+ Table "pgperf.snapshot"
+ Column | Type | Modifiers
+--------+-----------------------------+-----------
+ sid | integer | not null
+ ts | timestamp without time zone | not null
+ level | integer | not null
+Indexes:
+ "snapshot_pkey" PRIMARY KEY, btree (sid)
+ "snapshot_ts_idx" btree (ts)
+
diff --git a/src/pgperf/t/001_schema/expected/test91.out b/src/pgperf/t/001_schema/expected/test91.out
new file mode 100644
index 0000000..e72bb13
--- /dev/null
+++ b/src/pgperf/t/001_schema/expected/test91.out
@@ -0,0 +1,45 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+ nspname
+---------
+ pgperf
+(1 row)
+
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of relations
+Schema | Name | Type | Owner
+pgperf | snapshot | table | pgperf
+pgperf | snapshot_pg_current_xlog | table | pgperf
+pgperf | snapshot_pg_database_size | table | pgperf
+pgperf | snapshot_pg_locks | table | pgperf
+pgperf | snapshot_pg_relation_size | table | pgperf
+pgperf | snapshot_pg_stat_activity | table | pgperf
+pgperf | snapshot_pg_stat_bgwriter | table | pgperf
+pgperf | snapshot_pg_stat_database | table | pgperf
+pgperf | snapshot_pg_stat_database_conflicts | table | pgperf
+pgperf | snapshot_pg_stat_replication | table | pgperf
+pgperf | snapshot_pg_stat_statements | table | pgperf
+pgperf | snapshot_pg_stat_user_functions | table | pgperf
+pgperf | snapshot_pg_stat_user_indexes | table | pgperf
+pgperf | snapshot_pg_stat_user_tables | table | pgperf
+pgperf | snapshot_pg_statio_user_indexes | table | pgperf
+pgperf | snapshot_pg_statio_user_sequences | table | pgperf
+pgperf | snapshot_pg_statio_user_tables | table | pgperf
+pgperf | snapshot_pg_stats | table | pgperf
+pgperf | snapshot_pgstatindex | table | pgperf
+pgperf | snapshot_pgstattuple | table | pgperf
+(20 rows)
+Output format is aligned.
+Field separator is " | ".
+ Table "pgperf.snapshot"
+ Column | Type | Modifiers
+--------+-----------------------------+-----------
+ sid | integer | not null
+ ts | timestamp without time zone | not null
+ level | integer | not null
+Indexes:
+ "snapshot_pkey" PRIMARY KEY, btree (sid)
+ "snapshot_ts_idx" btree (ts)
+
diff --git a/src/pgperf/t/001_schema/expected/test92.out b/src/pgperf/t/001_schema/expected/test92.out
new file mode 100644
index 0000000..e72bb13
--- /dev/null
+++ b/src/pgperf/t/001_schema/expected/test92.out
@@ -0,0 +1,45 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+ nspname
+---------
+ pgperf
+(1 row)
+
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of relations
+Schema | Name | Type | Owner
+pgperf | snapshot | table | pgperf
+pgperf | snapshot_pg_current_xlog | table | pgperf
+pgperf | snapshot_pg_database_size | table | pgperf
+pgperf | snapshot_pg_locks | table | pgperf
+pgperf | snapshot_pg_relation_size | table | pgperf
+pgperf | snapshot_pg_stat_activity | table | pgperf
+pgperf | snapshot_pg_stat_bgwriter | table | pgperf
+pgperf | snapshot_pg_stat_database | table | pgperf
+pgperf | snapshot_pg_stat_database_conflicts | table | pgperf
+pgperf | snapshot_pg_stat_replication | table | pgperf
+pgperf | snapshot_pg_stat_statements | table | pgperf
+pgperf | snapshot_pg_stat_user_functions | table | pgperf
+pgperf | snapshot_pg_stat_user_indexes | table | pgperf
+pgperf | snapshot_pg_stat_user_tables | table | pgperf
+pgperf | snapshot_pg_statio_user_indexes | table | pgperf
+pgperf | snapshot_pg_statio_user_sequences | table | pgperf
+pgperf | snapshot_pg_statio_user_tables | table | pgperf
+pgperf | snapshot_pg_stats | table | pgperf
+pgperf | snapshot_pgstatindex | table | pgperf
+pgperf | snapshot_pgstattuple | table | pgperf
+(20 rows)
+Output format is aligned.
+Field separator is " | ".
+ Table "pgperf.snapshot"
+ Column | Type | Modifiers
+--------+-----------------------------+-----------
+ sid | integer | not null
+ ts | timestamp without time zone | not null
+ level | integer | not null
+Indexes:
+ "snapshot_pkey" PRIMARY KEY, btree (sid)
+ "snapshot_ts_idx" btree (ts)
+
diff --git a/src/pgperf/t/001_schema/expected/test93.out b/src/pgperf/t/001_schema/expected/test93.out
new file mode 100644
index 0000000..e72bb13
--- /dev/null
+++ b/src/pgperf/t/001_schema/expected/test93.out
@@ -0,0 +1,45 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+ nspname
+---------
+ pgperf
+(1 row)
+
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of relations
+Schema | Name | Type | Owner
+pgperf | snapshot | table | pgperf
+pgperf | snapshot_pg_current_xlog | table | pgperf
+pgperf | snapshot_pg_database_size | table | pgperf
+pgperf | snapshot_pg_locks | table | pgperf
+pgperf | snapshot_pg_relation_size | table | pgperf
+pgperf | snapshot_pg_stat_activity | table | pgperf
+pgperf | snapshot_pg_stat_bgwriter | table | pgperf
+pgperf | snapshot_pg_stat_database | table | pgperf
+pgperf | snapshot_pg_stat_database_conflicts | table | pgperf
+pgperf | snapshot_pg_stat_replication | table | pgperf
+pgperf | snapshot_pg_stat_statements | table | pgperf
+pgperf | snapshot_pg_stat_user_functions | table | pgperf
+pgperf | snapshot_pg_stat_user_indexes | table | pgperf
+pgperf | snapshot_pg_stat_user_tables | table | pgperf
+pgperf | snapshot_pg_statio_user_indexes | table | pgperf
+pgperf | snapshot_pg_statio_user_sequences | table | pgperf
+pgperf | snapshot_pg_statio_user_tables | table | pgperf
+pgperf | snapshot_pg_stats | table | pgperf
+pgperf | snapshot_pgstatindex | table | pgperf
+pgperf | snapshot_pgstattuple | table | pgperf
+(20 rows)
+Output format is aligned.
+Field separator is " | ".
+ Table "pgperf.snapshot"
+ Column | Type | Modifiers
+--------+-----------------------------+-----------
+ sid | integer | not null
+ ts | timestamp without time zone | not null
+ level | integer | not null
+Indexes:
+ "snapshot_pkey" PRIMARY KEY, btree (sid)
+ "snapshot_ts_idx" btree (ts)
+
diff --git a/src/pgperf/t/001_schema/input/test.sql b/src/pgperf/t/001_schema/input/test.sql
new file mode 100644
index 0000000..e6d6cd9
--- /dev/null
+++ b/src/pgperf/t/001_schema/input/test.sql
@@ -0,0 +1,14 @@
+select nspname from pg_namespace where nspname = 'pgperf';
+
+set search_path to pgperf;
+
+\a
+\f ' | '
+
+\d
+
+\a
+\f ' | '
+
+\d pgperf.snapshot
+
diff --git a/src/pgperf/t/002_procs/expected/test.out b/src/pgperf/t/002_procs/expected/test.out
new file mode 100644
index 0000000..204d630
--- /dev/null
+++ b/src/pgperf/t/002_procs/expected/test.out
@@ -0,0 +1,55 @@
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of functions
+Schema | Name | Result data type | Argument data types | Type
+pgperf | _check_function | boolean | name | normal
+pgperf | _check_table_or_view | boolean | name | normal
+pgperf | _get_server_version | integer | | normal
+pgperf | _is_super | boolean | | normal
+pgperf | create_snapshot | integer | integer | normal
+pgperf | create_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | create_snapshot_pg_database_size | boolean | integer | normal
+pgperf | create_snapshot_pg_locks | boolean | integer | normal
+pgperf | create_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_archiver | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_stats | boolean | integer | normal
+pgperf | create_snapshot_pgstatindex | boolean | integer | normal
+pgperf | create_snapshot_pgstattuple | boolean | integer | normal
+pgperf | delete_snapshot | boolean | integer | normal
+pgperf | delete_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | delete_snapshot_pg_database_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_locks | boolean | integer | normal
+pgperf | delete_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_archiver | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_stats | boolean | integer | normal
+pgperf | delete_snapshot_pgstatindex | boolean | integer | normal
+pgperf | delete_snapshot_pgstattuple | boolean | integer | normal
+pgperf | get_interval | integer | integer, integer | normal
+pgperf | purge_snapshots | integer | interval | normal
+(48 rows)
diff --git a/src/pgperf/t/002_procs/expected/test90.out b/src/pgperf/t/002_procs/expected/test90.out
new file mode 100644
index 0000000..135fe53
--- /dev/null
+++ b/src/pgperf/t/002_procs/expected/test90.out
@@ -0,0 +1,49 @@
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of functions
+Schema | Name | Result data type | Argument data types | Type
+pgperf | _check_function | boolean | name | normal
+pgperf | _check_table_or_view | boolean | name | normal
+pgperf | _get_server_version | integer | | normal
+pgperf | _is_super | boolean | | normal
+pgperf | create_snapshot | integer | integer | normal
+pgperf | create_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | create_snapshot_pg_database_size | boolean | integer | normal
+pgperf | create_snapshot_pg_locks | boolean | integer | normal
+pgperf | create_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_stats | boolean | integer | normal
+pgperf | create_snapshot_pgstatindex | boolean | integer | normal
+pgperf | create_snapshot_pgstattuple | boolean | integer | normal
+pgperf | delete_snapshot | boolean | integer | normal
+pgperf | delete_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | delete_snapshot_pg_database_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_locks | boolean | integer | normal
+pgperf | delete_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_stats | boolean | integer | normal
+pgperf | delete_snapshot_pgstatindex | boolean | integer | normal
+pgperf | delete_snapshot_pgstattuple | boolean | integer | normal
+pgperf | get_interval | integer | integer, integer | normal
+pgperf | purge_snapshots | integer | interval | normal
+(42 rows)
diff --git a/src/pgperf/t/002_procs/expected/test91.out b/src/pgperf/t/002_procs/expected/test91.out
new file mode 100644
index 0000000..720ba55
--- /dev/null
+++ b/src/pgperf/t/002_procs/expected/test91.out
@@ -0,0 +1,53 @@
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of functions
+Schema | Name | Result data type | Argument data types | Type
+pgperf | _check_function | boolean | name | normal
+pgperf | _check_table_or_view | boolean | name | normal
+pgperf | _get_server_version | integer | | normal
+pgperf | _is_super | boolean | | normal
+pgperf | create_snapshot | integer | integer | normal
+pgperf | create_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | create_snapshot_pg_database_size | boolean | integer | normal
+pgperf | create_snapshot_pg_locks | boolean | integer | normal
+pgperf | create_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_stats | boolean | integer | normal
+pgperf | create_snapshot_pgstatindex | boolean | integer | normal
+pgperf | create_snapshot_pgstattuple | boolean | integer | normal
+pgperf | delete_snapshot | boolean | integer | normal
+pgperf | delete_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | delete_snapshot_pg_database_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_locks | boolean | integer | normal
+pgperf | delete_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_stats | boolean | integer | normal
+pgperf | delete_snapshot_pgstatindex | boolean | integer | normal
+pgperf | delete_snapshot_pgstattuple | boolean | integer | normal
+pgperf | get_interval | integer | integer, integer | normal
+pgperf | purge_snapshots | integer | interval | normal
+(46 rows)
diff --git a/src/pgperf/t/002_procs/expected/test92.out b/src/pgperf/t/002_procs/expected/test92.out
new file mode 100644
index 0000000..720ba55
--- /dev/null
+++ b/src/pgperf/t/002_procs/expected/test92.out
@@ -0,0 +1,53 @@
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of functions
+Schema | Name | Result data type | Argument data types | Type
+pgperf | _check_function | boolean | name | normal
+pgperf | _check_table_or_view | boolean | name | normal
+pgperf | _get_server_version | integer | | normal
+pgperf | _is_super | boolean | | normal
+pgperf | create_snapshot | integer | integer | normal
+pgperf | create_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | create_snapshot_pg_database_size | boolean | integer | normal
+pgperf | create_snapshot_pg_locks | boolean | integer | normal
+pgperf | create_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_stats | boolean | integer | normal
+pgperf | create_snapshot_pgstatindex | boolean | integer | normal
+pgperf | create_snapshot_pgstattuple | boolean | integer | normal
+pgperf | delete_snapshot | boolean | integer | normal
+pgperf | delete_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | delete_snapshot_pg_database_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_locks | boolean | integer | normal
+pgperf | delete_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_stats | boolean | integer | normal
+pgperf | delete_snapshot_pgstatindex | boolean | integer | normal
+pgperf | delete_snapshot_pgstattuple | boolean | integer | normal
+pgperf | get_interval | integer | integer, integer | normal
+pgperf | purge_snapshots | integer | interval | normal
+(46 rows)
diff --git a/src/pgperf/t/002_procs/expected/test93.out b/src/pgperf/t/002_procs/expected/test93.out
new file mode 100644
index 0000000..720ba55
--- /dev/null
+++ b/src/pgperf/t/002_procs/expected/test93.out
@@ -0,0 +1,53 @@
+set search_path to pgperf;
+SET
+Output format is unaligned.
+Field separator is " | ".
+List of functions
+Schema | Name | Result data type | Argument data types | Type
+pgperf | _check_function | boolean | name | normal
+pgperf | _check_table_or_view | boolean | name | normal
+pgperf | _get_server_version | integer | | normal
+pgperf | _is_super | boolean | | normal
+pgperf | create_snapshot | integer | integer | normal
+pgperf | create_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | create_snapshot_pg_database_size | boolean | integer | normal
+pgperf | create_snapshot_pg_locks | boolean | integer | normal
+pgperf | create_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | create_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | create_snapshot_pg_stats | boolean | integer | normal
+pgperf | create_snapshot_pgstatindex | boolean | integer | normal
+pgperf | create_snapshot_pgstattuple | boolean | integer | normal
+pgperf | delete_snapshot | boolean | integer | normal
+pgperf | delete_snapshot_pg_current_xlog | boolean | integer | normal
+pgperf | delete_snapshot_pg_database_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_locks | boolean | integer | normal
+pgperf | delete_snapshot_pg_relation_size | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_activity | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_bgwriter | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_database_conflicts | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_replication | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_statements | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_functions | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_stat_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_indexes | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_sequences | boolean | integer | normal
+pgperf | delete_snapshot_pg_statio_user_tables | boolean | integer | normal
+pgperf | delete_snapshot_pg_stats | boolean | integer | normal
+pgperf | delete_snapshot_pgstatindex | boolean | integer | normal
+pgperf | delete_snapshot_pgstattuple | boolean | integer | normal
+pgperf | get_interval | integer | integer, integer | normal
+pgperf | purge_snapshots | integer | interval | normal
+(46 rows)
diff --git a/src/pgperf/t/002_procs/input/test.sql b/src/pgperf/t/002_procs/input/test.sql
new file mode 100644
index 0000000..668f5a6
--- /dev/null
+++ b/src/pgperf/t/002_procs/input/test.sql
@@ -0,0 +1,6 @@
+set search_path to pgperf;
+
+\a
+\f ' | '
+
+\df
diff --git a/src/pgperf/t/003_get_server_version/expected/test.out b/src/pgperf/t/003_get_server_version/expected/test.out
new file mode 100644
index 0000000..540aa25
--- /dev/null
+++ b/src/pgperf/t/003_get_server_version/expected/test.out
@@ -0,0 +1,24 @@
+SELECT pgperf._get_server_version() >= 90;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf._get_server_version() <= 94;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf._get_server_version() < 90;
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT pgperf._get_server_version() > 94;
+ ?column?
+----------
+ f
+(1 row)
+
diff --git a/src/pgperf/t/003_get_server_version/input/test.sql b/src/pgperf/t/003_get_server_version/input/test.sql
new file mode 100644
index 0000000..48f2d97
--- /dev/null
+++ b/src/pgperf/t/003_get_server_version/input/test.sql
@@ -0,0 +1,6 @@
+-- 9.0 to 9.4 is supported.
+SELECT pgperf._get_server_version() >= 90;
+SELECT pgperf._get_server_version() <= 94;
+
+SELECT pgperf._get_server_version() < 90;
+SELECT pgperf._get_server_version() > 94;
diff --git a/src/pgperf/t/004_check_function/expected/test.out b/src/pgperf/t/004_check_function/expected/test.out
new file mode 100644
index 0000000..8db309a
--- /dev/null
+++ b/src/pgperf/t/004_check_function/expected/test.out
@@ -0,0 +1,12 @@
+SELECT pgperf._check_function('_check_function');
+ _check_function
+-----------------
+ t
+(1 row)
+
+SELECT pgperf._check_function('nosuchfunction');
+ _check_function
+-----------------
+ f
+(1 row)
+
diff --git a/src/pgperf/t/004_check_function/input/test.sql b/src/pgperf/t/004_check_function/input/test.sql
new file mode 100644
index 0000000..426f97f
--- /dev/null
+++ b/src/pgperf/t/004_check_function/input/test.sql
@@ -0,0 +1,3 @@
+SELECT pgperf._check_function('_check_function');
+
+SELECT pgperf._check_function('nosuchfunction');
diff --git a/src/pgperf/t/005_check_table_or_view/expected/test.out b/src/pgperf/t/005_check_table_or_view/expected/test.out
new file mode 100644
index 0000000..f7b9840
--- /dev/null
+++ b/src/pgperf/t/005_check_table_or_view/expected/test.out
@@ -0,0 +1,18 @@
+SELECT pgperf._check_table_or_view('pg_database');
+ _check_table_or_view
+----------------------
+ t
+(1 row)
+
+SELECT pgperf._check_table_or_view('pg_stat_activity');
+ _check_table_or_view
+----------------------
+ t
+(1 row)
+
+SELECT pgperf._check_table_or_view('nosuchtableorview');
+ _check_table_or_view
+----------------------
+ f
+(1 row)
+
diff --git a/src/pgperf/t/005_check_table_or_view/input/test.sql b/src/pgperf/t/005_check_table_or_view/input/test.sql
new file mode 100644
index 0000000..e08961a
--- /dev/null
+++ b/src/pgperf/t/005_check_table_or_view/input/test.sql
@@ -0,0 +1,6 @@
+-- table
+SELECT pgperf._check_table_or_view('pg_database');
+-- view
+SELECT pgperf._check_table_or_view('pg_stat_activity');
+
+SELECT pgperf._check_table_or_view('nosuchtableorview');
diff --git a/src/pgperf/t/101_create_snapshot_pg_current_xlog/expected/test.out b/src/pgperf/t/101_create_snapshot_pg_current_xlog/expected/test.out
new file mode 100644
index 0000000..c728644
--- /dev/null
+++ b/src/pgperf/t/101_create_snapshot_pg_current_xlog/expected/test.out
@@ -0,0 +1,60 @@
+SELECT * FROM pgperf.snapshot_pg_current_xlog ORDER BY 1;
+ sid | location | insert_location
+-----+----------+-----------------
+(0 rows)
+
+SELECT pgperf.create_snapshot_pg_current_xlog(0);
+ create_snapshot_pg_current_xlog
+---------------------------------
+ t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_current_xlog(1);
+ create_snapshot_pg_current_xlog
+---------------------------------
+ t
+(1 row)
+
+WITH s0 AS (
+SELECT * FROM pgperf.snapshot_pg_current_xlog WHERE sid = 0
+), s1 AS (
+SELECT * FROM pgperf.snapshot_pg_current_xlog WHERE sid = 1
+)
+SELECT s0.location < s1.location, s0.insert_location < s1.insert_location
+ FROM s0, s1
+ WHERE s0.sid + 1 = s1.sid;
+ ?column? | ?column?
+----------+----------
+ t | t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_current_xlog(1);
+ delete_snapshot_pg_current_xlog
+---------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_current_xlog(0);
+ delete_snapshot_pg_current_xlog
+---------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/101_create_snapshot_pg_current_xlog/input/test.sql b/src/pgperf/t/101_create_snapshot_pg_current_xlog/input/test.sql
new file mode 100644
index 0000000..e5e764f
--- /dev/null
+++ b/src/pgperf/t/101_create_snapshot_pg_current_xlog/input/test.sql
@@ -0,0 +1,24 @@
+SELECT * FROM pgperf.snapshot_pg_current_xlog ORDER BY 1;
+
+SELECT pgperf.create_snapshot_pg_current_xlog(0);
+SELECT pgperf.create_snapshot_pg_current_xlog(1);
+
+WITH s0 AS (
+SELECT * FROM pgperf.snapshot_pg_current_xlog WHERE sid = 0
+), s1 AS (
+SELECT * FROM pgperf.snapshot_pg_current_xlog WHERE sid = 1
+)
+SELECT s0.location < s1.location, s0.insert_location < s1.insert_location
+ FROM s0, s1
+ WHERE s0.sid + 1 = s1.sid;
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+
+SELECT pgperf.delete_snapshot_pg_current_xlog(1);
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+
+SELECT pgperf.delete_snapshot_pg_current_xlog(0);
+
+SELECT count(*) FROM pgperf.snapshot_pg_current_xlog;
+
diff --git a/src/pgperf/t/102_create_snapshot_pg_database_size/expected/test.out b/src/pgperf/t/102_create_snapshot_pg_database_size/expected/test.out
new file mode 100644
index 0000000..43bd537
--- /dev/null
+++ b/src/pgperf/t/102_create_snapshot_pg_database_size/expected/test.out
@@ -0,0 +1,42 @@
+SELECT pgperf.create_snapshot_pg_database_size(0);
+ create_snapshot_pg_database_size
+----------------------------------
+ t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_database_size(1);
+ create_snapshot_pg_database_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_database_size(1);
+ delete_snapshot_pg_database_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_database_size(0);
+ delete_snapshot_pg_database_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/102_create_snapshot_pg_database_size/input/test.sql b/src/pgperf/t/102_create_snapshot_pg_database_size/input/test.sql
new file mode 100644
index 0000000..43a15df
--- /dev/null
+++ b/src/pgperf/t/102_create_snapshot_pg_database_size/input/test.sql
@@ -0,0 +1,11 @@
+SELECT pgperf.create_snapshot_pg_database_size(0);
+SELECT pgperf.create_snapshot_pg_database_size(1);
+
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+
+SELECT pgperf.delete_snapshot_pg_database_size(1);
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+
+SELECT pgperf.delete_snapshot_pg_database_size(0);
+SELECT count(*) FROM pgperf.snapshot_pg_database_size;
+
diff --git a/src/pgperf/t/103_create_snapshot_pg_locks/expected/test.out b/src/pgperf/t/103_create_snapshot_pg_locks/expected/test.out
new file mode 100644
index 0000000..6feddba
--- /dev/null
+++ b/src/pgperf/t/103_create_snapshot_pg_locks/expected/test.out
@@ -0,0 +1,72 @@
+SELECT pgperf.create_snapshot_pg_locks(0);
+ create_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 4
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_locks(1);
+ create_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 8
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_locks(2);
+ create_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 12
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_locks(1);
+ delete_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 8
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_locks(2);
+ delete_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 4
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_locks(0);
+ delete_snapshot_pg_locks
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/103_create_snapshot_pg_locks/input/test.sql b/src/pgperf/t/103_create_snapshot_pg_locks/input/test.sql
new file mode 100644
index 0000000..fdf5f56
--- /dev/null
+++ b/src/pgperf/t/103_create_snapshot_pg_locks/input/test.sql
@@ -0,0 +1,19 @@
+-- Create
+SELECT pgperf.create_snapshot_pg_locks(0);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+
+SELECT pgperf.create_snapshot_pg_locks(1);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+
+SELECT pgperf.create_snapshot_pg_locks(2);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+
+-- Delete
+SELECT pgperf.delete_snapshot_pg_locks(1);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+
+SELECT pgperf.delete_snapshot_pg_locks(2);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
+
+SELECT pgperf.delete_snapshot_pg_locks(0);
+SELECT count(*) FROM pgperf.snapshot_pg_locks;
diff --git a/src/pgperf/t/104_create_snapshot_pg_relation_size/expected/test.out b/src/pgperf/t/104_create_snapshot_pg_relation_size/expected/test.out
new file mode 100644
index 0000000..98e180c
--- /dev/null
+++ b/src/pgperf/t/104_create_snapshot_pg_relation_size/expected/test.out
@@ -0,0 +1,62 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pg_relation_size(0);
+ create_snapshot_pg_relation_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_relation_size(1);
+ create_snapshot_pg_relation_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_relation_size(0);
+ delete_snapshot_pg_relation_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_relation_size(1);
+ delete_snapshot_pg_relation_size
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/104_create_snapshot_pg_relation_size/input/test.sql b/src/pgperf/t/104_create_snapshot_pg_relation_size/input/test.sql
new file mode 100644
index 0000000..2145cf2
--- /dev/null
+++ b/src/pgperf/t/104_create_snapshot_pg_relation_size/input/test.sql
@@ -0,0 +1,20 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pg_relation_size(0);
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_relation_size(1);
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_relation_size(0);
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_relation_size(1);
+SELECT count(*) FROM pgperf.snapshot_pg_relation_size WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test.out b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test.out
new file mode 100644
index 0000000..79c61e0
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test.out
@@ -0,0 +1,99 @@
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND pid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND state_change < now()
+ AND waiting = false
+ AND state = 'active'
+ AND query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 2
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.pid = a.pid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ (b.state_change - a.state_change) > '0'::interval,
+ b.waiting = a.waiting,
+ b.state = a.state,
+ b.query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t | t | t | t | t | t | t | t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+ delete_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test90.out b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test90.out
new file mode 100644
index 0000000..a212aa3
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test90.out
@@ -0,0 +1,95 @@
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND procpid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND waiting = false
+ AND current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 2
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.procpid = a.procpid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ b.waiting = a.waiting,
+ b.current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t | t | t | t | t | t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+ delete_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test91.out b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test91.out
new file mode 100644
index 0000000..a212aa3
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/expected/test91.out
@@ -0,0 +1,95 @@
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND procpid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND waiting = false
+ AND current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+ count
+-------
+ 2
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.procpid = a.procpid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ b.waiting = a.waiting,
+ b.current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t | t | t | t | t | t
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+ create_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+ delete_snapshot_pg_stat_activity
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
+ ?column?
+----------
+ t
+(1 row)
+
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/setup.sql b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/setup.sql
new file mode 100644
index 0000000..71b3c07
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/setup.sql
@@ -0,0 +1 @@
+DELETE FROM pgperf.snapshot_pg_stat_activity;
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test.sql b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test.sql
new file mode 100644
index 0000000..9719b84
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test.sql
@@ -0,0 +1,57 @@
+-- create 1st snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_activity;
+-- \x
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND pid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND state_change < now()
+ AND waiting = false
+ AND state = 'active'
+ AND query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+
+-- create 2nd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.pid = a.pid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ (b.state_change - a.state_change) > '0'::interval,
+ b.waiting = a.waiting,
+ b.state = a.state,
+ b.query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+
+-- create 3rd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+
+-- delete 1st snapshot
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test90.sql b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test90.sql
new file mode 100644
index 0000000..afcb1e7
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test90.sql
@@ -0,0 +1,53 @@
+-- create 1st snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_activity;
+-- \x
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND procpid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND waiting = false
+ AND current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+
+-- create 2nd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.procpid = a.procpid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ b.waiting = a.waiting,
+ b.current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+
+-- create 3rd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+
+-- delete 1st snapshot
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
diff --git a/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test91.sql b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test91.sql
new file mode 100644
index 0000000..afcb1e7
--- /dev/null
+++ b/src/pgperf/t/105_create_snapshot_pg_stat_activity/input/test91.sql
@@ -0,0 +1,53 @@
+-- create 1st snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_activity;
+-- \x
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_activity
+ WHERE sid = 0
+ AND datid IS NOT NULL
+ AND datname = 'testdb'
+ AND procpid IS NOT NULL
+ AND usesysid IS NOT NULL
+ AND usename IS NOT NULL
+ AND application_name = 'psql'
+ AND client_port IS NOT NULL
+ AND backend_start < now()
+ AND xact_start < now()
+ AND query_start < now()
+ AND waiting = false
+ AND current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(0);';
+
+-- create 2nd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_activity;
+
+SELECT b.sid - a.sid = 1,
+ b.datid = a.datid,
+ b.datname = a.datname,
+ b.procpid = a.procpid,
+ b.usesysid = a.usesysid,
+ b.usename = a.usename,
+ b.application_name = a.application_name,
+ b.client_port = a.client_port,
+ (b.backend_start - a.backend_start) = '0'::interval,
+ (b.xact_start - a.xact_start) > '0'::interval,
+ (b.query_start - a.query_start) > '0'::interval,
+ b.waiting = a.waiting,
+ b.current_query = 'SELECT pgperf.create_snapshot_pg_stat_activity(1);'
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1 ) AS b
+;
+
+-- create 3rd snapshot
+SELECT pgperf.create_snapshot_pg_stat_activity(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_activity;
+
+-- delete 1st snapshot
+SELECT pgperf.delete_snapshot_pg_stat_activity(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_activity WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_activity;
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test.out b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test.out
new file mode 100644
index 0000000..ef0502c
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test.out
@@ -0,0 +1,72 @@
+SELECT pgperf.create_snapshot_pg_stat_archiver(0);
+ create_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_archiver(1);
+ create_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_archiver(2);
+ create_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(1);
+ delete_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(2);
+ delete_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(0);
+ delete_snapshot_pg_stat_archiver
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test90.out b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test90.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test90.out
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test91.out b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test91.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test91.out
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test92.out b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test92.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test92.out
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test93.out b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test93.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/expected/test93.out
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test.sql b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test.sql
new file mode 100644
index 0000000..33554da
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test.sql
@@ -0,0 +1,17 @@
+SELECT pgperf.create_snapshot_pg_stat_archiver(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.create_snapshot_pg_stat_archiver(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.create_snapshot_pg_stat_archiver(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_archiver(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_bgwriter;
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test90.sql b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test90.sql
new file mode 100644
index 0000000..28d12de
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test90.sql
@@ -0,0 +1,2 @@
+-- no test because 9.0 does not have pg_stat_archiver
+
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test91.sql b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test91.sql
new file mode 100644
index 0000000..33a58de
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test91.sql
@@ -0,0 +1,2 @@
+-- no test because 9.2 does not have pg_stat_archiver
+
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test92.sql b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test92.sql
new file mode 100644
index 0000000..33a58de
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test92.sql
@@ -0,0 +1,2 @@
+-- no test because 9.2 does not have pg_stat_archiver
+
diff --git a/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test93.sql b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test93.sql
new file mode 100644
index 0000000..25ed843
--- /dev/null
+++ b/src/pgperf/t/106_create_snapshot_pg_stat_archiver/input/test93.sql
@@ -0,0 +1,2 @@
+-- no test because 9.3 does not have pg_stat_archiver
+
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test.out b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test.out
new file mode 100644
index 0000000..85088e9
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test.out
@@ -0,0 +1,99 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND checkpoint_write_time >= 0
+ AND checkpoint_sync_time >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_backend_fsync >= 0
+ AND buffers_alloc >= 0
+ AND stats_reset IS NOT NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pg_sleep(1);
+ pg_sleep
+----------
+
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.checkpoint_write_time - a.checkpoint_write_time >= 0,
+ b.checkpoint_sync_time - a.checkpoint_sync_time > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_backend_fsync - a.buffers_backend_fsync = 0,
+ b.buffers_alloc - a.buffers_alloc > 0,
+ b.stats_reset - a.stats_reset = '0'::interval
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t | t | t | t | t
+(1 row)
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+ delete_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+DROP TABLE t1,t2;
+DROP TABLE
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test90.out b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test90.out
new file mode 100644
index 0000000..c016b67
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test90.out
@@ -0,0 +1,91 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_alloc >= 0;
+ ?column?
+----------
+ t
+(1 row)
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pg_sleep(1);
+ pg_sleep
+----------
+
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_alloc - a.buffers_alloc > 0
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t
+(1 row)
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+ delete_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+DROP TABLE t1,t2;
+DROP TABLE
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test91.out b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test91.out
new file mode 100644
index 0000000..7bcfe07
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/expected/test91.out
@@ -0,0 +1,95 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_backend_fsync >= 0
+ AND buffers_alloc >= 0
+ AND stats_reset IS NOT NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pg_sleep(1);
+ pg_sleep
+----------
+
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_backend_fsync - a.buffers_backend_fsync = 0,
+ b.buffers_alloc - a.buffers_alloc > 0,
+ b.stats_reset - a.stats_reset = '0'::interval
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+ ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column? | ?column?
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------
+ t | t | t | t | t | t | t | t | t | t
+(1 row)
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+SELECT 1000
+CHECKPOINT;
+CHECKPOINT
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+ create_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+ delete_snapshot_pg_stat_bgwriter
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+ ?column?
+----------
+ t
+(1 row)
+
+DROP TABLE t1,t2;
+DROP TABLE
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/setup.sql b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/setup.sql
new file mode 100644
index 0000000..7494645
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/setup.sql
@@ -0,0 +1 @@
+TRUNCATE pgperf.snapshot_pg_stat_bgwriter;
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test.sql b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test.sql
new file mode 100644
index 0000000..be64681
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test.sql
@@ -0,0 +1,55 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND checkpoint_write_time >= 0
+ AND checkpoint_sync_time >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_backend_fsync >= 0
+ AND buffers_alloc >= 0
+ AND stats_reset IS NOT NULL;
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+SELECT pg_sleep(1);
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_bgwriter ORDER BY sid;
+-- \x
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.checkpoint_write_time - a.checkpoint_write_time >= 0,
+ b.checkpoint_sync_time - a.checkpoint_sync_time > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_backend_fsync - a.buffers_backend_fsync = 0,
+ b.buffers_alloc - a.buffers_alloc > 0,
+ b.stats_reset - a.stats_reset = '0'::interval
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+DROP TABLE t1,t2;
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test90.sql b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test90.sql
new file mode 100644
index 0000000..397d4ae
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test90.sql
@@ -0,0 +1,47 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_alloc >= 0;
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+SELECT pg_sleep(1);
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_bgwriter ORDER BY sid;
+-- \x
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_alloc - a.buffers_alloc > 0
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+DROP TABLE t1,t2;
diff --git a/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test91.sql b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test91.sql
new file mode 100644
index 0000000..3a9d38e
--- /dev/null
+++ b/src/pgperf/t/107_create_snapshot_pg_stat_bgwriter/input/test91.sql
@@ -0,0 +1,51 @@
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(0);
+
+SELECT count(*) = 1
+ FROM pgperf.snapshot_pg_stat_bgwriter
+ WHERE sid = 0
+ AND checkpoints_timed >= 0
+ AND checkpoints_req >= 0
+ AND buffers_checkpoint >= 0
+ AND buffers_clean >= 0
+ AND maxwritten_clean >= 0
+ AND buffers_backend >= 0
+ AND buffers_backend_fsync >= 0
+ AND buffers_alloc >= 0
+ AND stats_reset IS NOT NULL;
+
+CREATE TABLE t1 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+SELECT pg_sleep(1);
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(1);
+-- \x
+-- SELECT * FROM pgperf.snapshot_pg_stat_bgwriter ORDER BY sid;
+-- \x
+
+SELECT b.sid - a.sid = 1,
+ b.checkpoints_timed - a.checkpoints_timed = 0,
+ b.checkpoints_req - a.checkpoints_req > 0,
+ b.buffers_checkpoint - a.buffers_checkpoint > 0,
+ b.buffers_clean - a.buffers_clean = 0,
+ b.maxwritten_clean - a.maxwritten_clean = 0,
+ b.buffers_backend - a.buffers_backend > 0,
+ b.buffers_backend_fsync - a.buffers_backend_fsync = 0,
+ b.buffers_alloc - a.buffers_alloc > 0,
+ b.stats_reset - a.stats_reset = '0'::interval
+ FROM ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 0 ) AS a,
+ ( SELECT * FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1 ) AS b
+;
+
+CREATE TABLE t2 AS
+ SELECT * FROM generate_series(1,1000);
+CHECKPOINT;
+
+SELECT pgperf.create_snapshot_pg_stat_bgwriter(2);
+SELECT count(*) = 3 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+SELECT pgperf.delete_snapshot_pg_stat_bgwriter(1);
+SELECT count(*) = 0 FROM pgperf.snapshot_pg_stat_bgwriter WHERE sid = 1;
+SELECT count(*) = 2 FROM pgperf.snapshot_pg_stat_bgwriter;
+
+DROP TABLE t1,t2;
diff --git a/src/pgperf/t/108_create_snapshot_pg_stat_database/expected/test.out b/src/pgperf/t/108_create_snapshot_pg_stat_database/expected/test.out
new file mode 100644
index 0000000..231b623
--- /dev/null
+++ b/src/pgperf/t/108_create_snapshot_pg_stat_database/expected/test.out
@@ -0,0 +1,72 @@
+SELECT pgperf.create_snapshot_pg_stat_database(0);
+ create_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_database(1);
+ create_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_database(2);
+ create_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database(1);
+ delete_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database(2);
+ delete_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database(0);
+ delete_snapshot_pg_stat_database
+----------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/108_create_snapshot_pg_stat_database/input/test.sql b/src/pgperf/t/108_create_snapshot_pg_stat_database/input/test.sql
new file mode 100644
index 0000000..9a4dcf4
--- /dev/null
+++ b/src/pgperf/t/108_create_snapshot_pg_stat_database/input/test.sql
@@ -0,0 +1,17 @@
+SELECT pgperf.create_snapshot_pg_stat_database(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+
+SELECT pgperf.create_snapshot_pg_stat_database(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+
+SELECT pgperf.create_snapshot_pg_stat_database(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+
+SELECT pgperf.delete_snapshot_pg_stat_database(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+
+SELECT pgperf.delete_snapshot_pg_stat_database(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
+
+SELECT pgperf.delete_snapshot_pg_stat_database(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database;
diff --git a/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test.out b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test.out
new file mode 100644
index 0000000..00dab9b
--- /dev/null
+++ b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test.out
@@ -0,0 +1,72 @@
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(0);
+ create_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(1);
+ create_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(2);
+ create_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(1);
+ delete_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(2);
+ delete_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(0);
+ delete_snapshot_pg_stat_database_conflicts
+--------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test90.out b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test90.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/expected/test90.out
diff --git a/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test.sql b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test.sql
new file mode 100644
index 0000000..d5781d5
--- /dev/null
+++ b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test.sql
@@ -0,0 +1,17 @@
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+
+SELECT pgperf.create_snapshot_pg_stat_database_conflicts(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
+
+SELECT pgperf.delete_snapshot_pg_stat_database_conflicts(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_database_conflicts;
diff --git a/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test90.sql b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test90.sql
new file mode 100644
index 0000000..7987726
--- /dev/null
+++ b/src/pgperf/t/109_create_snapshot_pg_stat_database_conflicts/input/test90.sql
@@ -0,0 +1,2 @@
+-- no test because 9.0 does not have pg_stat_database_conflicts
+
diff --git a/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test.out b/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test.out
new file mode 100644
index 0000000..59d468f
--- /dev/null
+++ b/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test.out
@@ -0,0 +1,72 @@
+SELECT pgperf.create_snapshot_pg_stat_replication(0);
+ create_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_replication(1);
+ create_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_replication(2);
+ create_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(1);
+ delete_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(2);
+ delete_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(0);
+ delete_snapshot_pg_stat_replication
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test90.out b/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test90.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pgperf/t/110_create_snapshot_pg_stat_replication/expected/test90.out
diff --git a/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test.sql b/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test.sql
new file mode 100644
index 0000000..56e4663
--- /dev/null
+++ b/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test.sql
@@ -0,0 +1,17 @@
+SELECT pgperf.create_snapshot_pg_stat_replication(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+
+SELECT pgperf.create_snapshot_pg_stat_replication(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+
+SELECT pgperf.create_snapshot_pg_stat_replication(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
+
+SELECT pgperf.delete_snapshot_pg_stat_replication(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_replication;
diff --git a/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test90.sql b/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test90.sql
new file mode 100644
index 0000000..adaa314
--- /dev/null
+++ b/src/pgperf/t/110_create_snapshot_pg_stat_replication/input/test90.sql
@@ -0,0 +1,2 @@
+-- no test because 9.0 does not have pg_stat_replication.
+
diff --git a/src/pgperf/t/111_create_snapshot_pg_stat_statements/expected/test.out b/src/pgperf/t/111_create_snapshot_pg_stat_statements/expected/test.out
new file mode 100644
index 0000000..a019286
--- /dev/null
+++ b/src/pgperf/t/111_create_snapshot_pg_stat_statements/expected/test.out
@@ -0,0 +1,54 @@
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_statements(0);
+ create_snapshot_pg_stat_statements
+------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_statements(1);
+ create_snapshot_pg_stat_statements
+------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+ count
+-------
+ 4
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_statements(0);
+ delete_snapshot_pg_stat_statements
+------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_statements(1);
+ delete_snapshot_pg_stat_statements
+------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+ count
+-------
+ 0
+(1 row)
+
diff --git a/src/pgperf/t/111_create_snapshot_pg_stat_statements/input/test.sql b/src/pgperf/t/111_create_snapshot_pg_stat_statements/input/test.sql
new file mode 100644
index 0000000..ce741e2
--- /dev/null
+++ b/src/pgperf/t/111_create_snapshot_pg_stat_statements/input/test.sql
@@ -0,0 +1,13 @@
+SELECT pg_stat_statements_reset();
+
+SELECT pgperf.create_snapshot_pg_stat_statements(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+
+SELECT pgperf.create_snapshot_pg_stat_statements(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+
+SELECT pgperf.delete_snapshot_pg_stat_statements(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
+
+SELECT pgperf.delete_snapshot_pg_stat_statements(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_statements;
diff --git a/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/expected/test.out b/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/expected/test.out
new file mode 100644
index 0000000..0fb06c4
--- /dev/null
+++ b/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/expected/test.out
@@ -0,0 +1,94 @@
+CREATE FUNCTION f1() RETURNS BOOLEAN
+ AS
+'
+BEGIN
+ RETURN true;
+END
+' LANGUAGE 'plpgsql';
+CREATE FUNCTION
+SELECT f1();
+ f1
+----
+ t
+(1 row)
+
+SELECT pg_sleep(1);
+ pg_sleep
+----------
+
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(0);
+ create_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(1);
+ create_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 6
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(2);
+ create_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 9
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(1);
+ delete_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 6
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(2);
+ delete_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(0);
+ delete_snapshot_pg_stat_user_functions
+----------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP FUNCTION f1();
+DROP FUNCTION
diff --git a/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/input/test.sql b/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/input/test.sql
new file mode 100644
index 0000000..ff6beb3
--- /dev/null
+++ b/src/pgperf/t/111_create_snapshot_pg_stat_user_functions/input/test.sql
@@ -0,0 +1,32 @@
+CREATE FUNCTION f1() RETURNS BOOLEAN
+ AS
+'
+BEGIN
+ RETURN true;
+END
+' LANGUAGE 'plpgsql';
+
+SELECT f1();
+
+SELECT pg_sleep(1);
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_functions(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_functions(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+DROP FUNCTION f1();
+
diff --git a/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/expected/test.out b/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/expected/test.out
new file mode 100644
index 0000000..8373b72
--- /dev/null
+++ b/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(0);
+ create_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(1);
+ create_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(2);
+ create_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(1);
+ delete_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(2);
+ delete_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(0);
+ delete_snapshot_pg_stat_user_indexes
+--------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/input/test.sql b/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/input/test.sql
new file mode 100644
index 0000000..39f4fd5
--- /dev/null
+++ b/src/pgperf/t/112_create_snapshot_pg_stat_user_indexes/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_indexes(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_indexes(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_indexes WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/expected/test.out b/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/expected/test.out
new file mode 100644
index 0000000..5b8a820
--- /dev/null
+++ b/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pg_stat_user_tables(0);
+ create_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_tables(1);
+ create_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stat_user_tables(2);
+ create_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(1);
+ delete_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(2);
+ delete_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(0);
+ delete_snapshot_pg_stat_user_tables
+-------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/input/test.sql b/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/input/test.sql
new file mode 100644
index 0000000..9d4cbbb
--- /dev/null
+++ b/src/pgperf/t/113_create_snapshot_pg_stat_user_tables/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pg_stat_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_stat_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_stat_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_tables WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/expected/test.out b/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/expected/test.out
new file mode 100644
index 0000000..479cac4
--- /dev/null
+++ b/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pg_statio_user_tables(0);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(1);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(2);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(1);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(2);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(0);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/input/test.sql b/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/input/test.sql
new file mode 100644
index 0000000..645da03
--- /dev/null
+++ b/src/pgperf/t/114_create_snapshot_pg_statio_user_indexes/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/expected/test.out b/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/expected/test.out
new file mode 100644
index 0000000..f04c756
--- /dev/null
+++ b/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/expected/test.out
@@ -0,0 +1,82 @@
+CREATE SEQUENCE s1;
+CREATE SEQUENCE
+SELECT nextval('s1');
+ nextval
+---------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(0);
+ create_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(1);
+ create_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(2);
+ create_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(1);
+ delete_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(2);
+ delete_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(0);
+ delete_snapshot_pg_statio_user_sequences
+------------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP SEQUENCE s1;
+DROP SEQUENCE
diff --git a/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/input/test.sql b/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/input/test.sql
new file mode 100644
index 0000000..9657f6a
--- /dev/null
+++ b/src/pgperf/t/114_create_snapshot_pg_statio_user_sequences/input/test.sql
@@ -0,0 +1,22 @@
+CREATE SEQUENCE s1;
+SELECT nextval('s1');
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_sequences(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_sequences(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+
+DROP SEQUENCE s1;
diff --git a/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/expected/test.out b/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/expected/test.out
new file mode 100644
index 0000000..479cac4
--- /dev/null
+++ b/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pg_statio_user_tables(0);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(1);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(2);
+ create_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(1);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(2);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(0);
+ delete_snapshot_pg_statio_user_tables
+---------------------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/input/test.sql b/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/input/test.sql
new file mode 100644
index 0000000..645da03
--- /dev/null
+++ b/src/pgperf/t/115_create_snapshot_pg_statio_user_tables/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pg_statio_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(1);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(2);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pg_statio_user_tables(0);
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/116_create_snapshot_pg_stats/expected/test.out b/src/pgperf/t/116_create_snapshot_pg_stats/expected/test.out
new file mode 100644
index 0000000..15443f9
--- /dev/null
+++ b/src/pgperf/t/116_create_snapshot_pg_stats/expected/test.out
@@ -0,0 +1,88 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+ANALYZE t1;
+ANALYZE
+SELECT pgperf.create_snapshot_pg_stats(0);
+ create_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stats(1);
+ create_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 4
+(1 row)
+
+SELECT pgperf.create_snapshot_pg_stats(2);
+ create_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 6
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stats(1);
+ delete_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 4
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stats(0);
+ delete_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pg_stats(2);
+ delete_snapshot_pg_stats
+--------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/116_create_snapshot_pg_stats/input/test.sql b/src/pgperf/t/116_create_snapshot_pg_stats/input/test.sql
new file mode 100644
index 0000000..0e380a3
--- /dev/null
+++ b/src/pgperf/t/116_create_snapshot_pg_stats/input/test.sql
@@ -0,0 +1,28 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+ANALYZE t1;
+
+SELECT pgperf.create_snapshot_pg_stats(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.create_snapshot_pg_stats(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.create_snapshot_pg_stats(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.delete_snapshot_pg_stats(1);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.delete_snapshot_pg_stats(0);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.delete_snapshot_pg_stats(2);
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/117_create_snapshot_pgstatindex/expected/test.out b/src/pgperf/t/117_create_snapshot_pgstatindex/expected/test.out
new file mode 100644
index 0000000..2f4405c
--- /dev/null
+++ b/src/pgperf/t/117_create_snapshot_pgstatindex/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pgstattuple(0);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pgstattuple(1);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pgstattuple(2);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(1);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(2);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(0);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/117_create_snapshot_pgstatindex/input/test.sql b/src/pgperf/t/117_create_snapshot_pgstatindex/input/test.sql
new file mode 100644
index 0000000..534db09
--- /dev/null
+++ b/src/pgperf/t/117_create_snapshot_pgstatindex/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pgstattuple(0);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pgstattuple(1);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pgstattuple(2);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(1);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(2);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(0);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/118_create_snapshot_pgstattuple/expected/test.out b/src/pgperf/t/118_create_snapshot_pgstattuple/expected/test.out
new file mode 100644
index 0000000..2f4405c
--- /dev/null
+++ b/src/pgperf/t/118_create_snapshot_pgstattuple/expected/test.out
@@ -0,0 +1,86 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+SELECT pgperf.create_snapshot_pgstattuple(0);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.create_snapshot_pgstattuple(1);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.create_snapshot_pgstattuple(2);
+ create_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 3
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(1);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 2
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(2);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT pgperf.delete_snapshot_pgstattuple(0);
+ delete_snapshot_pgstattuple
+-----------------------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
diff --git a/src/pgperf/t/118_create_snapshot_pgstattuple/input/test.sql b/src/pgperf/t/118_create_snapshot_pgstattuple/input/test.sql
new file mode 100644
index 0000000..534db09
--- /dev/null
+++ b/src/pgperf/t/118_create_snapshot_pgstattuple/input/test.sql
@@ -0,0 +1,26 @@
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+SELECT pgperf.create_snapshot_pgstattuple(0);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pgstattuple(1);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot_pgstattuple(2);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(1);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(2);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.delete_snapshot_pgstattuple(0);
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
diff --git a/src/pgperf/t/201_create_snapshot/expected/test.out b/src/pgperf/t/201_create_snapshot/expected/test.out
new file mode 100644
index 0000000..7a307a5
--- /dev/null
+++ b/src/pgperf/t/201_create_snapshot/expected/test.out
@@ -0,0 +1,214 @@
+CREATE FUNCTION f1() RETURNS BOOLEAN
+ AS
+'
+BEGIN
+ RETURN true;
+END
+' LANGUAGE 'plpgsql';
+CREATE FUNCTION
+SELECT f1();
+ f1
+----
+ t
+(1 row)
+
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+CREATE TABLE
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT 0 1
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+INSERT 0 1
+CREATE SEQUENCE s1;
+CREATE SEQUENCE
+SELECT nextval('s1');
+ nextval
+---------
+ 1
+(1 row)
+
+ANALYZE t1;
+ANALYZE
+ANALYZE s1;
+ANALYZE
+SELECT pg_sleep(1);
+ pg_sleep
+----------
+
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot;
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot(1);
+ create_snapshot
+-----------------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot;
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+ count
+-------
+ 4
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot(2);
+ create_snapshot
+-----------------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 2
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.create_snapshot(4);
+ create_snapshot
+-----------------
+ 2
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot;
+ count
+-------
+ 3
+(1 row)
+
+SELECT sid,level FROM pgperf.snapshot order by ts;
+ sid | level
+-----+-------
+ 0 | 1
+ 1 | 2
+ 2 | 4
+(3 rows)
+
+SELECT pgperf.delete_snapshot(2);
+ delete_snapshot
+-----------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot(1);
+ delete_snapshot
+-----------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+ count
+-------
+ 0
+(1 row)
+
+SELECT pgperf.delete_snapshot(0);
+ delete_snapshot
+-----------------
+ t
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot;
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+ count
+-------
+ 0
+(1 row)
+
+DROP TABLE t1;
+DROP TABLE
+DROP SEQUENCE s1;
+DROP SEQUENCE
+DROP FUNCTION f1();
+DROP FUNCTION
diff --git a/src/pgperf/t/201_create_snapshot/input/test.sql b/src/pgperf/t/201_create_snapshot/input/test.sql
new file mode 100644
index 0000000..225d402
--- /dev/null
+++ b/src/pgperf/t/201_create_snapshot/input/test.sql
@@ -0,0 +1,79 @@
+CREATE FUNCTION f1() RETURNS BOOLEAN
+ AS
+'
+BEGIN
+ RETURN true;
+END
+' LANGUAGE 'plpgsql';
+
+SELECT f1();
+
+CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
+INSERT INTO t1 VALUES ( 101, 'Park Gyu-Ri' );
+INSERT INTO t1 VALUES ( 102, 'Han Seung-Yeon' );
+INSERT INTO t1 VALUES ( 103, 'Nicole' );
+INSERT INTO t1 VALUES ( 104, 'Koo Ha-Ra' );
+INSERT INTO t1 VALUES ( 105, 'Kang Ji-Young' );
+
+CREATE SEQUENCE s1;
+SELECT nextval('s1');
+
+ANALYZE t1;
+ANALYZE s1;
+
+SELECT pg_sleep(1);
+
+-- Snapshot Level 1
+SELECT count(*) FROM pgperf.snapshot;
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot(1);
+
+SELECT count(*) FROM pgperf.snapshot;
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_sequences WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pg_stat_user_functions WHERE schemaname <> 'pgperf';
+
+-- Snapshot Level 2
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+SELECT pgperf.create_snapshot(2);
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+-- Snapshot Level 4
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+
+SELECT pgperf.create_snapshot(4);
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+
+SELECT count(*) FROM pgperf.snapshot;
+
+SELECT sid,level FROM pgperf.snapshot order by ts;
+
+-- Delete Snapshot Level 4
+SELECT pgperf.delete_snapshot(2);
+
+SELECT count(*) FROM pgperf.snapshot_pgstattuple WHERE schemaname <> 'pgperf';
+SELECT count(*) FROM pgperf.snapshot_pgstatindex WHERE schemaname <> 'pgperf';
+
+-- Delete Snapshot Level 2
+SELECT pgperf.delete_snapshot(1);
+
+SELECT count(*) FROM pgperf.snapshot_pg_stats WHERE schemaname = 'public';
+
+-- Delete Snapshot Level 1
+SELECT pgperf.delete_snapshot(0);
+
+SELECT count(*) FROM pgperf.snapshot;
+SELECT count(*) FROM pgperf.snapshot_pg_statio_user_tables WHERE schemaname <> 'pgperf';
+
+DROP TABLE t1;
+DROP SEQUENCE s1;
+DROP FUNCTION f1();
+
diff --git a/src/pgperf/t/Makefile b/src/pgperf/t/Makefile
new file mode 100644
index 0000000..d666ef6
--- /dev/null
+++ b/src/pgperf/t/Makefile
@@ -0,0 +1,17 @@
+TOPDIR=../../..
+
+include $(TOPDIR)/Makefile.global
+
+all:
+ echo noting to be done.
+
+check:
+ ./regress.sh
+
+clean:
+ find . -type f -name 'result.diff' | xargs rm -f
+ find . -type d -name 'output' | xargs rm -rf
+ rm -rf data90 data91 data92 data93 data94
+ rm -rf out90 out91 out92 out93 out94
+ rm -f catalog90.txt catalog91.txt catalog92.txt catalog93.txt catalog94.txt
+ rm -f *.log _postgresql.conf
diff --git a/src/pgperf/t/catalog.pl b/src/pgperf/t/catalog.pl
new file mode 100755
index 0000000..5bb355f
--- /dev/null
+++ b/src/pgperf/t/catalog.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+use strict;
+
+print <<EOF;
+SELECT relname FROM pg_class WHERE relkind IN ('r', 'v') AND relname LIKE E'pg\\_stat%' ORDER BY 1;
+
+CREATE TABLE pgstattuple AS SELECT * FROM pgstattuple('pg_database') LIMIT 0;
+CREATE TABLE pgstatindex AS SELECT * FROM pgstatindex('pg_database_datname_index') LIMIT 0;
+
+EOF
+
+my @syscat = (
+ "pg_stat_activity",
+ "pg_stat_all_indexes",
+ "pg_stat_all_tables",
+ "pg_stat_archiver",
+ "pg_stat_bgwriter",
+ "pg_stat_database",
+ "pg_stat_database_conflicts",
+ "pg_stat_replication",
+ "pg_stat_sys_indexes",
+ "pg_stat_sys_tables",
+ "pg_stat_user_functions",
+ "pg_stat_user_indexes",
+ "pg_stat_user_tables",
+ "pg_stat_xact_all_tables",
+ "pg_stat_xact_sys_tables",
+ "pg_stat_xact_user_functions",
+ "pg_stat_xact_user_tables",
+ "pg_statio_all_indexes",
+ "pg_statio_all_sequences",
+ "pg_statio_all_tables",
+ "pg_statio_sys_indexes",
+ "pg_statio_sys_sequences",
+ "pg_statio_sys_tables",
+ "pg_statio_user_indexes",
+ "pg_statio_user_sequences",
+ "pg_statio_user_tables",
+ "pg_locks",
+ "pg_statistic",
+ "pg_stats",
+# contrib views
+ "pg_stat_statements",
+# contrib functions
+ "pgstattuple",
+ "pgstatindex"
+ );
+
+foreach my $t (@syscat)
+{
+ print <<EOF;
+--
+-- $t
+--
+SELECT c.relname, a.attname, t.typname
+ FROM pg_class c, pg_attribute a, pg_type t
+ WHERE c.oid=a.attrelid
+ AND a.atttypid=t.oid
+ AND a.attnum>0
+ AND c.relname='$t'
+ ORDER BY c.relname,a.attnum;
+
+EOF
+}
diff --git a/src/pgperf/t/catalog.sh b/src/pgperf/t/catalog.sh
new file mode 100755
index 0000000..e972408
--- /dev/null
+++ b/src/pgperf/t/catalog.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+source ./test_common.sh
+
+function setUp()
+{
+ PGHOME=$1
+ PGDATA=$2
+
+ export PGHOME PGDATA
+
+ source ./test_common.sh
+
+ _pgctl_stop
+
+ rm -rf $PGDATA
+ _initdb /dev/null
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+
+ _pgctl_start
+
+ createdb testdb
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+
+ _install_pg_stat_statements testdb
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+
+ _install_pgstattuple testdb
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+
+ _get_version
+}
+
+function tearDown()
+{
+ PGHOME=$1
+ PGDATA=$2
+
+ export PGHOME PGDATA
+
+ _pgctl_stop
+ if [ $? -ne 0 ]; then
+ exit 1
+ fi
+}
+
+function get_catalog()
+{
+ PGHOME=$1
+ PGDATA=$2
+ OUTFILE=$3
+
+ rm -rf $PGDATA
+ setUp $PGHOME $PGDATA
+ if [ $? -ne 0 ]; then
+ echo "ERROR: SetUp failed."
+ exit 1
+ fi
+
+ ./catalog.pl | psql -A -t -F' ' testdb | grep -v SELECT > $OUTFILE
+
+ tearDown $PGHOME $PGDATA
+ if [ $? -ne 0 ]; then
+ echo "ERROR: TearDown failed."
+ exit 1
+ fi
+}
+
+get_catalog /usr/pgsql-9.0 ./data90 catalog90.txt
+get_catalog /usr/pgsql-9.1 ./data91 catalog91.txt
+get_catalog /usr/pgsql-9.2 ./data92 catalog92.txt
+get_catalog /usr/pgsql-9.3 ./data93 catalog93.txt
+get_catalog /usr/pgsql-9.4 ./data94 catalog94.txt
+
+exit 0
diff --git a/src/pgperf/t/regress.sh b/src/pgperf/t/regress.sh
new file mode 100755
index 0000000..0f0a2cd
--- /dev/null
+++ b/src/pgperf/t/regress.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+
+source ./test_common.sh
+
+function _setUp()
+{
+ PGHOME=$1
+ PGDATA=$2
+ INSTALLFILE=$3
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ killall postgres
+
+ _initdb _postgresql.conf
+ _pgctl_start
+
+ createdb testdb
+ if [ $? -ne 0 ]; then
+ echo "Failed to create a database."
+ exit 1
+ fi
+
+ _install_pg_stat_statements testdb
+ _install_pgstattuple testdb
+
+ psql -f ${INSTALLFILE} testdb
+ if [ $? -ne 0 ]; then
+ echo "Failed to run ${INSTALLFILE}."
+ exit 1
+ fi
+
+}
+
+function _tearDown()
+{
+ PGHOME=$1
+ PGDATA=$2
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ _pgctl_stop
+}
+
+
+function _test()
+{
+ _t=$1
+ echo "${_t}..."
+
+ ./${_t}.sh
+}
+
+function testsuite()
+{
+ PGHOME=$1
+ PGDATA=$2
+ OUTDIR=$3
+ PATH=..:${PGHOME}/bin:$_PATH
+ export PGHOME PGDATA PATH
+
+ _MAJORVERSION=`pg_config --version | perl -e 's/.* (\d+\.\d+).*/\1/;' -p`
+ export _MAJORVERSION
+
+ _MAJOR_STR=`echo $_MAJORVERSION | sed 's/\.//'`
+ export _MAJOR_STR
+
+ _setUp $PGHOME $PGDATA ../pgperf_snapshot_install${_MAJOR_STR}.sql > setup${_MAJOR_STR}.log 2>&1
+
+ echo "=========================================="
+ echo "PGHOME: $PGHOME"
+ echo "PGDATA: $PGDATA"
+ echo "PATH: $PATH"
+ echo "MAJORVERSION: $_MAJORVERSION"
+ echo "=========================================="
+
+ ./regress2.sh
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Regression test failed."
+ exit 1
+ fi
+# for t in $T; do
+# _test $t;
+# done;
+
+ _tearDown $PGHOME $PGDATA > teardown${_MAJOR_STR}.log 2>&1
+
+ rm -rf $OUTDIR
+ mkdir -p $OUTDIR
+ mv *.log $OUTDIR
+}
+
+rm -rf *.log *.out
+
+# -------------------------------------------------------
+# 9.0
+# -------------------------------------------------------
+cat /dev/null > _postgresql.conf
+echo "track_functions = all" >> _postgresql.conf
+echo "shared_preload_libraries = 'pg_stat_statements'" >> _postgresql.conf
+testsuite /usr/pgsql-9.0 ./data90 ./out90
+
+# -------------------------------------------------------
+# 9.1
+# -------------------------------------------------------
+testsuite /usr/pgsql-9.1 ./data91 ./out91
+
+# -------------------------------------------------------
+# 9.2
+# -------------------------------------------------------
+echo "track_io_timing = on" >> _postgresql.conf
+testsuite /usr/pgsql-9.2 ./data92 ./out92
+
+# -------------------------------------------------------
+# 9.3
+# -------------------------------------------------------
+# Checksum support
+_INITDB_OPTS="-k"
+testsuite /usr/pgsql-9.3 ./data93 ./out93
+
+# -------------------------------------------------------
+# 9.4
+# -------------------------------------------------------
+testsuite /usr/pgsql-9.4 ./data94 ./out94
diff --git a/src/pgperf/t/regress2.sh b/src/pgperf/t/regress2.sh
new file mode 100755
index 0000000..34bfa24
--- /dev/null
+++ b/src/pgperf/t/regress2.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+rm -f result.diff
+
+rm -rf ok.log failed.log
+
+./test.sh 001_schema
+./test.sh 002_procs
+./test.sh 003_get_server_version
+./test.sh 004_check_function
+./test.sh 005_check_table_or_view
+
+./test.sh 101_create_snapshot_pg_current_xlog
+./test.sh 102_create_snapshot_pg_database_size
+./test.sh 103_create_snapshot_pg_locks
+./test.sh 104_create_snapshot_pg_relation_size
+./test.sh 105_create_snapshot_pg_stat_activity
+./test.sh 106_create_snapshot_pg_stat_archiver
+./test.sh 107_create_snapshot_pg_stat_bgwriter
+./test.sh 108_create_snapshot_pg_stat_database
+./test.sh 109_create_snapshot_pg_stat_database_conflicts
+./test.sh 110_create_snapshot_pg_stat_replication
+./test.sh 111_create_snapshot_pg_stat_statements
+./test.sh 111_create_snapshot_pg_stat_user_functions
+./test.sh 112_create_snapshot_pg_stat_user_indexes
+./test.sh 113_create_snapshot_pg_stat_user_tables
+./test.sh 114_create_snapshot_pg_statio_user_indexes
+./test.sh 114_create_snapshot_pg_statio_user_sequences
+./test.sh 115_create_snapshot_pg_statio_user_tables
+./test.sh 116_create_snapshot_pg_stats
+./test.sh 117_create_snapshot_pgstatindex
+./test.sh 118_create_snapshot_pgstattuple
+
+./test.sh 201_create_snapshot
+
+# if not exist, create them.
+touch ok.log failed.log
+
+REGRESS_SUCCESS=`wc -l <ok.log`
+REGRESS_FAILED=`wc -l <failed.log`
+REGRESS_TOTAL=`expr $REGRESS_SUCCESS + $REGRESS_FAILED`
+
+echo -----------------------------------------
+echo Passed:$REGRESS_SUCCESS Failed:$REGRESS_FAILED Total:$REGRESS_TOTAL
+echo -----------------------------------------
+
+rm -rf ok.log failed.log
+
+if [ $REGRESS_FAILED -ne 0 ]; then
+ exit 1
+else
+ exit 0
+fi
+
diff --git a/src/pgperf/t/test.sh b/src/pgperf/t/test.sh
new file mode 100755
index 0000000..004ebcb
--- /dev/null
+++ b/src/pgperf/t/test.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+LANG=C
+PGCLIENTENCODING=utf8
+export LANG PGCLIENTENCODING
+
+TESTNAME=$1
+DBNAME=testdb
+PSQL_OPTS=""
+
+if [ "$TESTNAME" = "" ]; then
+ echo "Usage: $0 <TESTNAME>"
+ exit 1
+fi
+
+if [ ! -d $TESTNAME ]; then
+ echo Test $TESTNAME does not exist. Aborted.
+ echo $TESTNAME >> failed.log
+ exit 2;
+fi
+
+if [ ! -d "$TESTNAME/output" ]; then
+ mkdir -p $TESTNAME/output
+fi
+
+if [ -f "$TESTNAME/input/setup.sql" ]; then
+ psql ${PSQL_OPTS} -f $TESTNAME/input/setup.sql $DBNAME \
+ > $TESTNAME/output/setup.out 2> $TESTNAME/output/setup.err
+fi
+
+if [ -f "$TESTNAME/input/test${_MAJOR_STR}.sql" ]; then
+ psql ${PSQL_OPTS} -e -f $TESTNAME/input/test${_MAJOR_STR}.sql $DBNAME \
+ > $TESTNAME/output/test.out 2> $TESTNAME/output/test.err
+elif [ -f "$TESTNAME/input/test.sql" ]; then
+ psql ${PSQL_OPTS} -e -f $TESTNAME/input/test.sql $DBNAME \
+ > $TESTNAME/output/test.out 2> $TESTNAME/output/test.err
+else
+ echo Test $TESTNAME/input/test.sql does not exist. Aborted.
+ echo $TESTNAME >> failed.log
+ exit 3
+fi
+
+if [ -f "$TESTNAME/expected/test${_MAJOR_STR}.out" ]; then
+ diff --strip-trailing-cr -rc $TESTNAME/expected/test${_MAJOR_STR}.out $TESTNAME/output/test.out > $TESTNAME/result.diff
+elif [ -f "$TESTNAME/expected/test.out" ]; then
+ diff --strip-trailing-cr -rc $TESTNAME/expected/test.out $TESTNAME/output/test.out > $TESTNAME/result.diff
+else
+ diff --strip-trailing-cr -rc /dev/null $TESTNAME/output/test.out > $TESTNAME/result.diff
+fi
+
+if [ -s $TESTNAME/result.diff ]; then
+ echo $TESTNAME: Failed.
+ echo $TESTNAME >> failed.log
+else
+ echo $TESTNAME: OK.
+ echo $TESTNAME >> ok.log
+fi
+
+cat $TESTNAME/result.diff >> result.diff
+
diff --git a/src/pgperf/t/test_common.sh b/src/pgperf/t/test_common.sh
new file mode 100644
index 0000000..700d6e8
--- /dev/null
+++ b/src/pgperf/t/test_common.sh
@@ -0,0 +1,100 @@
+_PATH=$PATH
+
+PATH=${PGHOME}/bin:$PATH
+export PATH
+
+LANG=C
+export LANG
+
+PGPORT=5433
+export PGPORT
+
+function _initdb()
+{
+ CONF=$1
+
+ rm -rf $PGDATA
+ mkdir $PGDATA
+ initdb -D $PGDATA --no-locale -E utf-8 $_INITDB_OPTS
+
+ if [ -f $CONF ]; then
+ echo "Adding $CONF to postgresql.conf"
+ cat $CONF >> ${PGDATA}/postgresql.conf
+ fi
+}
+
+function _pgctl_start()
+{
+ pg_ctl -D ${PGDATA} -w start
+}
+
+function _pgctl_stop()
+{
+ pg_ctl -D ${PGDATA} -w stop
+}
+
+function _install_pg_stat_statements()
+{
+ DBNAME=$1
+
+ if [ -d $PGHOME/share/contrib ]; then
+ psql -f $PGHOME/share/contrib/pg_stat_statements.sql $DBNAME
+ if [ $? -ne 0 ]; then
+ echo "Failed to install pg_stat_statements."
+ exit 1
+ fi
+ else
+ psql -c 'create extension pg_stat_statements' $DBNAME
+ if [ $? -ne 0 ]; then
+ echo "Failed to install pg_stat_statements."
+ exit 1
+ fi
+ fi
+}
+
+function _install_pgstattuple()
+{
+ DBNAME=$1
+
+ if [ -d $PGHOME/share/contrib ]; then
+ psql -f $PGHOME/share/contrib/pgstattuple.sql $DBNAME
+ if [ $? -ne 0 ]; then
+ echo "Failed to install pgstattuple."
+ exit 1
+ fi
+ else
+ psql -c 'create extension pgstattuple' $DBNAME
+ if [ $? -ne 0 ]; then
+ echo "Failed to install pgstattuple."
+ exit 1
+ fi
+ fi
+}
+
+function _get_version()
+{
+ _MAJORVERSION=`pg_config --version | perl -e 's/.* (\d+\.\d+).*/\1/;' -p`
+ export _MAJORVERSION
+
+ _MAJOR_STR=`echo $_MAJORVERSION | sed 's/\.//'`
+ export _MAJOR_STR
+}
+
+function load_file()
+{
+ DBNAME=$1;
+ FILENAME=$2;
+
+ if [ -f $FILENAME ]; then
+ echo "Loading ${FILENAME} ..."
+ psql -f $FILENAME $DBNAME
+ if [ $? -ne 0 ]; then
+ echo "Failed to run $FILENAME on $DBNAME.".
+ exit 1
+ fi
+ else
+ echo "ERROR: $FILENAME not found."
+ exit 1
+ fi
+}
+
diff --git a/src/verifychecksum/Makefile b/src/verifychecksum/Makefile
new file mode 100644
index 0000000..3b65222
--- /dev/null
+++ b/src/verifychecksum/Makefile
@@ -0,0 +1,30 @@
+#
+# verifychecksum Makefile
+#
+# Copyright(c) 2015 Uptime Technologies, LLC.
+#
+INC_SERVER=$(shell pg_config --includedir-server)
+
+CFLAGS = -Wall -g -I$(INC_SERVER)
+LIBS =
+
+TARGET = verifychecksum.bin
+
+TOPDIR = ../..
+
+include $(TOPDIR)/Makefile.global
+
+all: $(TARGET)
+
+verifychecksum.bin: verifychecksum.c
+ $(CC) $(CFLAGS) -o $@ $< $(LIBS)
+
+install:
+ mkdir -p $(PREFIX)/bin
+ install -m 755 $(TARGET) $(PREFIX)/bin
+ mkdir -p $(PREFIX)/src
+ install -m 755 *.c $(PREFIX)/src
+
+clean:
+ rm -rf $(TARGET) *~
+
diff --git a/src/verifychecksum/verifychecksum.c b/src/verifychecksum/verifychecksum.c
new file mode 100644
index 0000000..35a803e
--- /dev/null
+++ b/src/verifychecksum/verifychecksum.c
@@ -0,0 +1,633 @@
+/*
+ * Utility to verify a postgres base directory.
+ *
+ * This utility scans the 'base' data directory of a postgres instance to find
+ * directories and segment files. Each segment file is then checked for any
+ * pages that may have invalid checksums. Invalid checksums are determined by
+ * comparing the stored checksum against the current checksum.
+ *
+ * In order to use this utility, it is necessary to enable checksums with
+ * initdb. The flag for initdb is either -k or --data-checksums. Without,
+ * enabling checksums, it is not possible to use this utility.
+ *
+ */
+
+/*
+ * TODO:
+ * - improve diagnostics and/or repair if corrupted pages found
+ * - create test that simulates/creates corruption
+ * - check directory permissions since sudo may be required
+ * - handle exceptions with setjmp or signals
+ * - aggregate and/or create a report of corrupted blocks
+ * - implement dump of corrupted pages ( hexdump? )
+ * - run on multiple segment files simultaneously
+ * - detect if checksums enabled; regex for unset checksums?
+ * - throttling mechanism based on diskio/memory/cpu
+ * - convert this to an extension?
+ * - implement page dumping if checksum mismatch
+ */
+
+/* standard header files */
+#include <errno.h>
+#include <getopt.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* headers for directory scanning */
+#include <dirent.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/* postgres specific header files */
+#include "c.h"
+#include "pg_config.h"
+#include "postgres.h"
+#include "storage/checksum_impl.h"
+
+#define MAX_DIR_LENGTH 256
+#define MAX_THREAD_COUNT 10
+
+/* global flag values */
+int verbose = 0;
+int dump_corrupted = 0;
+bool parallel = false;
+int num_directories = 0;
+
+// Variables used to control the multi-threading.
+volatile int queue_index = 0;
+pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+
+// SPECKLE_POSTGRES: Add a dummy function for builds with postgres assertions
+// enabled. The useful definition is at cs/src/backend/utils/error/assert.c
+// but this is not currently exported outside the postgres tree.
+void ExceptionalCondition(const char *conditionName, const char *errorType,
+ const char *fileName, int lineNumber) {
+ abort();
+}
+
+/*
+* Determine segment number by segment file name. For instance, if file
+* name is /path/to/xxxx.7 procedure returns 7. Default return value is 0.
+*/
+static unsigned int
+get_segment_number(const char* fileName)
+{
+ int segnumOffset = strlen(fileName) - 1;
+
+ if(segnumOffset < 0)
+ return 0;
+
+ while(isdigit(fileName[segnumOffset])) {
+ segnumOffset--;
+ if(segnumOffset < 0)
+ return 0;
+ }
+
+ if(fileName[segnumOffset] != '.')
+ return 0;
+
+ return atoi(&fileName[segnumOffset+1]);
+}
+
+static bool
+is_page_corrupted(const char *page, BlockNumber blkno, const char *filename,
+ const char *dirpath)
+{
+ /* Function checks a page header checksum value aginst the current
+ * checksum value of a page. NewPage checksums will be zero until they
+ * are set. There is a similar function PageIsVerified responsible for
+ * checking pages before they are loaded into buffer pool.
+ * see: src/backend/storage/page/bufpage.c
+ *
+ * Consider returning a negative value if page is new or checksum unset
+ * or if more detail for a page verifiction can be found.
+ */
+
+ PageHeader phdr = (PageHeader)page;
+
+ /* calculating blkno needs to be aboslute so that subsequent segment files
+ * have the blkno calculated based on all segment files and not relative to
+ * the current segment file.
+ * see: https://git.postgresql.org/gitweb/?p=pg_filedump.git;a=commitdiff;h=052ed0112967dd1e9b0e2cbe54821c04475f1a3a;hp=b163cdaa53b651958cc8
+ */
+
+ /* Segment size in bytes, BLCKSZ is 8192 by default, 8KB pages
+ * 1GB segment files are 131072 blocks of 8KB page size
+ * NOTE: pd_pagesize_version is BLCKSZ + version, since 8.3+, version is 4,
+ * resulting in pd_pagesize_version being 8196 when pagesize is 8KB
+ */
+ static unsigned int segmentSize = RELSEG_SIZE * BLCKSZ;
+
+ /* Number of current segment */
+ static unsigned int segmentNumber = 0;
+
+ if (parallel)
+ {
+ char full_filepath[MAX_DIR_LENGTH];
+ sprintf(full_filepath, "%s/%s", dirpath, filename);
+ segmentNumber = get_segment_number(full_filepath);
+ } else
+ {
+ segmentNumber = get_segment_number(filename);
+ }
+
+ /* segmentBlockOffset is the absolute blockNumber of the block when taking
+ * into account any previous segment files.
+ */
+ uint32 segmentBlockOffset = RELSEG_SIZE * segmentNumber;
+
+ uint16 checksum = pg_checksum_page((char *)page, segmentBlockOffset + blkno);
+
+ bool corrupted = false;
+
+ if (verbose)
+ {
+ printf("DEBUG: filename: %s/%s[%d]\n \
+ \tsegmentBlockOffset: %d, maxSegmentSize: %d,\n \
+ \tsegmentNumber: %d, relative blkno: %d, absolute blkno: %d,\n \
+ \tchecksum: %x, phdr->pd_checksum: %x,\n \
+ \tphdr->pd_flags: %d, phdr->pd_lower: %d, phdr->pd_upper: %d,\n \
+ \tphdr->pd_special: %d, phdr->pd_pagesize_version: %d,\n \
+ \tphdr->pd_prune_xid: %d\n",
+ dirpath, filename, blkno,
+ segmentBlockOffset, segmentSize,
+ segmentNumber, blkno, segmentBlockOffset + blkno,
+ checksum, phdr->pd_checksum,
+ phdr->pd_flags, phdr->pd_lower, phdr->pd_upper,
+ phdr->pd_special, phdr->pd_pagesize_version,
+ phdr->pd_prune_xid );
+ }
+
+ if (phdr->pd_checksum != 0 && phdr->pd_checksum != checksum)
+ {
+ corrupted = true;
+ if (verbose)
+ printf("ERROR: corruption found in %s/%s[%d], expected %x, found %x\n",
+ dirpath, filename, blkno, checksum, phdr->pd_checksum);
+ }
+
+ if (verbose)
+ printf("DEBUG: is_page_corrupted for %s/%s[%d] returns: %d\n",
+ dirpath, filename, blkno, corrupted);
+
+ return corrupted;
+}
+
+static uint32
+scan_segmentfile(const char *filename, const char *dirpath)
+{
+
+ /* Performance considerations:
+ * segment files can be up to 1GB in size before they are split
+ * https://www.postgresql.org/docs/9.6/static/storage-file-layout.html
+ */
+
+ /* Always skip checking pg_internal.init because always shows as
+ * corrupted. If this file ever becomes corrupted, OK to remove
+ * it as it is recreated upon server startup. Return false since
+ * zero corrupt pages are checked here.
+ */
+ if (strstr(filename, "pg_internal.init") != NULL)
+ return 0;
+
+ if (verbose)
+ printf("DEBUG: scanning segment filename: %s/%s\n",
+ dirpath, filename);
+
+ int fd;
+ char page[BLCKSZ];
+ BlockNumber blkno = 0;
+ BlockNumber corrupted = 0;
+
+ if (parallel)
+ {
+ char full_filepath[MAX_DIR_LENGTH];
+ sprintf(full_filepath, "%s/%s", dirpath, filename);
+ fd = open(full_filepath, O_RDONLY);
+ } else
+ {
+ fd = open(filename, O_RDONLY);
+ }
+
+ if (fd < 0)
+ {
+ fprintf(stderr, "ERROR: %s: %s cannot be opened\n", strerror(errno),
+ filename);
+ /* return 1 so that other segment files can be scanned, but that this
+ * segment file is marked as corrupted/some unknown error
+ */
+ return 1;
+ }
+
+ while (read(fd, page, BLCKSZ) == BLCKSZ)
+ {
+ if (is_page_corrupted(page, blkno, filename, dirpath))
+ {
+ corrupted++;
+ }
+ blkno++;
+ }
+ close(fd);
+
+ return corrupted;
+}
+
+// Counts the number of entries in the given directory. We use this to count
+// the number of elements in /pgsql/data/base, which contains only directories.
+static void count_entries(const char *dirpath) {
+ DIR *d;
+ struct dirent *dir;
+
+ d = opendir(dirpath);
+
+ if (d)
+ {
+ while ((dir = readdir(d)) != NULL)
+ {
+ if (strcmp(".", dir->d_name) == 0 ||
+ strcmp("..", dir->d_name) == 0)
+ continue;
+ num_directories++;
+ }
+ }
+ closedir(d);
+}
+
+// Builds a list of data directories to scan.
+static char** get_data_directories(const char *dirpath) {
+ DIR *d;
+ struct dirent *dir;
+ struct stat statbuf;
+ char **dirs = malloc(sizeof(char *) * num_directories);
+ int index = 0;
+
+ d = opendir(dirpath);
+
+ if (d)
+ {
+ while ((dir = readdir(d)) != NULL)
+ {
+ if (strcmp(".", dir->d_name) == 0 ||
+ strcmp("..", dir->d_name) == 0)
+ continue;
+
+ char full_dirpath[MAX_DIR_LENGTH];
+ sprintf(full_dirpath, "%s/%s", dirpath, dir->d_name);
+
+ // Try using d_type to verify it's a directory. If we can't,
+ // fall back to using lstat.
+ if (dir->d_type == DT_DIR)
+ {
+ dirs[index] = strdup(full_dirpath);
+ index++;
+ } else
+ {
+ int lstat_res = lstat(full_dirpath, &statbuf);
+ if (lstat_res < 0)
+ {
+ // TODO: Currently, when the file structure
+ // isn't what we expect, we log an error and keep going.
+ // We do not exit(1) from the script because then it will be
+ // reported as a checksum failure, and we will not be able
+ // to differentiate between failures due to the file
+ // structure not being what this script expects, and
+ // actual checksum failures. In theory, this should never
+ // happen, but just in case in the future Postgres changes
+ // the way they structure their data files, we should have
+ // a way to differentiate between the two cases, and exit
+ // here.
+ fprintf(stderr, "ERROR: lstat(%s) returned error: %s\n",
+ full_dirpath, strerror(errno));
+ }
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ dirs[index] = strdup(full_dirpath);
+ index++;
+ } else
+ {
+ // TODO: Same as the above comment; we should
+ // figure out a way to identify unexpected file structure.
+ fprintf(stderr, "ERROR: unexpected file strucuture; "
+ "expected %s to be a directory, but "
+ "statbuf.st_mode was %d. Only files are expected "
+ "under data directories.\n",
+ full_dirpath, statbuf.st_mode);
+ }
+ }
+ }
+ }
+ closedir(d);
+ return dirs;
+}
+
+// Scans a directory within /pgsql/data/base (Ex. /pgsql/data/base/12345).
+// The elements inside this directory should all be data files.
+// This method should be called via a new thread so that we can scan directories
+// in parallel.
+int scan_data_files(const char *dirpath) {
+ DIR *d;
+ struct dirent *dirent;
+ struct stat statbuf;
+ int corrupt_pages_found = 0;
+
+ d = opendir(dirpath);
+
+ if (d)
+ {
+ while ((dirent = readdir(d)) != NULL)
+ {
+ if (strstr(dirent->d_name, "pg_internal.init") != NULL ||
+ strcmp(".", dirent->d_name) == 0 ||
+ strcmp("..", dirent->d_name) == 0)
+ continue;
+
+ // Try using d_type to verify it's a file. If we can't, fall back
+ // to using lstat.
+ if (dirent->d_type == DT_REG)
+ {
+ corrupt_pages_found += scan_segmentfile(dirent->d_name,
+ dirpath);
+ } else
+ {
+ char full_dirpath[MAX_DIR_LENGTH];
+ sprintf(full_dirpath, "%s/%s", dirpath, dirent->d_name);
+
+ int lstat_res = lstat(full_dirpath, &statbuf);
+ if (lstat_res < 0)
+ {
+ // TODO: Currently, when the file structure
+ // isn't what we expect, we log an error and keep going.
+ // We do not exit(1) from the script because then it will be
+ // reported as a checksum failure, and we will not be able
+ // to differentiate between failures due to the file
+ // structure not being what this script expects, and
+ // actual checksum failures. In theory, this should never
+ // happen, but just in case in the future Postgres changes
+ // the way they structure their data files, we should have
+ // a way to differentiate between the two cases, and exit
+ // here.
+ fprintf(stderr, "ERROR: lstat(%s) returned error: %s\n",
+ full_dirpath, strerror(errno));
+ }
+
+ if (S_ISREG(statbuf.st_mode))
+ {
+ corrupt_pages_found +=
+ scan_segmentfile(dirent->d_name, dirpath);
+ } else
+ {
+ // TODO: Same as the above comment; we should
+ // figure out a way to identify unexpected file structure.
+ fprintf(stderr, "ERROR: unexpected file strucuture; "
+ "expected %s to be a file, but statbuf.st_mode was "
+ "%d. Only files are expected under data "
+ "directories.\n",
+ full_dirpath, statbuf.st_mode);
+ }
+ }
+ }
+ }
+
+ closedir(d);
+ return corrupt_pages_found;
+}
+
+// Gets the current queue index to process the data directory at that index,
+// and then increments the index.
+static int get_queue_index(int *index) {
+ pthread_mutex_lock(&mtx);
+ *index = queue_index;
+ queue_index++;
+ pthread_mutex_unlock(&mtx);
+ return *index;
+}
+
+// Function used by each thread. It will continously look for the next
+// data directory to scan from the queue, scan it, then repeat until
+// the queue has been processed.
+static uint32 process_directories_worker(void *dirs) {
+ const char **data_directories = (const char **)dirs;
+ uint32 corrupt_pages_found = 0;
+
+ int index = 0;
+ while (get_queue_index(&index) < num_directories) {
+ corrupt_pages_found += scan_data_files(data_directories[index]);
+ }
+
+ return corrupt_pages_found;
+}
+
+// Builds the queue of data directories, then spins up threads to
+// process the queue.
+static uint32 scan_base_directory(const char *dirpath) {
+ uint32 corrupt_pages_found = 0;
+
+ count_entries(dirpath);
+ char **dirs = get_data_directories(dirpath);
+
+ // Start up the workers.
+ pthread_t threads[MAX_THREAD_COUNT];
+ for (int i = 0; i < MAX_THREAD_COUNT; i++) {
+ pthread_create(threads + i, NULL, (void *)&process_directories_worker,
+ (void *)dirs);
+ }
+
+ // Wait for all threads to finish.
+ for (int i = 0; i < MAX_THREAD_COUNT; i++) {
+ int corrupt_pages;
+ pthread_join(threads[i], (void **) &corrupt_pages);
+ corrupt_pages_found += corrupt_pages;
+ }
+
+ // Free everything.
+ for (int i = 0; i < num_directories; i++) {
+ free(dirs[i]);
+ }
+ free(dirs);
+
+ return corrupt_pages_found;
+}
+
+static uint32
+scan_directory(const char *dirpath)
+{
+ /* scope is only to check the base directory, where the actual data files
+ * are located. Other directories contain temporary files used for
+ * transactions or queries in progress and should not be checked.
+ *
+ * Postgres stores data files in one directory per database defined,
+ * without additional nesting or leafs. This causes depth of database
+ * directories to always be one.
+ */
+
+ DIR *d;
+ struct dirent *dir;
+ struct stat statbuf;
+ uint32 corrupt_pages_found = 0;
+
+ d = opendir(dirpath);
+
+ if (verbose)
+ printf("DEBUG: called scan_directory(%s)\n", dirpath);
+
+ if (d)
+ {
+ chdir(dirpath);
+ while ((dir = readdir(d)) != NULL)
+ {
+ lstat(dir->d_name, &statbuf);
+
+ if (verbose)
+ printf("DEBUG: direntry: %s/%s - statbuf.st_mode: %d\n",
+ dirpath, dir->d_name, statbuf.st_mode);
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ if(strcmp(".", dir->d_name) == 0 ||
+ strcmp("..", dir->d_name) == 0)
+ continue;
+
+ char new_dirpath[MAX_DIR_LENGTH];
+ sprintf(new_dirpath, "%s/%s", dirpath, dir->d_name);
+
+ corrupt_pages_found += scan_directory(new_dirpath);
+ }
+ else if (S_ISREG(statbuf.st_mode))
+ {
+ corrupt_pages_found += scan_segmentfile(dir->d_name, dirpath);
+ }
+ }
+ closedir(d);
+ }
+
+ return corrupt_pages_found;
+}
+
+static void
+print_help(const char *argv_value)
+{
+ printf("Usage: %s [OPTIONS]\n", argv_value);
+ printf(" -v verbose\n");
+ printf(" -D directory data directory\n");
+ printf(" -h, --help print this help and exit\n");
+ printf(" -p scans data directories in parallel\n");
+ printf("\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ /*
+ * Flow is to use a data directory and traverse to find segments, each
+ * segment file is then scanned for corrupt pages.
+ */
+
+ int c;
+ uint32 corrupted_pages_found = 0;
+ const char *short_opt = "chD:vp";
+ char datadir[MAX_DIR_LENGTH];
+ struct stat statbuf;
+ struct option long_opt[] =
+ {
+ {"dumpcorrupted", no_argument, NULL, 'c'},
+ {"datadir", required_argument, NULL, 'D'},
+ {"help", no_argument, NULL, 'h'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"parallel", no_argument, NULL, 'p'},
+ {NULL, 0, NULL, 0 }
+ };
+
+ /* if no arguments passed, print help and exit(1) */
+ if (argc == 1)
+ {
+ print_help(argv[0]);
+ exit(1);
+ }
+
+ while((c = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1)
+ {
+ switch(c)
+ {
+ case -1: /* no more arguments */
+ case 0: /* long options toggles */
+ break;
+
+ case 'c':
+ dump_corrupted = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'p':
+ parallel = true;
+ break;
+
+ case 'D':
+ if (optarg)
+ {
+ /* '-D' argument of data directory must contain 'base'
+ * directory to scan, check if trailing back slash and
+ * append appropriately. Must use base since other
+ * directories, such as pg_xlog, contain currently
+ * unsupported file types.
+ */
+ strcpy(datadir, optarg);
+ if (strcmp(&optarg[strlen(optarg) - 1], "/") == 0)
+ {
+ strcat(datadir, "base");
+ }
+ else
+ {
+ strcat(datadir, "/base");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: -D argument could not be parsed\n");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ print_help(argv[0]);
+ exit(1);
+
+ default:
+ fprintf(stderr, "ERROR: %s: invalid option -- %c\n", argv[0], c);
+ print_help(argv[0]);
+ exit(1);
+ };
+ };
+
+ lstat(datadir, &statbuf);
+ if (!S_ISDIR(statbuf.st_mode))
+ {
+ fprintf(stderr, "ERROR: base %s is not a directory\n", datadir);
+ exit(1);
+ }
+
+ if (parallel)
+ {
+ corrupted_pages_found = scan_base_directory(datadir);
+ } else {
+ corrupted_pages_found = scan_directory(datadir);
+ }
+
+ if (corrupted_pages_found > 0)
+ {
+ printf("CORRUPTION FOUND: %d\n", corrupted_pages_found);
+ exit(1);
+ }
+ else
+ {
+ printf("NO CORRUPTION FOUND\n");
+ exit(0);
+ }
+}